呉のLaravel現行コード

This commit is contained in:
go.unhi 2025-08-15 00:31:48 +09:00
commit 8b7eab3c94
1120 changed files with 562725 additions and 0 deletions

18
.editorconfig Normal file
View File

@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4

65
.env.example Normal file
View File

@ -0,0 +1,65 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database
PHP_CLI_SERVER_WORKERS=4
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
# CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

11
.gitattributes vendored Normal file
View File

@ -0,0 +1,11 @@
* text=auto eol=lf
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
/.phpunit.cache
/node_modules
/public/build
/public/hot
/public/storage
/storage/*.key
/storage/pail
/vendor
.env
.env.backup
.env.production
.phpactor.json
.phpunit.result.cache
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
/auth.json
/.fleet
/.idea
/.nova
/.vscode
/.zed

73
README.md Normal file
View File

@ -0,0 +1,73 @@
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
<p align="center">
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
</p>
## プロジェクト概要So-Manager / Laravel12
本プロジェクトは Laravel5 旧コードからの移行を進めつつ、新規実装は Laravel12 の規約に沿って整理しています。旧Blade互換のため `Legacy` レイヤを用意していますが、新規実装で `Legacy` を直接参照することは禁止です(互換専用)。
### ディレクトリと責務(要点)
- `app/Models`: 正式なEloquentモデル1テーブル1モデル。例: `Park`, `PriceA`, `Ptype`, `RegularContract`
- `app/Legacy`: 旧モデル互換Blade互換専用。例: `Legacy\User`, `Legacy\OperatorQue`
- `app/Services`: 画面用ユースケースや共通処理。例: `UsingStatusService`, `UserService`, `CsvService`
- `app/Support`: 純技術ユーティリティ(ビジネス非依存)。例: `Csv`, `Files`
- `app/Enums`: 共通定数は Enum で表現(段階的移行)。
- `app/Providers/LegacyServiceProvider`: 旧FQCN互換class_aliasを提供。
### 実装ルール(重要)
- 新規コードは正式モデル+サービス経由で実装し、`Legacy` は参照しない
- Controller は薄く、複雑な処理は `app/Services` に集約する
- 旧Bladeは当面 `Legacy` 互換で動作可能(段階的に置換)
詳細なチームルールは `docs/TEAM_GUIDE_JA.md` を参照してください。
## 開発補助
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
Composer が未インストールの環境では、`php composer.phar dump-autoload` を利用してオートロードを更新してください。
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
## Laravel Sponsors
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
### Premium Partners
- **[Vehikl](https://vehikl.com/)**
- **[Tighten Co.](https://tighten.co)**
- **[WebReinvent](https://webreinvent.com/)**
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
- **[64 Robots](https://64robots.com)**
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
- **[Cyber-Duck](https://cyber-duck.co.uk)**
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
- **[Jump24](https://jump24.co.uk)**
- **[Redberry](https://redberry.international/laravel/)**
- **[Active Logic](https://activelogic.com)**
- **[byte5](https://byte5.de)**
- **[OP.GG](https://op.gg)**
## Contributing
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Code of Conduct
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
## Security Vulnerabilities
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
## License
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).

175
SHJ_BATCH_LOG_README.md Normal file
View File

@ -0,0 +1,175 @@
# SHJ-8 共通バッチ処理ログシステム
## 概要
SHJ-8は**全てのSHJ系バッチ処理**の実行ログを**batch_logテーブル**に登録する**共通機能**です。
仕様書に基づき、batch_logテーブルの各フィールドを活用してSHJ-8の要件を満たします
### フィールドマッピング
- **デバイスID**`parameters`JSON形式
- **プロセス名**`process_name`
- **ジョブ名**`message`の一部
- **ステータス**`status`
- **ステータスコメント**`message`、`error_details`
- **登録日時**`start_time`、`created_at`
- **更新日時**`end_time`、`updated_at`
## 処理フロー
### 【処理1】入力パラメーターをチェックする
- デバイスID: device表での存在確認
- プロセス名/ジョブ名: いずれか必須の確認
- ステータス: 必須チェック
- 登録日時/更新日時: yyyy/mm/dd形式の検証
### 【処理2】バッチ処理ログを登録する
- batch_logテーブルへのINSERT処理
- job_log_idはAUTO_INCREMENTで自動生成
- status_commentは処理成功時に「処理成功」を設定
- 仕様書指定の日付でcreated_at/updated_atを設定
### 【処理3】処理結果を返却する
- 処理結果: 0=正常終了、1=異常終了
- 異常情報の詳細メッセージ
## 使用方法
### コマンド実行
```bash
php artisan shj:batch-log {デバイスID} {プロセス名} {ジョブ名} {ステータス} {登録日時} {更新日時}
```
### 実行例
```bash
# SHJ-4C室割当処理のログ記録
php artisan shj:batch-log 9999 "SHJ-4C" "室割当処理" "success" "2025/01/15" "2025/01/15"
# SHJメール送信処理のログ記録
php artisan shj:batch-log 9999 "SHJ-MAIL-SEND" "メール送信" "running" "2025/01/15" "2025/01/15"
# その他のSHJ系バッチのログ記録
php artisan shj:batch-log 9999 "SHJ-5" "駐車場空き確認" "completed" "2025/01/15" "2025/01/15"
```
## テストデータ
`database/seeders/ShjBatchLogTestSeeder.php`にテスト用のデバイスデータが含まれています。
### テストデータ挿入
```bash
php artisan db:seed --class=ShjBatchLogTestSeeder
```
### テストデバイス
- device_id: 9999 (テスト用プリンター)
## データベーステーブル
### batch_log統一テーブル
```sql
CREATE TABLE `batch_log` (
`id` bigint(20) UNSIGNED NOT NULL,
`process_name` varchar(50) NOT NULL COMMENT 'プロセス名',
`status` varchar(20) NOT NULL COMMENT 'ステータス',
`start_time` datetime NOT NULL COMMENT '開始時刻',
`end_time` datetime DEFAULT NULL COMMENT '終了時刻',
`parameters` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'パラメータJSON形式',
`message` text DEFAULT NULL COMMENT 'メッセージ',
`error_details` text DEFAULT NULL COMMENT 'エラー詳細',
`execution_count` int(11) NOT NULL DEFAULT 0 COMMENT '実行回数',
`success_count` int(11) NOT NULL DEFAULT 0 COMMENT '成功回数',
`error_count` int(11) NOT NULL DEFAULT 0 COMMENT 'エラー回数',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
)
```
### device
```sql
CREATE TABLE `device` (
`device_id` int(10) UNSIGNED NOT NULL,
`park_id` int(10) UNSIGNED DEFAULT NULL,
`device_type` varchar(255) DEFAULT NULL,
`device_subject` varchar(255) DEFAULT NULL,
-- その他のフィールド...
)
```
## ファイル構成
### モデル
- `app/Models/Device.php` - deviceテーブルのEloquentモデル
- `app/Models/Batch/BatchLog.php` - **batch_logテーブル対応モデル**
- SHJ-8仕様に基づく共通バッチログ機能
- `createShjJobLog()` メソッドでbatch_logに統一記録
- 全SHJ系バッチで共通利用
### コマンド
- `app/Console/Commands/ShjBatchLogCommand.php` - Artisanコマンド
### サービス
- `app/Services/ShjBatchLogService.php` - 核となるビジネスロジック
### 設定
- `app/Providers/AppServiceProvider.php` - サービスの依存関係注入設定
## ログ
処理の詳細なログは`storage/logs/laravel.log`に出力されます。
## エラーハンドリング
- デバイスID存在チェックエラー
- パラメータ形式エラー
- 日付形式エラー
- データベース挿入エラー
- 全てのエラーはbatch_logテーブルに記録
## 入出力仕様
### 入力パラメーター
| No | パラメーター | 桁数 | 型 | 多重度 | 備考 |
|----|-------------|------|-----|--------|------|
| 1 | デバイスID | 10 | 数字列 | 1 | |
| 2 | プロセス名 | 20 | 文字列 | 1 | |
| 3 | ジョブ名 | 20 | 文字列 | 1 | |
| 4 | ステータス | 10 | 文字列 | 1 | |
| 5 | 登録日時 | 10 | 日付(yyyy/mm/dd) | 1 | |
| 6 | 更新日時 | 10 | 日付(yyyy/mm/dd) | 1 | |
### 出力パラメーター
| No | パラメーター | 桁数 | 型 | 多重度 | 備考 |
|----|-------------|------|-----|--------|------|
| 1 | 処理結果 | 1 | 数値 | 1 | 0:正常終了/1:異常終了 |
| 2 | 異常情報 | ## | 文字列 | 1 | |
## 出力結果(仕様書準拠)
### 正常終了時
```json
{
"処理結果": 0,
"異常情報": "",
"batch_log_id": 1
}
```
### エラー時
```json
{
"処理結果": 1,
"異常情報": "エラー: デバイスID 9999 が見つかりません",
"batch_log_id": 1
}
```
### 処理結果の値
- **0**: 正常終了
- **1**: 異常終了
### 異常情報
- 正常時: 空文字
- エラー時: エラーの詳細内容
## 注意事項
1. デバイスIDは事前にdeviceテーブルに存在している必要があります
2. プロセス名とジョブ名はいずれか必須ですが、どちらも設定可能です
3. 日付形式はyyyy/mm/dd形式で入力してください
4. batch_logテーブルを使用してSHJ-8の機能を実現しています
5. すべてのSHJ系バッチで共通利用できる統一ログシステムです

56
about_path.md Normal file
View File

@ -0,0 +1,56 @@
目录と説明(現状の骨格・日文注釈)
app/
Models/
BaseModel.php
共通ベースモデル(必要に応じて利用。既定のタイムスタンプ名など最小共通を集約)
Park.php
正式モデルpark テーブル)。新規コードはこちらを使用
PriceA.php
正式モデルprice_a テーブル)。料金・収容台数情報
Ptype.php
正式モデルptype テーブル)。車種分類
RegularContract.php
正式モデルregular_contract テーブル)。定期契約
Device.php
デバイス情報park リレーションは正式モデル Park を参照)
MailTemplate.php / Ope.php / Batch/BatchLog.php / User.php
既存機能のモデル(必要最小のみ記載)
Concerns/
HasSortable.php
共通ソート用 Traitsort/sort_type を安全適用)
Legacy/
User.php
旧 user テーブル互換モデル。旧 Blade 等の互換のため残置
OperatorQue.php
旧 operator_que テーブル互換。定数/関連取得getUser/getParkを維持
Park.php
旧 park テーブル互換(旧表示向け)。新規コードは Models/Park を使用
Services/
UsingStatusService.php
利用率統計の共通入口(旧 Helper 機能を集約)
UserService.php
利用者検索・取得の共通入口(旧 User::search の最小互換)
OperatorQueService.php
オペレータキュー一覧の共通入口
CsvService.php / FileService.php
CSV 入出力ファイルアップロードの共通サービスUtils ラッパ)
ShjFourCService.php / ShjMailSendService.php
既存バッチ/メール機能のサービス(モデル参照は通用モデルに統一済)
Support/
Csv.php / Files.php
純粋な技術ユーティリティ(ビジネス非依存の小粒機能)
Enums/
QueueClass.php / QueueStatus.php
旧配列定数の移行先(段階的に Enum 化を推奨)
Providers/
AppServiceProvider.php
サービスの DI 初期化ShjFourC/ShjMailSend の依存を正式モデルに更新済)
LegacyServiceProvider.php
旧 FQCN 互換class_alias で \App\* → \App\Legacy\* を提供)
Utils.php
旧ユーティリティ。CSV/ファイルは Service 経由に収束予定(当面互換のため存置)
運用ルール(要点)
新規実装は必ず通用モデルApp\Models\Park/PriceA/Ptype/RegularContractとサービス経由で実装
旧 Blade は Legacy モデルの互換でそのまま動作(新規で Legacy を直接参照しない)
“1テーブル1正式モデル” を維持。Legacy 側は互換専用と明記
統計/CSV/外部 I/F は Service に集約。Controller は薄く保つ

View File

@ -0,0 +1,238 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use App\Models\Batch\BatchLog;
use App\Models\Device;
/**
* SHJ-8 バッチ処理ログ登録コマンド
*
* 統一BatchLogを使用してバッチ処理の実行ログをbatch_logテーブルに登録する
* 仕様書に基づくSHJ-8の要求パラメータを受け取り、通用のログシステムで記録
*/
class ShjBatchLogCommand extends Command
{
/**
* コンソールコマンドの名前とシグネチャ
*
* 引数:
* - device_id: デバイスID (必須)
* - process_name: プロセス名 (必須)
* - job_name: ジョブ名 (必須)
* - status: ステータス (必須)
* - created_date: 登録日時 (必須、yyyy/mm/dd形式)
* - updated_date: 更新日時 (必須、yyyy/mm/dd形式)
*
* @var string
*/
protected $signature = 'shj:batch-log {device_id : デバイスID} {process_name : プロセス名} {job_name : ジョブ名} {status : ステータス} {created_date : 登録日時} {updated_date : 更新日時}';
/**
* コンソールコマンドの説明
*
* @var string
*/
protected $description = 'SHJ-8 バッチ処理ログ登録 - バッチ処理の実行ログを登録';
/**
* コンストラクタ
*/
public function __construct()
{
parent::__construct();
}
/**
* コンソールコマンドを実行
*
* 処理フロー:
* 1. 入力パラメーターをチェックする
* 2. 統一BatchLogを使用してbatch_logテーブルに記録
* 3. 仕様書準拠の処理結果を返却する
*
* @return int
*/
public function handle()
{
try {
// 開始ログ出力
$startTime = now();
$this->info('SHJ-8 バッチ処理ログ登録を開始します。');
// 引数取得
$deviceId = (int) $this->argument('device_id');
$processName = $this->argument('process_name');
$jobName = $this->argument('job_name');
$status = $this->argument('status');
$createdDate = $this->argument('created_date');
$updatedDate = $this->argument('updated_date');
Log::info('SHJ-8 バッチ処理ログ登録開始', [
'start_time' => $startTime,
'device_id' => $deviceId,
'process_name' => $processName,
'job_name' => $jobName,
'status' => $status,
'created_date' => $createdDate,
'updated_date' => $updatedDate
]);
// 【処理1】入力パラメーターをチェックする
$paramCheckResult = $this->validateParameters($deviceId, $processName, $jobName, $status, $createdDate, $updatedDate);
if (!$paramCheckResult['valid']) {
$this->error('パラメータエラー: ' . $paramCheckResult['message']);
// 仕様書【判断1】パラメーターNG時の結果出力
$this->line('処理結果: 1'); // 1 = 異常終了
$this->line('異常情報: ' . $paramCheckResult['message']);
return self::FAILURE;
}
// 【処理2】統一BatchLogを使用してログ登録
$batchLog = BatchLog::createBatchLog(
$processName, // 実際のプロセス名を使用
$status,
[
'device_id' => $deviceId,
'job_name' => $jobName,
'status_comment' => BatchLog::getSuccessComment(),
'input_created_date' => $createdDate,
'input_updated_date' => $updatedDate,
'shj8_params' => [
'device_id' => $deviceId,
'process_name' => $processName,
'job_name' => $jobName,
'status' => $status,
'created_date' => $createdDate,
'updated_date' => $updatedDate
]
],
$jobName . '' . BatchLog::getSuccessComment()
);
$endTime = now();
$this->info('SHJ-8 バッチ処理ログ登録が正常に完了しました。');
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}");
Log::info('SHJ-8 バッチ処理ログ登録完了', [
'end_time' => $endTime,
'duration_seconds' => $startTime->diffInSeconds($endTime),
'batch_log_id' => $batchLog->id
]);
// 仕様書【処理3】正常終了時の結果出力
$this->line('処理結果: 0'); // 0 = 正常終了
$this->line('異常情報: '); // 正常時は空文字
return self::SUCCESS;
} catch (\Exception $e) {
$this->error('SHJ-8 バッチ処理ログ登録で予期しないエラーが発生しました: ' . $e->getMessage());
Log::error('SHJ-8 バッチ処理ログ登録例外エラー', [
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
// 仕様書【処理3】異常終了時の結果出力
$this->line('処理結果: 1'); // 1 = 異常終了
$this->line('異常情報: エラー: ' . $e->getMessage());
return self::FAILURE;
}
}
/**
* 【処理1】パラメータの妥当性を検証
*
* 仕様書に基づく検証内容:
* - デバイスID: 必須、数値、device表に存在するか
* - プロセス名: 「プロセス名」「ジョブ名」いずれか必須
* - ジョブ名: 「プロセス名」「ジョブ名」いずれか必須
* - ステータス: 必須
* - 登録日時: 必須、yyyy/mm/dd形式
* - 更新日時: 必須、yyyy/mm/dd形式
*
* @param int $deviceId デバイスID
* @param string $processName プロセス名
* @param string $jobName ジョブ名
* @param string $status ステータス
* @param string $createdDate 登録日時
* @param string $updatedDate 更新日時
* @return array 検証結果 ['valid' => bool, 'message' => string]
*/
private function validateParameters(int $deviceId, string $processName, string $jobName, string $status, string $createdDate, string $updatedDate): array
{
// デバイスID存在チェック
if ($deviceId <= 0) {
return [
'valid' => false,
'message' => 'パラメーターNG: デバイスIDは正の整数である必要があります'
];
}
if (!Device::exists($deviceId)) {
return [
'valid' => false,
'message' => "パラメーターNG: デバイスID {$deviceId} が存在しません"
];
}
// プロセス名とジョブ名のいずれか必須チェック
if (empty($processName) && empty($jobName)) {
return [
'valid' => false,
'message' => 'パラメーターNG: プロセス名またはジョブ名のいずれかは必須です'
];
}
// ステータス必須チェック
if (empty($status)) {
return [
'valid' => false,
'message' => 'パラメーターNG: ステータスは必須です'
];
}
// 日付形式チェック
if (!$this->isValidDateFormat($createdDate)) {
return [
'valid' => false,
'message' => 'パラメーターNG: 登録日時の形式が正しくありませんyyyy/mm/dd'
];
}
if (!$this->isValidDateFormat($updatedDate)) {
return [
'valid' => false,
'message' => 'パラメーターNG: 更新日時の形式が正しくありませんyyyy/mm/dd'
];
}
return [
'valid' => true,
'message' => 'パラメーターチェックOK'
];
}
/**
* 日付形式の検証
*
* @param string $date 日付文字列
* @return bool 有効な日付形式かどうか
*/
private function isValidDateFormat(string $date): bool
{
// yyyy/mm/dd形式の正規表現チェック
if (!preg_match('/^\d{4}\/\d{2}\/\d{2}$/', $date)) {
return false;
}
// 実際の日付として有効かチェック
$dateParts = explode('/', $date);
return checkdate((int)$dateParts[1], (int)$dateParts[2], (int)$dateParts[0]);
}
}

View File

@ -0,0 +1,157 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use App\Services\ShjFourCService;
/**
* SHJ-4C 室割当処理コマンド
*
* 駐輪場の区画別利用率状況に基づく室割当処理を実行する
* バックグラウンドで実行される定期バッチ処理
*/
class ShjFourCCommand extends Command
{
/**
* コンソールコマンドの名前とシグネチャ
*
* 引数:
* - park_id: 駐輪場ID (必須)
* - ptype_id: 駐輪分類ID (必須)
* - psection_id: 車種区分ID (必須)
*
* @var string
*/
protected $signature = 'shj:4c {park_id : 駐輪場ID} {ptype_id : 駐輪分類ID} {psection_id : 車種区分ID}';
/**
* コンソールコマンドの説明
*
* @var string
*/
protected $description = 'SHJ-4C 室割当処理 - ゾーン情報取得及び割当処理を実行';
/**
* SHJ-4Cサービスクラス
*
* @var ShjFourCService
*/
protected $shjFourCService;
/**
* コンストラクタ
*
* @param ShjFourCService $shjFourCService
*/
public function __construct(ShjFourCService $shjFourCService)
{
parent::__construct();
$this->shjFourCService = $shjFourCService;
}
/**
* コンソールコマンドを実行
*
* 処理フロー:
* 1. パラメータ取得と検証
* 2. ゾーン情報取得処理
* 3. 割当判定処理
* 4. バッチログ作成
* 5. 処理結果返却
*
* @return int
*/
public function handle()
{
try {
// 開始ログ出力
$startTime = now();
$this->info('SHJ-4C 室割当処理を開始します。');
Log::info('SHJ-4C 室割当処理開始', [
'start_time' => $startTime,
'park_id' => $this->argument('park_id'),
'ptype_id' => $this->argument('ptype_id'),
'psection_id' => $this->argument('psection_id')
]);
// 引数取得
$parkId = $this->argument('park_id');
$ptypeId = $this->argument('ptype_id');
$psectionId = $this->argument('psection_id');
// パラメータ検証
if (!$this->validateParameters($parkId, $ptypeId, $psectionId)) {
$this->error('パラメータが不正です。');
return self::FAILURE;
}
// SHJ-4C処理実行
$result = $this->shjFourCService->executeRoomAllocation($parkId, $ptypeId, $psectionId);
// 処理結果確認
if ($result['success']) {
$endTime = now();
$this->info('SHJ-4C 室割当処理が正常に完了しました。');
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}");
Log::info('SHJ-4C 室割当処理完了', [
'end_time' => $endTime,
'duration_seconds' => $startTime->diffInSeconds($endTime),
'result' => $result
]);
return self::SUCCESS;
} else {
$this->error('SHJ-4C 室割当処理でエラーが発生しました: ' . $result['message']);
Log::error('SHJ-4C 室割当処理エラー', [
'error' => $result['message'],
'details' => $result['details'] ?? null
]);
return self::FAILURE;
}
} catch (\Exception $e) {
$this->error('SHJ-4C 室割当処理で予期しないエラーが発生しました: ' . $e->getMessage());
Log::error('SHJ-4C 室割当処理例外エラー', [
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return self::FAILURE;
}
}
/**
* パラメータの妥当性を検証
*
* @param mixed $parkId 駐輪場ID
* @param mixed $ptypeId 駐輪分類ID
* @param mixed $psectionId 車種区分ID
* @return bool 検証結果
*/
private function validateParameters($parkId, $ptypeId, $psectionId): bool
{
// 必須パラメータチェック
if (empty($parkId) || empty($ptypeId) || empty($psectionId)) {
$this->error('全てのパラメータは必須です。');
return false;
}
// 数値形式チェック
if (!is_numeric($parkId) || !is_numeric($ptypeId) || !is_numeric($psectionId)) {
$this->error('全てのパラメータは数値である必要があります。');
return false;
}
// 正の整数チェック
if ($parkId <= 0 || $ptypeId <= 0 || $psectionId <= 0) {
$this->error('全てのパラメータは正の整数である必要があります。');
return false;
}
return true;
}
}

View File

@ -0,0 +1,177 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use App\Services\ShjMailSendService;
/**
* SHJ メール送信処理コマンド
*
* メールテンプレートを使用したメール送信処理を実行する
* バックグラウンドで実行される定期バッチ処理
*/
class ShjMailSendCommand extends Command
{
/**
* コンソールコマンドの名前とシグネチャ
*
* 引数:
* - mail_address: メールアドレス (必須)
* - backup_mail_address: 予備メールアドレス (必須)
* - mail_template_id: メールテンプレートID (必須)
*
* @var string
*/
protected $signature = 'shj:mail-send {mail_address : メールアドレス} {backup_mail_address : 予備メールアドレス} {mail_template_id : メールテンプレートID}';
/**
* コンソールコマンドの説明
*
* @var string
*/
protected $description = 'SHJ メール送信処理 - テンプレートに基づくメール送信を実行';
/**
* SHJメール送信サービスクラス
*
* @var ShjMailSendService
*/
protected $shjMailSendService;
/**
* コンストラクタ
*
* @param ShjMailSendService $shjMailSendService
*/
public function __construct(ShjMailSendService $shjMailSendService)
{
parent::__construct();
$this->shjMailSendService = $shjMailSendService;
}
/**
* コンソールコマンドを実行
*
* 処理フロー:
* 1. 入力パラメーターをチェックする
* 2. メール送信テンプレート情報を取得する
* 3. メールを送信する
* 4. 処理結果を返却する
*
* @return int
*/
public function handle()
{
try {
// 開始ログ出力
$startTime = now();
$this->info('SHJ メール送信処理を開始します。');
Log::info('SHJ メール送信処理開始', [
'start_time' => $startTime,
'mail_address' => $this->argument('mail_address'),
'backup_mail_address' => $this->argument('backup_mail_address'),
'mail_template_id' => $this->argument('mail_template_id')
]);
// 引数取得
$mailAddress = $this->argument('mail_address');
$backupMailAddress = $this->argument('backup_mail_address');
$mailTemplateId = $this->argument('mail_template_id');
// 【処理1】パラメータ検証
if (!$this->validateParameters($mailAddress, $backupMailAddress, $mailTemplateId)) {
$this->error('パラメータが不正です。');
return self::FAILURE;
}
// SHJメール送信処理実行
$result = $this->shjMailSendService->executeMailSend($mailAddress, $backupMailAddress, $mailTemplateId);
// 処理結果確認
if ($result['success']) {
$endTime = now();
$this->info('SHJ メール送信処理が正常に完了しました。');
$this->info("処理時間: {$startTime->diffInSeconds($endTime)}");
Log::info('SHJ メール送信処理完了', [
'end_time' => $endTime,
'duration_seconds' => $startTime->diffInSeconds($endTime),
'result' => $result
]);
return self::SUCCESS;
} else {
$this->error('SHJ メール送信処理でエラーが発生しました: ' . $result['message']);
Log::error('SHJ メール送信処理エラー', [
'error' => $result['message'],
'details' => $result['details'] ?? null
]);
return self::FAILURE;
}
} catch (\Exception $e) {
$this->error('SHJ メール送信処理で予期しないエラーが発生しました: ' . $e->getMessage());
Log::error('SHJ メール送信処理例外エラー', [
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return self::FAILURE;
}
}
/**
* 【処理1】パラメータの妥当性を検証
*
* 仕様書に基づく検証内容:
* - メールアドレス: 「メールアドレス」「予備メールアドレス」いずれか必須
* - メールテンプレートID: 必須
*
* @param mixed $mailAddress メールアドレス
* @param mixed $backupMailAddress 予備メールアドレス
* @param mixed $mailTemplateId メールテンプレートID
* @return bool 検証結果
*/
private function validateParameters($mailAddress, $backupMailAddress, $mailTemplateId): bool
{
// メールテンプレートIDチェック
if (empty($mailTemplateId)) {
$this->error('メールテンプレートIDは必須です。');
return false;
}
// 数値形式チェックメールテンプレートID
if (!is_numeric($mailTemplateId)) {
$this->error('メールテンプレートIDは数値である必要があります。');
return false;
}
// 正の整数チェックメールテンプレートID
if ($mailTemplateId <= 0) {
$this->error('メールテンプレートIDは正の整数である必要があります。');
return false;
}
// メールアドレスチェック(いずれか必須)
if (empty($mailAddress) && empty($backupMailAddress)) {
$this->error('メールアドレスまたは予備メールアドレスのいずれかは必須です。');
return false;
}
// メールアドレス形式チェック
if (!empty($mailAddress) && !filter_var($mailAddress, FILTER_VALIDATE_EMAIL)) {
$this->error('メールアドレスの形式が正しくありません。');
return false;
}
if (!empty($backupMailAddress) && !filter_var($backupMailAddress, FILTER_VALIDATE_EMAIL)) {
$this->error('予備メールアドレスの形式が正しくありません。');
return false;
}
return true;
}
}

26
app/Enums/QueueClass.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace App\Enums;
/**
* キュー種別PHP列挙型
* 備考:旧定数(\App\Legacy\OperatorQue::QueClassから段階的に移行
*/
enum QueueClass: string
{
case 社会人 = '本人確認(社会人)';
case 学生 = '本人確認(学生)';
case タグ発送 = 'タグ発送';
case 予約告知電話 = '予約告知電話';
case 定期更新電話 = '定期更新電話';
case 返金 = '返金';
case 再発行リミット超過 = '再発行リミット超過';
case 支払い催促 = '支払い催促';
case シール発行催促 = 'シール発行催促';
case サーバーエラー = 'サーバーエラー';
case プリンタエラー = 'プリンタエラー';
case スキャナーエラー = 'スキャナーエラー';
case プリンタ用紙残少警告 = 'プリンタ用紙残少警告';
}

17
app/Enums/QueueStatus.php Normal file
View File

@ -0,0 +1,17 @@
<?php
namespace App\Enums;
/**
* キューステータスPHP列挙型
* 備考:旧定数(\App\Legacy\OperatorQue::QueStatusから段階的に移行
*/
enum QueueStatus: string
{
case 発生 = 'キュー発生';
case 作業中 = 'キュー作業中';
case 作業済 = 'キュー作業済';
case 返金済 = '返金済';
}

View File

@ -0,0 +1,232 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Services\UsingStatusService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
/**
* 区画別利用率状況コントローラー
* Laravel 12対応旧Laravel 5.7からの移行版
*
* 機能:駐輪場の利用率統計表示とフィルタリング
* URL/using_status
*/
class UsingStatusController extends Controller
{
/**
* コントローラーのインスタンス作成
* Laravel 12変更点:ミドルウェアは routes/web.php で処理するように変更
*
* @return void
*/
public function __construct()
{
// Laravel 12: ミドルウェアは routes/web.php で処理
// Laravel 5.7: $this->middleware('auth'); を使用していた
}
/**
* 区画別利用率状況ページの表示
* Laravel 12変更点Request処理の最適化、エラーハンドリングの強化
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View|\Illuminate\Http\RedirectResponse
*/
public function index(Request $request, UsingStatusService $service)
{
try {
// CSRF トークンの自動検証Laravel 12標準機能
// リクエストパラメータの取得
// Laravel 12変更点$request->input()の使用を推奨
$parkId = $request->input('park_id', null);
$isSearchRequest = $request->has('search') || $request->isMethod('post');
// ログ出力(デバッグ用)
Log::info('区画別利用率状況ページアクセス', [
'park_id' => $parkId,
'is_search' => $isSearchRequest,
'method' => $request->method()
]);
// 駐輪場一覧の取得(選択用ドロップダウン)
$parkList = $service->getParkList();
// 利用率統計データの取得
// Laravel 12変更点デフォルトで全データを表示ユーザー選択不要
$utilizationStats = $service->getUtilizationStats($parkId);
// データが空の場合の処理
if ($utilizationStats->isEmpty() && $parkId) {
// 指定された駐輪場のデータが見つからない場合
return redirect()->route('using_status')
->with('warning', '選択された駐輪場のデータが見つかりませんでした。');
}
// 検索要求でない場合は全データを表示
if (!$isSearchRequest && !$parkId) {
$utilizationStats = $service->getUtilizationStats(null);
}
// 合計値の計算
$totals = $service->calculateTotals($utilizationStats);
// 選択された駐輪場の情報
$selectedPark = null;
if ($parkId && $parkList->isNotEmpty()) {
$selectedPark = $parkList->firstWhere('park_id', $parkId);
}
// ビューに渡すデータの準備
$viewData = [
'parkList' => $parkList, // 駐輪場選択用リスト
'utilizationStats' => $utilizationStats, // 利用率統計データ
'totals' => $totals, // 合計値
'selectedParkId' => $parkId, // 選択された駐輪場ID
'selectedPark' => $selectedPark, // 選択された駐輪場情報
'isSearchRequest' => $isSearchRequest, // 検索リクエストかどうか
'hasData' => $utilizationStats->isNotEmpty() // データが存在するかどうか
];
// 成功メッセージの設定(検索時)
if ($isSearchRequest && $utilizationStats->isNotEmpty()) {
session()->flash('success', '利用率データを正常に取得しました。');
}
return view('admin.using_status.index', $viewData);
} catch (\Exception $e) {
// エラーログの出力
Log::error('区画別利用率状況ページエラー', [
'error' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'park_id' => $parkId ?? null
]);
// エラー発生時のリダイレクト
return redirect()->route('using_status')
->with('error', 'データの取得中にエラーが発生しました。管理者にお問い合わせください。');
}
}
/**
* 駐輪場一覧の取得
* Laravel 12変更点:プライベートメソッドでの処理分離
*
* @return \Illuminate\Support\Collection
*/
// 旧: Helper 直呼びはサービス化により撤去
/**
* 利用率統計データの取得
* Laravel 12変更点:エラーハンドリングの強化
*
* @param int|null $parkId 駐輪場ID
* @return \Illuminate\Support\Collection
*/
// 旧: Helper 直呼びはサービス化により撤去
/**
* 合計値の計算
* Laravel 12変更点:計算ロジックの分離
*
* @param \Illuminate\Support\Collection $stats 統計データ
* @return array
*/
// 旧: Helper 直呼びはサービス化により撤去
/**
* Ajax用API将来拡張用
* Laravel 12対応JSON APIレスポンス
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function apiGetUtilization(Request $request, UsingStatusService $service)
{
try {
$parkId = $request->input('park_id');
// データ取得
$stats = $service->getUtilizationStats($parkId);
$totals = $service->calculateTotals($stats);
return response()->json([
'success' => true,
'data' => $stats,
'totals' => $totals,
'message' => 'データを正常に取得しました。'
]);
} catch (\Exception $e) {
Log::error('API利用率取得エラー', [
'error' => $e->getMessage()
]);
return response()->json([
'success' => false,
'message' => 'データの取得に失敗しました。',
'error' => app()->isProduction() ? null : $e->getMessage()
], 500);
}
}
/**
* データエクスポート(将来拡張用)
* Laravel 12対応CSVエクスポート機能
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function exportCsv(Request $request, UsingStatusService $service, \App\Services\CsvService $csv)
{
try {
$parkId = $request->input('park_id');
$stats = $service->getUtilizationStats($parkId);
$headers = [
'Content-Type' => 'text/csv; charset=UTF-8',
'Content-Disposition' => 'attachment; filename="利用率状況_' . date('Y-m-d_H-i-s') . '.csv"'
];
// サービスでCSV文字列を構築
$rows = [];
// ヘッダー
$rows[] = ['駐輪場', '車種', '限界収容台数', '現在収容台数', '空き', '利用率'];
foreach ($stats as $stat) {
$rows[] = [
(string) $stat->park_name,
(string) $stat->ptype_subject,
(string) $stat->park_limit,
(string) $stat->current_count,
(string) $stat->available,
sprintf('%.1f%%', (float) $stat->usage_rate),
];
}
$csvData = $csv->buildCsvString($rows, ',');
return response($csvData, 200, $headers);
} catch (\Exception $e) {
Log::error('CSVエクスポートエラー', [
'error' => $e->getMessage()
]);
return redirect()->route('using_status')
->with('error', 'CSVエクスポートに失敗しました。');
}
}
/**
* CSVデータの生成プライベートメソッド
* Laravel 12変更点:文字エンコーディングの考慮
*
* @param \Illuminate\Support\Collection $stats
* @return string
*/
// CSV 生成は CsvService に委譲(本メソッドは撤去)
}

View File

@ -0,0 +1,234 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
class LoginController extends Controller
{
/**
* ログイン成功後のリダイレクト先
*
* @var string
*/
protected $redirectTo = '/home';
/**
* コントローラーのインスタンス作成
* Laravel 12変更点:ミドルウェアは routes/web.php で処理するように変更
*
* @return void
*/
public function __construct()
{
// Laravel 12: ミドルウェアは routes/web.php で処理
// Laravel 5.7: $this->middleware('guest')->except('logout'); を使用していた
}
/**
* ログインフォームを表示
*
* @return \Illuminate\View\View
*/
public function showLoginForm()
{
return view('auth.login');
}
/**
* ログインリクエストを処理
* Laravel 12変更点AuthenticatesUsersトレイトを使わず独自実装
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function login(Request $request)
{
$this->validateLogin($request);
// ログイン試行回数制限チェック
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
// ログイン認証試行
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// ログイン失敗時の処理
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
/**
* ログインリクエストのバリデーション
* Laravel 12変更点ope_id, ope_passフィールドを使用Laravel 5.7と同じ)
*
* @param \Illuminate\Http\Request $request
* @return void
*/
protected function validateLogin(Request $request)
{
$request->validate([
'ope_id' => 'required|string', // オペレータID旧システムと同じ
'ope_pass' => 'required|string', // オペレータパスワード(旧システムと同じ)
]);
}
/**
* ログイン認証を試行
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function attemptLogin(Request $request)
{
return Auth::attempt($this->credentials($request), false);
}
/**
* 認証用の資格情報を取得
* Laravel 12変更点ope_idとope_passをpasswordフィールドにマッピング
*
* @param \Illuminate\Http\Request $request
* @return array
*/
protected function credentials(Request $request)
{
// Laravel 5.7: ope_id, ope_passをそのまま使用
// Laravel 12: ope_passをpasswordにマッピングして認証
return $request->only('ope_id') + ['password' => $request->input('ope_pass')];
}
/**
* ログイン成功時のレスポンス
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendLoginResponse(Request $request)
{
$request->session()->regenerate();
$this->clearLoginAttempts($request);
return redirect()->intended($this->redirectTo);
}
/**
* ログイン失敗時のレスポンス
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendFailedLoginResponse(Request $request)
{
throw ValidationException::withMessages([
'ope_id' => [trans('auth.failed')],
]);
}
/**
* ログアウト処理
* Laravel 12変更点:セッション無効化処理を追加
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
public function logout(Request $request)
{
Auth::logout();
// Laravel 12: セッション無効化とトークン再生成を明示的に実行
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/login');
}
/**
* ログイン試行回数が上限を超えているかチェック
* Laravel 12変更点RateLimiterファサードを使用
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function hasTooManyLoginAttempts(Request $request)
{
return RateLimiter::tooManyAttempts(
$this->throttleKey($request), 5
);
}
/**
* ログイン試行回数をインクリメント
*
* @param \Illuminate\Http\Request $request
* @return void
*/
protected function incrementLoginAttempts(Request $request)
{
RateLimiter::hit(
$this->throttleKey($request), 60
);
}
/**
* ログイン試行回数制限をクリア
*
* @param \Illuminate\Http\Request $request
* @return void
*/
protected function clearLoginAttempts(Request $request)
{
RateLimiter::clear($this->throttleKey($request));
}
/**
* ロックアウト発生時のイベント処理
*
* @param \Illuminate\Http\Request $request
* @return void
*/
protected function fireLockoutEvent(Request $request)
{
// 必要に応じてイベントを発火
}
/**
* レート制限用のスロットルキーを取得
*
* @param \Illuminate\Http\Request $request
* @return string
*/
protected function throttleKey(Request $request)
{
return Str::lower($request->input('ope_id')).'|'.$request->ip();
}
/**
* ロックアウト時のレスポンス
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendLockoutResponse(Request $request)
{
$seconds = RateLimiter::availableIn(
$this->throttleKey($request)
);
throw ValidationException::withMessages([
'ope_id' => [trans('auth.throttle', ['seconds' => $seconds])],
]);
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
abstract class Controller
{
//
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* コントローラーのインスタンス作成
* Laravel 12変更点:ミドルウェアは routes/web.php で処理するように変更
*
* @return void
*/
public function __construct()
{
// Laravel 12: ミドルウェアは routes/web.php で処理
// Laravel 5.7: $this->middleware('auth'); を使用していた
}
/**
* アプリケーションのダッシュボードを表示
* 認証後のホーム画面
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('home');
}
}

View File

@ -0,0 +1,87 @@
<?php
namespace App\Legacy;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
/**
* 旧システム オペレータキューモデル(互換)
* - テーブル: operator_que
* - 主キー: que_id
* - 旧定数/関連取得メソッドを維持
*/
class OperatorQue extends Model
{
public const CREATED_AT = 'created_at';
public const UPDATED_AT = 'updated_at';
/**
* キュー種別
*/
public const QueClass = [
'本人確認(社会人)',
'本人確認(学生)',
'タグ発送',
'予約告知電話',
'定期更新電話',
'返金',
'再発行リミット超過',
'支払い催促',
'シール発行催促',
'サーバーエラー',
'プリンタエラー',
'スキャナーエラー',
'プリンタ用紙残少警告',
];
/**
* キューステータス
*/
public const QueStatus = [
'キュー発生',
'キュー作業中',
'キュー作業済',
'返金済',
];
protected $table = 'operator_que';
protected $primaryKey = 'que_id';
protected $fillable = [
'user_id',
'que_comment',
'park_id',
'que_class',
'que_status',
'que_status_comment',
];
protected static function boot()
{
parent::boot();
static::creating(function (OperatorQue $model) {
if (Auth::check() && property_exists($model, 'operator_id')) {
$model->operator_id = Auth::user()->ope_id ?? null;
}
});
}
/**
* 利用者取得(旧互換)
*/
public function getUser()
{
return $this->belongsTo(User::class, 'user_id', 'user_seq')->first();
}
/**
* 駐輪場取得(旧互換)
*/
public function getPark()
{
return $this->belongsTo(Park::class, 'park_id', 'park_id')->first();
}
}

58
app/Legacy/Park.php Normal file
View File

@ -0,0 +1,58 @@
<?php
namespace App\Legacy;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
/**
* 旧システム 駐輪場モデル(互換)
* - テーブル: park
* - 主キー: park_id
*/
class Park extends Model
{
public const CREATED_AT = 'created_at';
public const UPDATED_AT = 'updated_at';
protected $table = 'park';
protected $primaryKey = 'park_id';
protected $fillable = [
'park_name',
'park_ruby',
'park_syllabary',
'park_adrs',
'park_close_flag',
'park_day',
'alert_flag',
'print_number',
'keep_alive',
];
protected static function boot()
{
parent::boot();
static::creating(function (Park $model) {
if (Auth::check() && property_exists($model, 'operator_id')) {
$model->operator_id = Auth::user()->ope_id ?? null;
}
});
}
/**
* 閉設フラグの表示(旧互換)
*/
public function getParkCloseFlagDisplay()
{
if ($this->park_close_flag === 1) {
return '閉設';
}
if ($this->park_close_flag === 0) {
return '開設';
}
return '';
}
}

114
app/Legacy/User.php Normal file
View File

@ -0,0 +1,114 @@
<?php
namespace App\Legacy;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
/**
* 旧システム利用者モデル(互換)
* - テーブル: user
* - 主キー: user_seq
* - 旧機能で参照されるメソッド/定数を保持
*/
class User extends Model
{
public const CREATED_AT = 'created_at';
public const UPDATED_AT = 'updated_at';
protected $table = 'user';
protected $primaryKey = 'user_seq';
protected $fillable = [
'user_seq',
'user_id',
'member_id',
'user_manual_regist_flag',
'user_mailing_flag',
'contract_number',
'user_tag_serial',
'user_tag_serial_64',
'qr_code',
'tag_qr_flag',
'user_aid',
'user_park_number',
'user_place_qrid',
'user_categoryid',
'user_name',
'user_phonetic',
'ward_residents',
'user_gender',
'user_birthdate',
'user_mobile',
'user_homephone',
'user_primemail',
'user_submail',
'user_regident_zip',
'user_regident_pre',
'user_regident_city',
'user_regident_add',
'user_relate_zip',
'user_relate_pre',
'user_relate_city',
'user_relate_add',
'user_workplace',
'user_school',
'user_graduate',
'user_reduction',
'user_idcard',
'user_idcard_chk_flag',
'user_chk_day',
'user_chk_opeid',
'user_tag_issue',
'issue_permission',
'user_quit_flag',
'user_quitday',
'user_remarks',
'user_age',
];
protected static function boot()
{
parent::boot();
static::creating(function (User $model) {
// 旧挙動作成者オペレータIDを保存
if (Auth::check() && property_exists($model, 'ope_id')) {
$model->ope_id = Auth::user()->ope_id ?? null;
}
});
}
/**
* : ユーザーSEQで1件取得
*/
public static function getUserBySeq($seq)
{
return static::find($seq);
}
/**
* : ユーザー区分Usertype
*/
public function getUserType()
{
return $this->belongsTo(Usertype::class, 'user_categoryid', 'user_categoryid')->first();
}
/**
* : 氏名セレクト用
*/
public static function getList()
{
return static::pluck('user_name', 'user_seq');
}
/**
* : 電話番号取得
*/
public static function getUserPhone()
{
return static::select('user_seq', 'user_name', 'user_mobile', 'user_homephone')->get();
}
}

21
app/Models/BaseModel.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* 共通ベースモデル
* - プロジェクト全体のデフォルト設定を集約(必要最小限)
*/
abstract class BaseModel extends Model
{
/**
* タイムスタンプ列名(必要な場合に上書きする)
* 旧システムでは created_at / updated_at を使用
*/
public const CREATED_AT = 'created_at';
public const UPDATED_AT = 'updated_at';
}

View File

@ -0,0 +1,159 @@
<?php
namespace App\Models\Batch;
use Illuminate\Database\Eloquent\Model;
use Carbon\Carbon;
/**
* 共通バッチログモデル - batch_logテーブル
*
* 全てのSHJ系バッチ処理の実行ログを管理する統一モデル
*/
class BatchLog extends Model
{
/**
* テーブル名
*
* @var string
*/
protected $table = 'batch_log';
/**
* プライマリキー
*
* @var string
*/
protected $primaryKey = 'id';
/**
* 一括代入可能な属性
*
* @var array
*/
protected $fillable = [
'process_name', // プロセス名
'status', // ステータス
'start_time', // 開始時刻
'end_time', // 終了時刻
'parameters', // パラメータJSON形式
'message', // メッセージ
'error_details', // エラー詳細
'execution_count', // 実行回数
'success_count', // 成功回数
'error_count', // エラー回数
'created_at', // 作成日時
'updated_at' // 更新日時
];
/**
* キャストする属性
*
* @var array
*/
protected $casts = [
'id' => 'integer',
'start_time' => 'datetime',
'end_time' => 'datetime',
'parameters' => 'array',
'execution_count' => 'integer',
'success_count' => 'integer',
'error_count' => 'integer',
'created_at' => 'datetime',
'updated_at' => 'datetime'
];
/**
* ステータスの定数
*/
const STATUS_START = 'start'; // 開始
const STATUS_RUNNING = 'running'; // 実行中
const STATUS_SUCCESS = 'success'; // 成功
const STATUS_ERROR = 'error'; // エラー
const STATUS_WARNING = 'warning'; // 警告
const STATUS_CANCELLED = 'cancelled'; // キャンセル
/**
* 通用バッチログ作成メソッド
*
* 任意のバッチ処理で使用可能な統一ログ記録機能
* 実際の実行コマンド名をそのまま記録
*
* @param string $processName プロセス名shj1, shj9:daily等
* @param string $status ステータス
* @param array $parameters パラメーター配列
* @param string $message メッセージ
* @param array $additionalData 追加データdevice_id等
* @return BatchLog 作成されたバッチログ
*/
public static function createBatchLog(
string $processName,
string $status,
array $parameters = [],
string $message = '',
array $additionalData = []
): BatchLog {
// パラメーターに追加データをマージ
$allParameters = array_merge($parameters, $additionalData, [
'executed_at' => now()->toISOString()
]);
return self::create([
'process_name' => $processName,
'status' => $status,
'start_time' => now(),
'end_time' => ($status === self::STATUS_SUCCESS || $status === self::STATUS_ERROR) ? now() : null,
'parameters' => $allParameters,
'message' => $message,
'error_details' => ($status === self::STATUS_ERROR) ? $message : null,
'execution_count' => 1,
'success_count' => $status === self::STATUS_SUCCESS ? 1 : 0,
'error_count' => $status === self::STATUS_ERROR ? 1 : 0
]);
}
/**
* 成功時のステータスコメント生成
*
* @return string ステータスコメント
*/
public static function getSuccessComment(): string
{
return '処理成功';
}
/**
* エラー時のステータスコメント生成
*
* @param string $errorMessage エラーメッセージ
* @return string ステータスコメント
*/
public static function getErrorComment(string $errorMessage): string
{
return 'エラー: ' . $errorMessage;
}
/**
* バッチログの文字列表現
*
* @return string
*/
public function __toString(): string
{
return sprintf(
'BatchLog[ID:%d, Process:%s, Status:%s, Time:%s]',
$this->id,
$this->process_name,
$this->status,
$this->start_time ? $this->start_time->format('Y-m-d H:i:s') : 'N/A'
);
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace App\Models\Concerns;
use Illuminate\Database\Eloquent\Builder;
/**
* ソート用トレイト
* - 画面の sort/sort_type 入力に合わせて安全に orderBy を適用
*/
trait HasSortable
{
/**
* 安全なソート適用
*
* @param Builder $query
* @param string|null $column
* @param string|null $direction
* @param array<string> $allowList 許可カラム(省略時は無制限だが推奨しない)
*/
public function scopeApplySort(Builder $query, ?string $column, ?string $direction, array $allowList = []): Builder
{
if (empty($column)) {
return $query;
}
$dir = strtolower($direction ?? 'asc');
if (!in_array($dir, ['asc', 'desc'], true)) {
$dir = 'asc';
}
if (!empty($allowList) && !in_array($column, $allowList, true)) {
return $query; // 許可されていないカラムは無視
}
return $query->orderBy($column, $dir);
}
}

83
app/Models/Device.php Normal file
View File

@ -0,0 +1,83 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* デバイスモデル - deviceテーブル
*
* ハードウェアデバイス(印刷機等)の情報を管理するモデル
* batch_logテーブルとの関連でバッチ処理ログに使用される
*/
class Device extends Model
{
protected $table = 'device';
protected $primaryKey = 'device_id';
public $timestamps = true;
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at';
protected $fillable = [
'park_id', // 駐輪場ID
'device_type', // デバイスタイプ
'device_subject', // デバイス件名
'device_identifier', // デバイス識別子
'device_work', // デバイス作業
'device_workstart', // 作業開始日
'device_replace', // 交換日
'device_remarks', // 備考
'operator_id' // オペレータID
];
protected $casts = [
'device_workstart' => 'date',
'device_replace' => 'date',
];
/**
* 駐輪場との関連付け
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function park()
{
// UsingStatus系は廃止。正式モデル Park を使用。
return $this->belongsTo(Park::class, 'park_id', 'park_id');
}
/**
* バッチログとの関連付けbatch_logテーブル
* 統一BatchLogで管理される
*
* @return \Illuminate\Database\Query\Builder
*/
public function batchLogs()
{
return \DB::table('batch_log')
->where('parameters->device_id', $this->device_id);
}
/**
* デバイスIDの存在確認
*
* @param int $deviceId デバイスID
* @return bool 存在するかどうか
*/
public static function exists(int $deviceId): bool
{
return self::where('device_id', $deviceId)->exists();
}
/**
* デバイス情報を取得
*
* @param int $deviceId デバイスID
* @return Device|null デバイス情報
*/
public static function findByDeviceId(int $deviceId): ?Device
{
return self::where('device_id', $deviceId)->first();
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* メールテンプレートモデル - mail_templateテーブル
*
* メール送信テンプレート情報を管理するモデル
* 使用プログラムIDに基づいてテンプレートを取得し、メール送信処理で使用される
*/
class MailTemplate extends Model
{
protected $table = 'mail_template';
protected $primaryKey = 'mail_template_id';
public $timestamps = true;
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at';
protected $fillable = [
'pg_id', // 使用プログラムID
'internal_id', // 内部ID
'mgr_cc_flag', // エリアマネージャー同報フラグ
'bcc_adrs', // BCCアドレス
'use_flag', // 使用フラグ
'memo', // メモ
'subject', // 件名
'text', // 本文
'operator_id' // オペレータID
];
protected $casts = [
'mgr_cc_flag' => 'boolean',
'use_flag' => 'boolean',
];
/**
* 使用プログラムIDでメールテンプレート情報を取得
*
* 仕様書に基づく検索条件:
* - 使用プログラムID = 入力パラメーター
* - 使用フラグ = 1
*
* @param int $pgId 使用プログラムID
* @return MailTemplate|null メールテンプレート情報
*/
public static function getByProgramId(int $pgId): ?MailTemplate
{
return self::where('pg_id', $pgId)
->where('use_flag', 1)
->first();
}
/**
* エリアマネージャー同報が有効かチェック
*
* @return bool エリアマネージャー同報フラグ
*/
public function isManagerCcEnabled(): bool
{
return (bool) $this->mgr_cc_flag;
}
/**
* BCCアドレスを取得
*
* @return string|null BCCアドレス
*/
public function getBccAddress(): ?string
{
return $this->bcc_adrs;
}
/**
* メール件名を取得
*
* @return string|null メール件名
*/
public function getSubject(): ?string
{
return $this->subject;
}
/**
* メール本文を取得
*
* @return string|null メール本文
*/
public function getText(): ?string
{
return $this->text;
}
}

177
app/Models/Ope.php Normal file
View File

@ -0,0 +1,177 @@
<?php
namespace App\Models;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Utils;
/**
* オペレータ認証モデル
* Laravel 12変更点App\ModelsディレクトリからApp\Modelsディレクトリに移動
* Laravel 5.7: App直下に配置されていた
*/
class Ope extends Authenticatable
{
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at';
// オペレータタイプ定数(旧システムから継承)
const OPE_TYPE = [
'管理者',
'マネージャー',
'オペレーター',
'エリアマネージャー',
];
protected $table = 'ope'; // データベーステーブル名(旧システムと同じ)
protected $primaryKey = 'ope_id'; // プライマリキー(旧システムと同じ)
/**
* 一括代入可能な属性
* Laravel 5.7から引き継いだフィールド構成
*/
protected $fillable = [
'//TODO オペレータID not found in database specs',
'ope_name', // オペレータ名
'ope_type', // オペレータ種別
'ope_mail', // メールアドレス
'ope_phone', // 電話番号
'ope_sendalart_que1', // キュー1アラート送信
'ope_sendalart_que2', // キュー2アラート送信
'ope_sendalart_que3', // キュー3アラート送信
'ope_sendalart_que4', // キュー4アラート送信
'ope_sendalart_que5', // キュー5アラート送信
'ope_sendalart_que6', // キュー6アラート送信
'ope_sendalart_que7', // キュー7アラート送信
'ope_sendalart_que8', // キュー8アラート送信
'ope_sendalart_que9', // キュー9アラート送信
'ope_sendalart_que10', // キュー10アラート送信
'ope_sendalart_que11', // キュー11アラート送信
'ope_sendalart_que12', // キュー12アラート送信
'ope_sendalart_que13', // キュー13アラート送信
'ope_auth1', // 権限1
'ope_auth2', // 権限2
'ope_auth3', // 権限3
'ope_auth4', // 権限4
'ope_quit_flag', // 退職フラグ
'ope_quitday' // 退職日
];
/**
* 認証用パスワードを取得
* Laravel 12変更点ope_passフィールドを認証パスワードとして使用
*
* @return string
*/
public function getAuthPassword()
{
return $this->ope_pass;
}
/**
* passwordフィールドアクセサー読み取り用
* Laravel 12対応Laravel認証システムがpasswordフィールドを読み取る際にope_passの値を返す
* Laravel 5.7との差異:旧システムでは不要だった仮想フィールド対応
*
* @return string|null
*/
public function getPasswordAttribute()
{
// データベースのope_passフィールドの値を返す
return $this->attributes['ope_pass'] ?? null;
}
/**
* passwordフィールドミューテータ書き込み用
* Laravel 12対応Laravel認証システムがpasswordフィールドを更新する際にope_passフィールドに保存
* Laravel 5.7との差異:旧システムでは不要だった仮想フィールド対応
*
* @param string $value パスワード値
* @return void
*/
public function setPasswordAttribute($value)
{
// passwordフィールドへの書き込みをope_passフィールドにリダイレクト
$this->attributes['ope_pass'] = $value;
}
/**
* 属性設定のオーバーライド
* Remember Tokenの設定を無効化旧システムでは使用していない
*
* @param string $key
* @param mixed $value
*/
public function setAttribute($key, $value)
{
$isRememberTokenAttribute = $key == $this->getRememberTokenName();
if (!$isRememberTokenAttribute) {
parent::setAttribute($key, $value);
}
}
/**
* オペレータ検索機能
* Laravel 5.7から継承したメソッド(検索・ソート・ページネーション)
*
* @param array $inputs
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|\Illuminate\Database\Eloquent\Collection
*/
public static function search($inputs)
{
$list = self::query();
// POST検索条件の処理
if ($inputs['isMethodPost']) {
// 検索条件があればここに追加
}
// ソート処理
if ($inputs['sort']) {
$list->orderBy($inputs['sort'], $inputs['sort_type']);
}
// エクスポート用かページネーション用かの判定
if ($inputs['isExport']) {
$list = $list->get();
} else {
$list = $list->paginate(Utils::item_per_page);
}
return $list;
}
/**
* プライマリキーでオペレータを取得
*
* @param mixed $pk
* @return \App\Models\Ope|null
*/
public static function getByPk($pk)
{
return self::find($pk);
}
/**
* プライマリキーでオペレータを削除
*
* @param array $arr
* @return int
*/
public static function deleteByPk($arr)
{
return self::whereIn('ope_id', $arr)->delete();
}
/**
* オペレータのリストを取得(セレクトボックス用)
* Laravel 5.7から継承したメソッド
*
* @return \Illuminate\Support\Collection
*/
public static function getList()
{
return self::pluck('ope_name', 'ope_id');
}
}

44
app/Models/Park.php Normal file
View File

@ -0,0 +1,44 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* 駐輪場モデル - parkテーブル正式モデル
* 旧UsingStatusParkの責務を置き換え
*/
class Park extends Model
{
protected $table = 'park';
protected $primaryKey = 'park_id';
public $timestamps = true;
public const CREATED_AT = 'created_at';
public const UPDATED_AT = 'updated_at';
protected $fillable = [
'park_name',
'park_ruby',
'park_syllabary',
'park_adrs',
'park_close_flag',
'park_day',
'alert_flag',
'print_number',
'keep_alive',
'city_id',
'operator_id',
];
/**
* 料金設定との関連付け
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function prices()
{
return $this->hasMany(PriceA::class, 'park_id', 'park_id');
}
}

60
app/Models/PriceA.php Normal file
View File

@ -0,0 +1,60 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* 料金・容量モデル - price_aテーブル正式モデル
* 旧UsingStatusPriceの責務を置き換え
*/
class PriceA extends Model
{
protected $table = 'price_a';
protected $primaryKey = 'price_parkplaceid';
public $timestamps = true;
public const CREATED_AT = 'created_at';
public const UPDATED_AT = 'updated_at';
protected $fillable = [
'prine_name',
'price_month',
'park_id',
'psection_id',
'price_ptypeid',
'user_categoryid',
'pplace_id',
'park_number',
'park_standard',
'park_limit',
'price',
'operator_id',
];
/**
* 駐輪場
*/
public function park()
{
return $this->belongsTo(Park::class, 'park_id', 'park_id');
}
/**
* 車種分類
*/
public function ptype()
{
return $this->belongsTo(Ptype::class, 'price_ptypeid', 'ptype_id');
}
/**
* 定期契約
*/
public function contracts()
{
return $this->hasMany(RegularContract::class, 'price_parkplaceid', 'price_parkplaceid');
}
}

35
app/Models/Ptype.php Normal file
View File

@ -0,0 +1,35 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* 車種分類モデル - ptypeテーブル正式モデル
* 旧UsingStatusPtypeの責務を置き換え
*/
class Ptype extends Model
{
protected $table = 'ptype';
protected $primaryKey = 'ptype_id';
public $timestamps = true;
public const CREATED_AT = 'created_at';
public const UPDATED_AT = 'updated_at';
protected $fillable = [
'ptype_subject',
'ptype_remarks',
'operator_id',
];
/**
* 料金設定
*/
public function prices()
{
return $this->hasMany(PriceA::class, 'price_ptypeid', 'ptype_id');
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* 定期契約モデル - regular_contractテーブル正式モデル
* 旧UsingStatusContractの責務を置き換え
*/
class RegularContract extends Model
{
protected $table = 'regular_contract';
protected $primaryKey = 'contract_id';
public $timestamps = true;
public const CREATED_AT = 'created_at';
public const UPDATED_AT = 'updated_at';
protected $fillable = [
'contract_qr_id',
'user_id',
'user_categoryid',
'reserve_id',
'park_id',
'price_parkplaceid',
'user_securitynum',
'reserve_date',
'contract_reserve',
'contract_created_at',
'contract_updated_at',
'contract_cancelday',
'contract_reduction',
'contract_periods',
'contract_periode',
'enable_months',
'printable_date',
'contract_taxid',
'billing_amount',
'contract_payment_day',
'contract_money',
'refunds',
'refunds_comment',
'repayment_at',
'contact_guid',
'contact_shop_code',
'contract_cvs_class',
'contract_flag',
'settlement_transaction_id',
'contract_seal_issue',
'seal_reissue_request',
'update_flag',
'contract_permission',
'contract_cancel_flag',
'800m_flag',
'tag_qr_flag',
'tag_change_flag',
'park_position',
'ope_id',
'contract_manual',
'contract_notice',
'contract_payment_number',
];
/**
* 料金設定
*/
public function price()
{
return $this->belongsTo(PriceA::class, 'price_parkplaceid', 'price_parkplaceid');
}
}

48
app/Models/User.php Normal file
View File

@ -0,0 +1,48 @@
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\ShjFourCService;
use App\Services\ShjMailSendService;
class AppServiceProvider extends ServiceProvider
{
/**
* アプリケーションサービスの登録
*/
public function register(): void
{
// SHJ-4C室割当処理サービスを登録
$this->app->singleton(ShjFourCService::class, function ($app) {
return new ShjFourCService(
$app->make(\App\Models\Park::class),
$app->make(\App\Models\RegularContract::class),
$app->make(\App\Models\Batch\BatchLog::class)
);
});
// SHJメール送信処理サービスを登録
$this->app->singleton(ShjMailSendService::class, function ($app) {
return new ShjMailSendService(
$app->make(\App\Models\MailTemplate::class),
$app->make(\App\Models\Batch\BatchLog::class)
);
});
}
/**
* アプリケーションサービスの初期化
*/
public function boot(): void
{
//
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
/**
* 旧システム互換用サービスプロバイダ
* 目的旧Blade/旧コードが参照する FQCN: \App\OperatorQueを新プロジェクトで解決可能にする
*/
class LegacyServiceProvider extends ServiceProvider
{
/**
* アプリ起動時の登録処理
* - 旧FQCNと互換レイヤApp\Legacy\*)のエイリアスを貼る
*/
public function register(): void
{
// \App\OperatorQue → \App\Legacy\OperatorQue
if (!class_exists(\App\OperatorQue::class) && class_exists(\App\Legacy\OperatorQue::class)) {
class_alias(\App\Legacy\OperatorQue::class, \App\OperatorQue::class);
}
// \App\User → \App\Legacy\User
if (!class_exists(\App\User::class) && class_exists(\App\Legacy\User::class)) {
class_alias(\App\Legacy\User::class, \App\User::class);
}
// \App\Park → \App\Legacy\Park
if (!class_exists(\App\Park::class) && class_exists(\App\Legacy\Park::class)) {
class_alias(\App\Legacy\Park::class, \App\Park::class);
}
}
/**
* アプリ起動後(ブート時)の処理
*/
public function boot(): void
{
// 現時点では何もしない(必要に応じて追加)
}
}

View File

@ -0,0 +1,72 @@
<?php
namespace App\Services;
/**
* CSV入出力サービス
* - UtilsのcsvToArrayラッパー将来のエクスポート共通化
*/
class CsvService
{
/**
* CSV 配列
*
* @param string $path ファイルパス
* @param string $delimiter 区切り文字
* @return array|false
*/
public function toArray(string $path, string $delimiter = ',')
{
return \App\Utils::csvToArray($path, $delimiter);
}
/**
* 配列 CSV簡易版
*
* @param string $path 出力パス
* @param array<array-key, array<array-key, string|int|float|null>> $rows 行データ
* @param string $delimiter 区切り文字
*/
public function fromArray(string $path, array $rows, string $delimiter = ','): bool
{
$fp = fopen($path, 'w');
if ($fp === false) return false;
foreach ($rows as $row) {
// null を空文字として扱う
$normalized = array_map(static function ($v) {
return $v === null ? '' : (string) $v;
}, $row);
fputcsv($fp, $normalized, $delimiter);
}
fclose($fp);
return true;
}
/**
* 配列 CSV文字列メモリ構築
*
* @param array<array-key, array<array-key, string|int|float|null>> $rows 行データ
* @param string $delimiter 区切り文字(デフォルト: カンマ)
* @param bool $withBom 先頭にUTF-8 BOMを付与するか
* @return string CSVテキスト
*/
public function buildCsvString(array $rows, string $delimiter = ',', bool $withBom = true): string
{
$stream = fopen('php://temp', 'r+');
if ($stream === false) {
return '';
}
foreach ($rows as $row) {
$normalized = array_map(static function ($v) {
return $v === null ? '' : (string) $v;
}, $row);
fputcsv($stream, $normalized, $delimiter);
}
rewind($stream);
$csv = stream_get_contents($stream) ?: '';
fclose($stream);
return $withBom ? ("\xEF\xBB\xBF" . $csv) : $csv;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Services;
use Illuminate\Http\UploadedFile;
/**
* ファイル入出力サービス
* - 旧UtilsのuploadFile互換ラッパー
*/
class FileService
{
/**
* 画像ファイルを保存(旧互換)
*
* @param UploadedFile $file
* @param string|null $fileNamePrefix
* @return string|false 保存ファイル名
*/
public function uploadImage(UploadedFile $file, ?string $fileNamePrefix = null)
{
return \App\Utils::uploadFile($file, $fileNamePrefix);
}
/**
* 画像URL取得旧互換
*/
public function getImageUrl(string $filename = ''): string
{
return \App\Utils::getImageUrl($filename);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Services;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/**
* オペレータキュー取得サービス
* - Bladeからモデル定数/メソッド直呼びを減らすための集約
*/
class OperatorQueService
{
/**
* 画面検索条件でページング
*/
public function paginate(array $inputs): LengthAwarePaginator
{
$query = \App\Legacy\OperatorQue::query();
$sort = $inputs['sort'] ?? 'que_id';
$dir = $inputs['sort_type'] ?? 'desc';
return $query->orderBy($sort, $dir)->paginate(\App\Utils::item_per_page);
}
}

View File

@ -0,0 +1,406 @@
<?php
namespace App\Services;
use App\Models\Park;
use App\Models\RegularContract;
use App\Models\Batch\BatchLog;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
/**
* SHJ-4C 室割当処理サービス
*
* ゾーン情報取得及び割当処理を実行するビジネスロジック
* バッチ処理「SHJ-4C室割当」の核となる処理を担当
*/
class ShjFourCService
{
/**
* Park モデル
*
* @var Park
*/
protected $parkModel;
/**
* RegularContract モデル
*
* @var RegularContract
*/
protected $contractModel;
/**
* BatchLog モデル
*
* @var BatchLog
*/
protected $batchLogModel;
/**
* コンストラクタ
*
* @param Park $parkModel
* @param RegularContract $contractModel
* @param BatchLog $batchLogModel
*/
public function __construct(
Park $parkModel,
RegularContract $contractModel,
BatchLog $batchLogModel
) {
$this->parkModel = $parkModel;
$this->contractModel = $contractModel;
$this->batchLogModel = $batchLogModel;
}
/**
* SHJ-4C 室割当処理メイン実行
*
* 処理フロー:
* 【処理1】ゾーン情報取得
* 【判断1】割当判定
* 【処理2】バッチログ作成
* 【処理3】処理結果返却
*
* @param int $parkId 駐輪場ID
* @param int $ptypeId 駐輪分類ID
* @param int $psectionId 車種区分ID
* @return array 処理結果
*/
public function executeRoomAllocation(int $parkId, int $ptypeId, int $psectionId): array
{
$batchLogId = null;
try {
// バッチ処理開始ログ作成(実際のコマンド名を記録)
$batchLog = BatchLog::createBatchLog(
'shj4c',
BatchLog::STATUS_START,
[
'park_id' => $parkId,
'ptype_id' => $ptypeId,
'psection_id' => $psectionId
],
'SHJ-4C 室割当処理開始'
);
$batchLogId = $batchLog->id;
Log::info('SHJ-4C 室割当処理開始', [
'batch_log_id' => $batchLogId,
'park_id' => $parkId,
'ptype_id' => $ptypeId,
'psection_id' => $psectionId
]);
// 【処理1】ゾーン情報取得
$zoneInfo = $this->getZoneInformation($parkId, $ptypeId, $psectionId);
if (empty($zoneInfo)) {
$message = '対象のゾーン情報が見つかりません';
// バッチログ更新(通用方法使用)
$batchLog->update([
'status' => BatchLog::STATUS_ERROR,
'end_time' => now(),
'message' => $message,
'error_details' => $message,
'error_count' => 1
]);
return [
'success' => false,
'message' => $message,
'batch_log_id' => $batchLogId
];
}
// 【判断1】割当判定処理
$allocationResult = $this->performAllocationJudgment($zoneInfo, $parkId, $ptypeId, $psectionId);
if (!$allocationResult['can_allocate']) {
// 割当NGの場合、対象事室番号を設定
$this->setTargetRoomNumber($allocationResult['target_room_number']);
// バッチログ更新(警告)
$batchLog->update([
'status' => BatchLog::STATUS_WARNING,
'end_time' => now(),
'message' => '割当処理NG: ' . $allocationResult['reason'],
'success_count' => 1 // 処理は成功したが割当NGのため
]);
return [
'success' => true,
'message' => '割当判定完了割当NG',
'allocation_result' => $allocationResult,
'batch_log_id' => $batchLogId
];
}
// 【処理2】割当実行割当OKの場合
$executionResult = $this->executeAllocation($zoneInfo, $allocationResult);
// バッチ処理完了ログ更新
$batchLog->update([
'status' => BatchLog::STATUS_SUCCESS,
'end_time' => now(),
'message' => 'SHJ-4C 室割当処理正常完了',
'success_count' => 1
]);
Log::info('SHJ-4C 室割当処理完了', [
'batch_log_id' => $batchLogId,
'execution_result' => $executionResult
]);
// 【処理3】処理結果返却
return [
'success' => true,
'message' => 'SHJ-4C 室割当処理が正常に完了しました',
'zone_info' => $zoneInfo,
'allocation_result' => $allocationResult,
'execution_result' => $executionResult,
'batch_log_id' => $batchLogId
];
} catch (\Exception $e) {
$errorMessage = 'SHJ-4C 室割当処理でエラーが発生: ' . $e->getMessage();
if (isset($batchLog) && $batchLog) {
$batchLog->update([
'status' => BatchLog::STATUS_ERROR,
'end_time' => now(),
'message' => $errorMessage,
'error_details' => $e->getMessage(),
'error_count' => 1
]);
}
Log::error('SHJ-4C 室割当処理エラー', [
'batch_log_id' => $batchLogId,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return [
'success' => false,
'message' => $errorMessage,
'details' => $e->getMessage(),
'batch_log_id' => $batchLogId
];
}
}
/**
* 【処理1】ゾーン情報取得
*
* 駐輪場ID、駐輪分類ID、車種区分IDに紐づくゾーン情報を取得する
* SQLクエリは設計書の仕様に基づく
*
* @param int $parkId 駐輪場ID
* @param int $ptypeId 駐輪分類ID
* @param int $psectionId 車種区分ID
* @return array ゾーン情報
*/
private function getZoneInformation(int $parkId, int $ptypeId, int $psectionId): array
{
try {
// 設計書に記載されたSQLクエリに基づくゾーン情報取得
$zoneInfo = DB::table('zone as T1')
->select([
'T1.zone_id',
'T1.zone_name',
'T1.zone_contracted_count as zone_contracted_count',
'T1.zone_capacity as zone_capacity',
'T1.zone_permitted_count as zone_permitted_count',
'T1.zone_sort',
'T2.update_grace_period_start_date',
'T2.update_grace_period_end_date'
])
->join('park as T2', 'T1.park_id', '=', 'T2.park_id')
->where('T1.park_id', $parkId)
->where('T1.ptype_id', $ptypeId)
->where('T1.psection_id', $psectionId)
->where('T1.delete_flag', 0)
->orderBy('T1.zone_sort')
->get()
->toArray();
Log::info('ゾーン情報取得完了', [
'park_id' => $parkId,
'ptype_id' => $ptypeId,
'psection_id' => $psectionId,
'zone_count' => count($zoneInfo)
]);
return $zoneInfo;
} catch (\Exception $e) {
Log::error('ゾーン情報取得エラー', [
'park_id' => $parkId,
'ptype_id' => $ptypeId,
'psection_id' => $psectionId,
'error' => $e->getMessage()
]);
throw $e;
}
}
/**
* 【判断1】割当判定処理
*
* ゾーン内の定期契約マスタから該当レコード数を取得し、
* 定期契約マスタから最適な車室番号を選定する割当判定を実行
*
* @param array $zoneInfo ゾーン情報
* @param int $parkId 駐輪場ID
* @param int $ptypeId 駐輪分類ID
* @param int $psectionId 車種区分ID
* @return array 割当判定結果
*/
private function performAllocationJudgment(array $zoneInfo, int $parkId, int $ptypeId, int $psectionId): array
{
try {
foreach ($zoneInfo as $zone) {
// 各ゾーンに対する定期契約情報取得
$contractInfo = $this->getRegularContractInfo($parkId, $zone->zone_id, $ptypeId, $psectionId);
// 割当可能性判定
if ($this->canAllocateToZone($zone, $contractInfo)) {
return [
'can_allocate' => true,
'zone_id' => $zone->zone_id,
'zone_name' => $zone->zone_name,
'contract_info' => $contractInfo,
'reason' => '割当OK: ゾーンID ' . $zone->zone_id
];
}
}
// 全ゾーンで割当NGの場合
$targetRoomNumber = $this->generateTargetRoomNumber($parkId, $ptypeId, $psectionId);
return [
'can_allocate' => false,
'target_room_number' => $targetRoomNumber,
'reason' => '全ゾーンで割当できませんでした'
];
} catch (\Exception $e) {
Log::error('割当判定処理エラー', [
'error' => $e->getMessage()
]);
throw $e;
}
}
/**
* 定期契約情報取得
*
* 設計書のSQLクエリに基づく定期契約マスタ検索
*
* @param int $parkId 駐輪場ID
* @param int $zoneId ゾーンID
* @param int $ptypeId 駐輪分類ID
* @param int $psectionId 車種区分ID
* @return array 定期契約情報
*/
private function getRegularContractInfo(int $parkId, int $zoneId, int $ptypeId, int $psectionId): array
{
$currentDate = Carbon::now()->format('Y-m-d');
$contractInfo = DB::table('regular_contract as T1')
->select(['T1.contract_id'])
->where('T1.park_id', $parkId)
->where('T1.zone_id', $zoneId)
->where('T1.ptype_id', $ptypeId)
->where('T1.psection_id', $psectionId)
->where('T1.place_no', '!=', '')
->where(function ($query) use ($currentDate) {
$query->where(function ($subQuery) use ($currentDate) {
// パターンA: 処理1.更新期間開始日 <= 処理1.更新期間終了日 の場合
$subQuery->whereRaw("date_format(now(), '%y-%m-%d') >= date_format(now(), '%y-%m-%d')")
->whereRaw("date_format(now(), '%y-%m-%d') <= date_format(now(), '%y-%m-%d')");
})
->orWhere(function ($subQuery) use ($currentDate) {
// パターンB: その他の場合
$subQuery->whereRaw("date_format(now(), '%y-%m-%d') >= date_format(now(), '%y-%m-%d')");
});
})
->where('T1.contract_flag', 1)
->get()
->toArray();
return $contractInfo;
}
/**
* ゾーン割当可能性判定
*
* @param object $zone ゾーン情報
* @param array $contractInfo 契約情報
* @return bool 割当可能かどうか
*/
private function canAllocateToZone($zone, array $contractInfo): bool
{
$contractedCount = count($contractInfo);
$capacity = $zone->zone_capacity;
// 空きがある場合は割当可能
return $contractedCount < $capacity;
}
/**
* 対象事室番号生成
*
* @param int $parkId 駐輪場ID
* @param int $ptypeId 駐輪分類ID
* @param int $psectionId 車種区分ID
* @return string 対象事室番号
*/
private function generateTargetRoomNumber(int $parkId, int $ptypeId, int $psectionId): string
{
return sprintf('%d_%d_%d', $parkId, $ptypeId, $psectionId);
}
/**
* 対象事室番号設定
*
* @param string $targetRoomNumber 対象事室番号
* @return void
*/
private function setTargetRoomNumber(string $targetRoomNumber): void
{
Log::info('対象事室番号設定', [
'target_room_number' => $targetRoomNumber
]);
// 実際の事室番号設定ロジックをここに実装
// 具体的な仕様が必要な場合は後で追加実装
}
/**
* 割当実行
*
* @param array $zoneInfo ゾーン情報
* @param array $allocationResult 割当判定結果
* @return array 実行結果
*/
private function executeAllocation(array $zoneInfo, array $allocationResult): array
{
// 割当実行の具体的なロジックを実装
// 設計書に詳細仕様があれば追加実装
return [
'executed' => true,
'zone_id' => $allocationResult['zone_id'],
'message' => '割当実行完了'
];
}
}

View File

@ -0,0 +1,443 @@
<?php
namespace App\Services;
use App\Models\MailTemplate;
use App\Models\Batch\BatchLog;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
/**
* SHJ メール送信処理サービス
*
* メールテンプレートを使用したメール送信処理を実行するビジネスロジック
* バッチ処理「SHJメール送信」の核となる処理を担当
*/
class ShjMailSendService
{
/**
* MailTemplate モデル
*
* @var MailTemplate
*/
protected $mailTemplateModel;
/**
* BatchLog モデル
*
* @var BatchLog
*/
protected $batchLogModel;
/**
* コンストラクタ
*
* @param MailTemplate $mailTemplateModel
* @param BatchLog $batchLogModel
*/
public function __construct(
MailTemplate $mailTemplateModel,
BatchLog $batchLogModel
) {
$this->mailTemplateModel = $mailTemplateModel;
$this->batchLogModel = $batchLogModel;
}
/**
* SHJ メール送信処理メイン実行
*
* 処理フロー:
* 【処理1】入力パラメーターをチェックする
* 【処理2】メール送信テンプレート情報を取得する
* 【判断2】取得結果判定
* 【処理3】メールを送信する
* 【処理4】処理結果を返却する
*
* @param string $mailAddress メールアドレス
* @param string $backupMailAddress 予備メールアドレス
* @param int $mailTemplateId メールテンプレートID
* @return array 処理結果
*/
public function executeMailSend(string $mailAddress, string $backupMailAddress, int $mailTemplateId): array
{
$batchLogId = null;
try {
// バッチ処理開始ログ作成(実際のコマンド名を記録)
$batchLog = BatchLog::createBatchLog(
'shj-mail-send',
BatchLog::STATUS_START,
[
'mail_address' => $mailAddress,
'backup_mail_address' => $backupMailAddress,
'mail_template_id' => $mailTemplateId
],
'SHJ メール送信処理開始'
);
$batchLogId = $batchLog->id;
Log::info('SHJ メール送信処理開始', [
'batch_log_id' => $batchLogId,
'mail_address' => $mailAddress,
'backup_mail_address' => $backupMailAddress,
'mail_template_id' => $mailTemplateId
]);
// 【処理1】入力パラメーターをチェックする
$paramCheckResult = $this->checkInputParameters($mailAddress, $backupMailAddress, $mailTemplateId);
if (!$paramCheckResult['valid']) {
$this->updateBatchLog($batchLogId, 'error', $paramCheckResult['message']);
return [
'success' => false,
'result_code' => 1, // 異常終了
'message' => $paramCheckResult['message'],
'batch_log_id' => $batchLogId
];
}
// 【処理2】メール送信テンプレート情報を取得する
$templateInfo = $this->getMailTemplateInfo($mailTemplateId);
// 【判断2】取得結果判定
if (empty($templateInfo)) {
$message = "メールテンプレートが存在しません。テンプレートID: {$mailTemplateId}";
$this->updateBatchLog($batchLogId, 'error', $message);
return [
'success' => false,
'result_code' => 1, // 異常終了
'message' => $message,
'batch_log_id' => $batchLogId
];
}
// 【処理3】メールを送信する
$mailSendResult = $this->sendMail($mailAddress, $backupMailAddress, $templateInfo);
if (!$mailSendResult['success']) {
$this->updateBatchLog($batchLogId, 'error', $mailSendResult['message']);
return [
'success' => false,
'result_code' => 1, // 異常終了
'message' => $mailSendResult['message'],
'batch_log_id' => $batchLogId
];
}
// バッチ処理完了ログ更新
$this->updateBatchLog($batchLogId, 'success', 'SHJ メール送信処理正常完了');
Log::info('SHJ メール送信処理完了', [
'batch_log_id' => $batchLogId,
'mail_send_result' => $mailSendResult
]);
// 【処理4】処理結果を返却する
return [
'success' => true,
'result_code' => 0, // 正常終了
'message' => 'SHJ メール送信処理が正常に完了しました',
'mail_send_result' => $mailSendResult,
'batch_log_id' => $batchLogId
];
} catch (\Exception $e) {
$errorMessage = 'SHJ メール送信処理でエラーが発生: ' . $e->getMessage();
if ($batchLogId) {
$this->updateBatchLog($batchLogId, 'error', $errorMessage);
}
Log::error('SHJ メール送信処理エラー', [
'batch_log_id' => $batchLogId,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return [
'success' => false,
'result_code' => 1, // 異常終了
'message' => $errorMessage,
'details' => $e->getMessage(),
'batch_log_id' => $batchLogId
];
}
}
/**
* 【処理1】入力パラメーターをチェックする
*
* 仕様書に基づく詳細チェック:
* - メールアドレス形式チェック
* - テンプレートID存在性チェック
*
* @param string $mailAddress メールアドレス
* @param string $backupMailAddress 予備メールアドレス
* @param int $mailTemplateId メールテンプレートID
* @return array チェック結果
*/
private function checkInputParameters(string $mailAddress, string $backupMailAddress, int $mailTemplateId): array
{
try {
// メールアドレス存在チェック(いずれか必須)
if (empty($mailAddress) && empty($backupMailAddress)) {
return [
'valid' => false,
'message' => 'パラメーターNG: メールアドレスまたは予備メールアドレスのいずれかは必須です'
];
}
// メールアドレス形式チェック
if (!empty($mailAddress) && !filter_var($mailAddress, FILTER_VALIDATE_EMAIL)) {
return [
'valid' => false,
'message' => 'パラメーターNG: メールアドレスの形式が正しくありません'
];
}
if (!empty($backupMailAddress) && !filter_var($backupMailAddress, FILTER_VALIDATE_EMAIL)) {
return [
'valid' => false,
'message' => 'パラメーターNG: 予備メールアドレスの形式が正しくありません'
];
}
// メールテンプレートID形式チェック
if ($mailTemplateId <= 0) {
return [
'valid' => false,
'message' => 'パラメーターNG: メールテンプレートIDは正の整数である必要があります'
];
}
Log::info('入力パラメーターチェック完了', [
'mail_address' => $mailAddress,
'backup_mail_address' => $backupMailAddress,
'mail_template_id' => $mailTemplateId
]);
return [
'valid' => true,
'message' => 'パラメーターチェックOK'
];
} catch (\Exception $e) {
Log::error('入力パラメーターチェックエラー', [
'error' => $e->getMessage()
]);
return [
'valid' => false,
'message' => 'パラメーターチェック中にエラーが発生しました: ' . $e->getMessage()
];
}
}
/**
* 【処理2】メール送信テンプレート情報を取得する
*
* 仕様書に基づくSQLクエリ:
* SELECT エリアマネージャー同報, bccアドレス, 件名, 本文
* FROM メール送信テンプレート
* WHERE 使用プログラムID = 入力パラメーター使用プログラムID
* AND 使用フラグ = 1
*
* @param int $mailTemplateId メールテンプレートID使用プログラムIDとして扱う
* @return MailTemplate|null メールテンプレート情報
*/
private function getMailTemplateInfo(int $mailTemplateId): ?MailTemplate
{
try {
// 仕様書に記載されたSQLクエリに基づくメールテンプレート情報取得
// 注意: 仕様書では「使用プログラムID」を条件にしているが、
// 入力パラメーターは「メールテンプレートID」なので、pg_idで検索
$templateInfo = $this->mailTemplateModel::where('pg_id', $mailTemplateId)
->where('use_flag', 1)
->first();
if ($templateInfo) {
Log::info('メールテンプレート情報取得完了', [
'mail_template_id' => $mailTemplateId,
'template_found' => true,
'subject' => $templateInfo->getSubject()
]);
} else {
Log::warning('メールテンプレート情報取得結果', [
'mail_template_id' => $mailTemplateId,
'template_found' => false,
'message' => 'テンプレートが見つかりません'
]);
}
return $templateInfo;
} catch (\Exception $e) {
Log::error('メールテンプレート情報取得エラー', [
'mail_template_id' => $mailTemplateId,
'error' => $e->getMessage()
]);
throw $e;
}
}
/**
* 【処理3】メールを送信する
*
* 仕様書に基づくmb_send_mail関数使用:
* - 送信者: 処理2で取得したメールアドレス処理1で予備メールアドレス
* - タイトル: 処理2で取得したタイトル
* - 本文: 処理2で取得した本文※現在の文字列は「So-Manager一般的なWebサイト内部処理」参照
* - 追加ヘッダ: 処理2で取得したbccアドレス※値が設定されている場合のみ
*
* @param string $mailAddress メールアドレス
* @param string $backupMailAddress 予備メールアドレス
* @param MailTemplate $templateInfo テンプレート情報
* @return array 送信結果
*/
private function sendMail(string $mailAddress, string $backupMailAddress, MailTemplate $templateInfo): array
{
try {
// 送信先アドレス決定(優先: メールアドレス、代替: 予備メールアドレス)
$toAddress = !empty($mailAddress) ? $mailAddress : $backupMailAddress;
// メール内容取得
$subject = $templateInfo->getSubject() ?? '';
$message = $templateInfo->getText() ?? '';
// 追加ヘッダ設定
$headers = $this->buildMailHeaders($templateInfo);
Log::info('メール送信準備完了', [
'to_address' => $toAddress,
'subject' => $subject,
'has_bcc' => !empty($templateInfo->getBccAddress()),
'manager_cc_enabled' => $templateInfo->isManagerCcEnabled()
]);
// mb_send_mail関数を使用してメール送信
$sendResult = mb_send_mail(
$toAddress, // 送信先
$subject, // 件名
$message, // 本文
$headers // 追加ヘッダ
);
if ($sendResult) {
Log::info('メール送信成功', [
'to_address' => $toAddress,
'subject' => $subject
]);
return [
'success' => true,
'message' => 'メール送信が正常に完了しました',
'to_address' => $toAddress,
'subject' => $subject
];
} else {
$errorMessage = 'mb_send_mail関数でメール送信に失敗しました';
Log::error('メール送信失敗', [
'to_address' => $toAddress,
'subject' => $subject,
'error' => $errorMessage
]);
return [
'success' => false,
'message' => $errorMessage
];
}
} catch (\Exception $e) {
$errorMessage = 'メール送信中にエラーが発生: ' . $e->getMessage();
Log::error('メール送信エラー', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return [
'success' => false,
'message' => $errorMessage
];
}
}
/**
* メールヘッダを構築
*
* 仕様書に基づく設定:
* - BCCアドレス: 値が設定されている場合のみ追加
* - エリアマネージャー同報: フラグが有効な場合の処理
*
* @param MailTemplate $templateInfo テンプレート情報
* @return string メールヘッダ
*/
private function buildMailHeaders(MailTemplate $templateInfo): string
{
$headers = [];
// BCCアドレス設定値が設定されている場合のみ
$bccAddress = $templateInfo->getBccAddress();
if (!empty($bccAddress)) {
$headers[] = "Bcc: {$bccAddress}";
}
// エリアマネージャー同報フラグが有効な場合
// ※具体的な処理内容は仕様書に詳細がないため、ログ出力のみ
if ($templateInfo->isManagerCcEnabled()) {
Log::info('エリアマネージャー同報フラグが有効です', [
'mail_template_id' => $templateInfo->mail_template_id
]);
// 実際のエリアマネージャーアドレス取得・設定処理は追加仕様が必要
}
// From設定システム設定から取得
$fromAddress = config('mail.from.address', 'noreply@so-manager.com');
$headers[] = "From: {$fromAddress}";
// Content-Type設定日本語対応
$headers[] = "Content-Type: text/plain; charset=UTF-8";
return implode("\r\n", $headers);
}
/**
* バッチログ作成
*
* @param string $processName プロセス名
* @param string $status ステータス
* @param array $params パラメータ
* @return int バッチログID
*/
private function createBatchLog(string $processName, string $status, array $params = []): int
{
return $this->batchLogModel->create([
'process_name' => $processName,
'status' => $status,
'start_time' => now(),
'parameters' => json_encode($params),
'message' => 'バッチ処理開始'
])->id;
}
/**
* バッチログ更新
*
* @param int $batchLogId バッチログID
* @param string $status ステータス
* @param string $message メッセージ
* @return void
*/
private function updateBatchLog(int $batchLogId, string $status, string $message): void
{
$this->batchLogModel->where('id', $batchLogId)->update([
'status' => $status,
'end_time' => now(),
'message' => $message
]);
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\Services;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
/**
* 利用者取得サービス
* - 旧テーブル互換のまま、画面用の取得口を一本化する
*/
class UserService
{
/**
* ユーザーSEQで1件取得旧互換
*/
public function findBySeq(int $userSeq): ?object
{
return \App\Legacy\User::find($userSeq);
}
/**
* 画面検索条件でページング旧User::searchの最小互換
*/
public function paginate(array $inputs): LengthAwarePaginator
{
$query = \App\Legacy\User::query();
if (!empty($inputs['user_id'])) {
$query->where('user_id', 'like', '%'.$inputs['user_id'].'%');
}
if (!empty($inputs['member_id'])) {
$query->where('member_id', 'like', '%'.$inputs['member_id'].'%');
}
if (!empty($inputs['user_tag_serial'])) {
$query->where('user_tag_serial', 'like', '%'.$inputs['user_tag_serial'].'%');
}
if (!empty($inputs['user_phonetic'])) {
$query->where('user_phonetic', 'like', '%'.$inputs['user_phonetic'].'%');
}
if (!empty($inputs['phone'])) {
$phone = $inputs['phone'];
$query->where(function ($q) use ($phone) {
$q->where('user_mobile', 'like', '%'.$phone.'%')
->orWhere('user_homephone', 'like', '%'.$phone.'%');
});
}
$sort = $inputs['sort'] ?? 'user_seq';
$dir = $inputs['sort_type'] ?? 'desc';
return $query->orderBy($sort, $dir)->paginate(\App\Utils::item_per_page);
}
}

View File

@ -0,0 +1,103 @@
<?php
namespace App\Services;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Collection;
use App\Models\Park;
/**
* 利用率状況サービス
* - UsingStatusHelper の機能をサービスとして集約
* - 画面やAPIから本サービスを経由して取得する
*/
class UsingStatusService
{
/**
* 駐輪場別利用率統計を取得
*
* @param int|null $parkId 駐輪場IDnull の場合は全て)
* @return Collection 統計データのコレクション
*/
public function getUtilizationStats(?int $parkId = null): Collection
{
// 暫定的な収容台数実DBに無いカラムのため
$defaultCapacity = [
1 => 100, // 自転車
2 => 50, // 原付
3 => 30, // その他
];
$query = DB::table('park as p')
->leftJoin('price_a as pr', 'p.park_id', '=', 'pr.park_id')
->leftJoin('ptype as pt', 'pr.price_ptypeid', '=', 'pt.ptype_id')
->leftJoin('regular_contract as rc', function ($join) {
$join->on('pr.price_parkplaceid', '=', 'rc.price_parkplaceid')
->where('rc.contract_cancel_flag', '=', 0);
})
->select([
'p.park_id',
'p.park_name',
'pt.ptype_id',
'pt.ptype_subject',
DB::raw('COUNT(rc.contract_id) as current_count'),
])
->whereNotNull('pr.price_parkplaceid')
->whereNotNull('pt.ptype_id')
->where('p.park_close_flag', '!=', 1);
if ($parkId) {
$query->where('p.park_id', $parkId);
}
$results = $query
->groupBy(['p.park_id', 'p.park_name', 'pt.ptype_id', 'pt.ptype_subject'])
->orderBy('p.park_name')
->orderBy('pt.ptype_subject')
->get();
// 後計算で容量・空き・利用率を付与
foreach ($results as $result) {
$capacity = $defaultCapacity[$result->ptype_id] ?? 50;
$result->park_limit = $capacity;
$result->available = $capacity - $result->current_count;
$result->usage_rate = $capacity > 0 ? round(($result->current_count / $capacity * 100), 1) : 0;
}
return $results;
}
/**
* 駐輪場一覧を取得(選択用)
*/
public function getParkList(): Collection
{
return Park::select('park_id', 'park_name')
->where('park_close_flag', '!=', 1)
->orderBy('park_name')
->get();
}
/**
* 合計行の計算
*
* @param Collection $stats
* @return array{total_limit:int,total_current:int,total_available:int,total_usage_rate:float}
*/
public function calculateTotals(Collection $stats): array
{
$totalLimit = (int) $stats->sum('park_limit');
$totalCurrent = (int) $stats->sum('current_count');
$totalAvailable = $totalLimit - $totalCurrent;
$totalUsageRate = $totalLimit > 0 ? round(($totalCurrent / $totalLimit * 100), 1) : 0.0;
return [
'total_limit' => $totalLimit,
'total_current' => $totalCurrent,
'total_available' => $totalAvailable,
'total_usage_rate' => $totalUsageRate,
];
}
}

28
app/Support/Csv.php Normal file
View File

@ -0,0 +1,28 @@
<?php
namespace App\Support;
/**
* 純粋なCSVユーティリティ
* - ビジネスロジックは含めない
*/
class Csv
{
/**
* 文字列配列をCSV行に変換
* 備考: 囲い/エスケープは fputcsv に委譲するのが安全だが、ここでは簡易版を提供
*/
public static function join(array $fields, string $delimiter = ','): string
{
$escaped = array_map(static function ($v) use ($delimiter) {
$s = (string) $v;
if (str_contains($s, '"') || str_contains($s, $delimiter) || str_contains($s, "\n")) {
$s = '"'.str_replace('"', '""', $s).'"';
}
return $s;
}, $fields);
return implode($delimiter, $escaped);
}
}

30
app/Support/Files.php Normal file
View File

@ -0,0 +1,30 @@
<?php
namespace App\Support;
use Illuminate\Support\Facades\Storage;
/**
* ファイルユーティリティ
*/
class Files
{
/**
* 一時ファイルパス生成
*/
public static function tempPath(string $prefix = 'sm_', string $suffix = '.tmp'): string
{
$dir = sys_get_temp_dir();
return $dir.DIRECTORY_SEPARATOR.$prefix.uniqid('', true).$suffix;
}
/**
* ストレージに保存(簡易)
*/
public static function putPublic(string $path, string $contents): bool
{
return Storage::disk('public')->put($path, $contents);
}
}

134
app/Utils.php Normal file
View File

@ -0,0 +1,134 @@
<?php
/**
* ユーティリティクラス
* Laravel 5.7から引き継いだヘルパー機能
* Created by PhpStorm.
* User: mrpha
* Date: 5/31/2016
* Time: 1:49 PM
*/
namespace App;
use File;
use Illuminate\Support\Facades\Hash;
/**
* 共通ユーティリティクラス
* ファイルアップロード、パスワード暗号化、CSV処理など
*/
class Utils
{
// ページあたりのアイテム数(旧システムから継承)
const item_per_page = 50;
// 画像保存パス(旧システムから継承)
const image_path = 'storage/images/';
/**
* 画像ファイルをimagesフォルダにアップロード
* Laravel 5.7から継承したメソッド
*
* @param \Illuminate\Http\UploadedFile $file アップロードファイル
* @param string|null $fileName ファイル名(指定しない場合は自動生成)
* @return string|false アップロード成功時はファイル名、失敗時はfalse
*/
public static function uploadFile($file, $fileName = null)
{
$destinationPath = self::getImagePath();
$date = strtotime(date('Y-m-d H:i:s'));
// ファイルをフォルダに移動
$fileName = $fileName . $date . '.' . $file->getClientOriginalExtension();
if ($file->move($destinationPath, $fileName)) {
$filePath = $destinationPath.$fileName;
if (file_exists($filePath)){
chmod($filePath, 0755); // ファイル権限設定
}
return $fileName;
}
return false;
}
/**
* 画像パスを取得
*
* @param string $filname ファイル名
* @return string 画像パス
*/
public static function getImagePath($filname = '')
{
$path = self::image_path;
if ($filname) $path .= $filname;
return $path;
}
/**
* 画像URLを取得
*
* @param string $filname ファイル名
* @return string 画像URL
*/
public static function getImageUrl($filname = '')
{
return url(self::getImagePath($filname));
}
/**
* パスワードのハッシュ化
* Laravel 12変更点Hashファサードを使用してハッシュ化
* Laravel 5.7: 独自のソルト処理を実装していた
*
* @param string $pw 平文パスワード
* @param string $seq ユーザーシーケンス(ソルト用)
* @return string ハッシュ化されたパスワード
*/
public static function getHashPassword($pw, $seq)
{
// 旧システムと同じソルト形式を維持
$salt = $seq.'SOMSALT';
// Laravel 12: Argon2ハッシュアルゴリズムを使用
return Hash::make($pw, [
'memory' => 1024,
'time' => 25,
'threads' => 2,
'salt' => $salt
]);
}
/**
* CSVファイルを配列に変換
* Laravel 5.7から継承したメソッド(データインポート用)
*
* @param string $filename CSVファイルパス
* @param string $delimiter 区切り文字(デフォルト:カンマ)
* @return array|false 変換された配列、またはfalse失敗時
*/
public static function csvToArray($filename = '', $delimiter = ',')
{
// ファイル存在確認
if (!file_exists($filename) || !is_readable($filename))
return false;
$header = null;
$data = array();
// CSVファイルを読み込み
if (($handle = fopen($filename, 'r')) !== false)
{
while (($row = fgetcsv($handle, 1000, $delimiter)) !== false)
{
if (!$header)
$header = $row; // 最初の行はヘッダーとして扱う
else
$data[] = $row; // データ行として配列に追加
}
fclose($handle);
}
return $data;
}
}

18
artisan Normal file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env php
<?php
use Illuminate\Foundation\Application;
use Symfony\Component\Console\Input\ArgvInput;
define('LARAVEL_START', microtime(true));
// Register the Composer autoloader...
require __DIR__.'/vendor/autoload.php';
// Bootstrap Laravel and handle the command...
/** @var Application $app */
$app = require_once __DIR__.'/bootstrap/app.php';
$status = $app->handleCommand(new ArgvInput);
exit($status);

18
bootstrap/app.php Normal file
View File

@ -0,0 +1,18 @@
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
//
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();

2
bootstrap/cache/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

6
bootstrap/providers.php Normal file
View File

@ -0,0 +1,6 @@
<?php
return [
App\Providers\AppServiceProvider::class,
App\Providers\LegacyServiceProvider::class,
];

74
composer.json Normal file
View File

@ -0,0 +1,74 @@
{
"$schema": "https://getcomposer.org/schema.json",
"name": "laravel/laravel",
"type": "project",
"description": "The skeleton application for the Laravel framework.",
"keywords": ["laravel", "framework"],
"license": "MIT",
"require": {
"php": "^8.2",
"laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1"
},
"require-dev": {
"fakerphp/faker": "^1.23",
"laravel/pail": "^1.2.2",
"laravel/pint": "^1.13",
"laravel/sail": "^1.41",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpunit/phpunit": "^11.5.3"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi",
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
"@php artisan migrate --graceful --ansi"
],
"dev": [
"Composer\\Config::disableProcessTimeout",
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
]
},
"extra": {
"branch-alias": {
"dev-master": "12.x-dev"
},
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true
}
},
"minimum-stability": "dev",
"prefer-stable": true
}

8105
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

126
config/app.php Normal file
View File

@ -0,0 +1,126 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Application Name
|--------------------------------------------------------------------------
|
| This value is the name of your application, which will be used when the
| framework needs to place the application's name in a notification or
| other UI elements where an application name needs to be displayed.
|
*/
'name' => env('APP_NAME', 'Laravel'),
/*
|--------------------------------------------------------------------------
| Application Environment
|--------------------------------------------------------------------------
|
| This value determines the "environment" your application is currently
| running in. This may determine how you prefer to configure various
| services the application utilizes. Set this in your ".env" file.
|
*/
'env' => env('APP_ENV', 'production'),
/*
|--------------------------------------------------------------------------
| Application Debug Mode
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/
'debug' => (bool) env('APP_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Application URL
|--------------------------------------------------------------------------
|
| This URL is used by the console to properly generate URLs when using
| the Artisan command line tool. You should set this to the root of
| the application so that it's available within Artisan commands.
|
*/
'url' => env('APP_URL', 'http://localhost'),
/*
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. The timezone
| is set to "UTC" by default as it is suitable for most use cases.
|
*/
'timezone' => 'UTC',
/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by Laravel's translation / localization methods. This option can be
| set to any locale for which you plan to have translation strings.
|
*/
'locale' => env('APP_LOCALE', 'en'),
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
/*
|--------------------------------------------------------------------------
| Encryption Key
|--------------------------------------------------------------------------
|
| This key is utilized by Laravel's encryption services and should be set
| to a random, 32 character string to ensure that all encrypted values
| are secure. You should do this prior to deploying the application.
|
*/
'cipher' => 'AES-256-CBC',
'key' => env('APP_KEY'),
'previous_keys' => [
...array_filter(
explode(',', env('APP_PREVIOUS_KEYS', ''))
),
],
/*
|--------------------------------------------------------------------------
| Maintenance Mode Driver
|--------------------------------------------------------------------------
|
| These configuration options determine the driver used to determine and
| manage Laravel's "maintenance mode" status. The "cache" driver will
| allow maintenance mode to be controlled across multiple machines.
|
| Supported drivers: "file", "cache"
|
*/
'maintenance' => [
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
'store' => env('APP_MAINTENANCE_STORE', 'database'),
],
];

116
config/auth.php Normal file
View File

@ -0,0 +1,116 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option defines the default authentication "guard" and password
| reset "broker" for your application. You may change these values
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => env('AUTH_GUARD', 'web'),
'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| which utilizes session storage plus the Eloquent user provider.
|
| All authentication guards have a user provider, which defines how the
| users are actually retrieved out of your database or other storage
| system used by the application. Typically, Eloquent is utilized.
|
| Supported: "session"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication guards have a user provider, which defines how the
| users are actually retrieved out of your database or other storage
| system used by the application. Typically, Eloquent is utilized.
|
| If you have multiple user tables or models you may configure multiple
| providers to represent the model / table. These providers may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
// Laravel 12変更点標準のUserモデルからOpeモデルに変更旧システムとの互換性維持
'model' => App\Models\Ope::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| These configuration options specify the behavior of Laravel's password
| reset functionality, including the table utilized for token storage
| and the user provider that is invoked to actually retrieve users.
|
| The expiry time is the number of minutes that each reset token will be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
| The throttle setting is the number of seconds a user must wait before
| generating more password reset tokens. This prevents the user from
| quickly generating a very large amount of password reset tokens.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
'expire' => 60,
'throttle' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| window expires and users are asked to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
];

108
config/cache.php Normal file
View File

@ -0,0 +1,108 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Cache Store
|--------------------------------------------------------------------------
|
| This option controls the default cache store that will be used by the
| framework. This connection is utilized if another isn't explicitly
| specified when running a cache operation inside the application.
|
*/
'default' => env('CACHE_STORE', 'database'),
/*
|--------------------------------------------------------------------------
| Cache Stores
|--------------------------------------------------------------------------
|
| Here you may define all of the cache "stores" for your application as
| well as their drivers. You may even define multiple stores for the
| same cache driver to group types of items stored in your caches.
|
| Supported drivers: "array", "database", "file", "memcached",
| "redis", "dynamodb", "octane", "null"
|
*/
'stores' => [
'array' => [
'driver' => 'array',
'serialize' => false,
],
'database' => [
'driver' => 'database',
'connection' => env('DB_CACHE_CONNECTION'),
'table' => env('DB_CACHE_TABLE', 'cache'),
'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
'lock_table' => env('DB_CACHE_LOCK_TABLE'),
],
'file' => [
'driver' => 'file',
'path' => storage_path('framework/cache/data'),
'lock_path' => storage_path('framework/cache/data'),
],
'memcached' => [
'driver' => 'memcached',
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
'sasl' => [
env('MEMCACHED_USERNAME'),
env('MEMCACHED_PASSWORD'),
],
'options' => [
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
],
'servers' => [
[
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
'port' => env('MEMCACHED_PORT', 11211),
'weight' => 100,
],
],
],
'redis' => [
'driver' => 'redis',
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
],
'dynamodb' => [
'driver' => 'dynamodb',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
'endpoint' => env('DYNAMODB_ENDPOINT'),
],
'octane' => [
'driver' => 'octane',
],
],
/*
|--------------------------------------------------------------------------
| Cache Key Prefix
|--------------------------------------------------------------------------
|
| When utilizing the APC, database, memcached, Redis, and DynamoDB cache
| stores, there might be other applications using the same cache. For
| that reason, you may prefix every cache key to avoid collisions.
|
*/
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
];

174
config/database.php Normal file
View File

@ -0,0 +1,174 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Database Connection Name
|--------------------------------------------------------------------------
|
| Here you may specify which of the database connections below you wish
| to use as your default connection for database operations. This is
| the connection which will be utilized unless another connection
| is explicitly specified when you execute a query / statement.
|
*/
'default' => env('DB_CONNECTION', 'sqlite'),
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Below are all of the database connections defined for your application.
| An example configuration is provided for each database system which
| is supported by Laravel. You're free to add / remove connections.
|
*/
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DB_URL'),
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
'busy_timeout' => null,
'journal_mode' => null,
'synchronous' => null,
],
'mysql' => [
'driver' => 'mysql',
'url' => env('DB_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'mariadb' => [
'driver' => 'mariadb',
'url' => env('DB_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'pgsql' => [
'driver' => 'pgsql',
'url' => env('DB_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8'),
'prefix' => '',
'prefix_indexes' => true,
'search_path' => 'public',
'sslmode' => 'prefer',
],
'sqlsrv' => [
'driver' => 'sqlsrv',
'url' => env('DB_URL'),
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '1433'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8'),
'prefix' => '',
'prefix_indexes' => true,
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
],
],
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| This table keeps track of all the migrations that have already run for
| your application. Using this information, we can determine which of
| the migrations on disk haven't actually been run on the database.
|
*/
'migrations' => [
'table' => 'migrations',
'update_date_on_publish' => true,
],
/*
|--------------------------------------------------------------------------
| Redis Databases
|--------------------------------------------------------------------------
|
| Redis is an open source, fast, and advanced key-value store that also
| provides a richer body of commands than a typical key-value system
| such as Memcached. You may define your connection settings here.
|
*/
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
'persistent' => env('REDIS_PERSISTENT', false),
],
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
],
'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_CACHE_DB', '1'),
],
],
];

80
config/filesystems.php Normal file
View File

@ -0,0 +1,80 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Filesystem Disk
|--------------------------------------------------------------------------
|
| Here you may specify the default filesystem disk that should be used
| by the framework. The "local" disk, as well as a variety of cloud
| based disks are available to your application for file storage.
|
*/
'default' => env('FILESYSTEM_DISK', 'local'),
/*
|--------------------------------------------------------------------------
| Filesystem Disks
|--------------------------------------------------------------------------
|
| Below you may configure as many filesystem disks as necessary, and you
| may even configure multiple disks for the same driver. Examples for
| most supported storage drivers are configured here for reference.
|
| Supported drivers: "local", "ftp", "sftp", "s3"
|
*/
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app/private'),
'serve' => true,
'throw' => false,
'report' => false,
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
'throw' => false,
'report' => false,
],
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'throw' => false,
'report' => false,
],
],
/*
|--------------------------------------------------------------------------
| Symbolic Links
|--------------------------------------------------------------------------
|
| Here you may configure the symbolic links that will be created when the
| `storage:link` Artisan command is executed. The array keys should be
| the locations of the links and the values should be their targets.
|
*/
'links' => [
public_path('storage') => storage_path('app/public'),
],
];

132
config/logging.php Normal file
View File

@ -0,0 +1,132 @@
<?php
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
use Monolog\Processor\PsrLogMessageProcessor;
return [
/*
|--------------------------------------------------------------------------
| Default Log Channel
|--------------------------------------------------------------------------
|
| This option defines the default log channel that is utilized to write
| messages to your logs. The value provided here should match one of
| the channels present in the list of "channels" configured below.
|
*/
'default' => env('LOG_CHANNEL', 'stack'),
/*
|--------------------------------------------------------------------------
| Deprecations Log Channel
|--------------------------------------------------------------------------
|
| This option controls the log channel that should be used to log warnings
| regarding deprecated PHP and library features. This allows you to get
| your application ready for upcoming major versions of dependencies.
|
*/
'deprecations' => [
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
'trace' => env('LOG_DEPRECATIONS_TRACE', false),
],
/*
|--------------------------------------------------------------------------
| Log Channels
|--------------------------------------------------------------------------
|
| Here you may configure the log channels for your application. Laravel
| utilizes the Monolog PHP logging library, which includes a variety
| of powerful log handlers and formatters that you're free to use.
|
| Available drivers: "single", "daily", "slack", "syslog",
| "errorlog", "monolog", "custom", "stack"
|
*/
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => explode(',', env('LOG_STACK', 'single')),
'ignore_exceptions' => false,
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'replace_placeholders' => true,
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'days' => env('LOG_DAILY_DAYS', 14),
'replace_placeholders' => true,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
'level' => env('LOG_LEVEL', 'critical'),
'replace_placeholders' => true,
],
'papertrail' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
'handler_with' => [
'host' => env('PAPERTRAIL_URL'),
'port' => env('PAPERTRAIL_PORT'),
'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
],
'processors' => [PsrLogMessageProcessor::class],
],
'stderr' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => StreamHandler::class,
'formatter' => env('LOG_STDERR_FORMATTER'),
'with' => [
'stream' => 'php://stderr',
],
'processors' => [PsrLogMessageProcessor::class],
],
'syslog' => [
'driver' => 'syslog',
'level' => env('LOG_LEVEL', 'debug'),
'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
'replace_placeholders' => true,
],
'errorlog' => [
'driver' => 'errorlog',
'level' => env('LOG_LEVEL', 'debug'),
'replace_placeholders' => true,
],
'null' => [
'driver' => 'monolog',
'handler' => NullHandler::class,
],
'emergency' => [
'path' => storage_path('logs/laravel.log'),
],
],
];

116
config/mail.php Normal file
View File

@ -0,0 +1,116 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Mailer
|--------------------------------------------------------------------------
|
| This option controls the default mailer that is used to send all email
| messages unless another mailer is explicitly specified when sending
| the message. All additional mailers can be configured within the
| "mailers" array. Examples of each type of mailer are provided.
|
*/
'default' => env('MAIL_MAILER', 'log'),
/*
|--------------------------------------------------------------------------
| Mailer Configurations
|--------------------------------------------------------------------------
|
| Here you may configure all of the mailers used by your application plus
| their respective settings. Several examples have been configured for
| you and you are free to add your own as your application requires.
|
| Laravel supports a variety of mail "transport" drivers that can be used
| when delivering an email. You may specify which one you're using for
| your mailers below. You may also add additional mailers if needed.
|
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
| "postmark", "resend", "log", "array",
| "failover", "roundrobin"
|
*/
'mailers' => [
'smtp' => [
'transport' => 'smtp',
'scheme' => env('MAIL_SCHEME'),
'url' => env('MAIL_URL'),
'host' => env('MAIL_HOST', '127.0.0.1'),
'port' => env('MAIL_PORT', 2525),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url(env('APP_URL', 'http://localhost'), PHP_URL_HOST)),
],
'ses' => [
'transport' => 'ses',
],
'postmark' => [
'transport' => 'postmark',
// 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
// 'client' => [
// 'timeout' => 5,
// ],
],
'resend' => [
'transport' => 'resend',
],
'sendmail' => [
'transport' => 'sendmail',
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
],
'log' => [
'transport' => 'log',
'channel' => env('MAIL_LOG_CHANNEL'),
],
'array' => [
'transport' => 'array',
],
'failover' => [
'transport' => 'failover',
'mailers' => [
'smtp',
'log',
],
],
'roundrobin' => [
'transport' => 'roundrobin',
'mailers' => [
'ses',
'postmark',
],
],
],
/*
|--------------------------------------------------------------------------
| Global "From" Address
|--------------------------------------------------------------------------
|
| You may wish for all emails sent by your application to be sent from
| the same address. Here you may specify a name and address that is
| used globally for all emails that are sent by your application.
|
*/
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
];

112
config/queue.php Normal file
View File

@ -0,0 +1,112 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Queue Connection Name
|--------------------------------------------------------------------------
|
| Laravel's queue supports a variety of backends via a single, unified
| API, giving you convenient access to each backend using identical
| syntax for each. The default queue connection is defined below.
|
*/
'default' => env('QUEUE_CONNECTION', 'database'),
/*
|--------------------------------------------------------------------------
| Queue Connections
|--------------------------------------------------------------------------
|
| Here you may configure the connection options for every queue backend
| used by your application. An example configuration is provided for
| each backend supported by Laravel. You're also free to add more.
|
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
|
*/
'connections' => [
'sync' => [
'driver' => 'sync',
],
'database' => [
'driver' => 'database',
'connection' => env('DB_QUEUE_CONNECTION'),
'table' => env('DB_QUEUE_TABLE', 'jobs'),
'queue' => env('DB_QUEUE', 'default'),
'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
'after_commit' => false,
],
'beanstalkd' => [
'driver' => 'beanstalkd',
'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),
'queue' => env('BEANSTALKD_QUEUE', 'default'),
'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),
'block_for' => 0,
'after_commit' => false,
],
'sqs' => [
'driver' => 'sqs',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
'queue' => env('SQS_QUEUE', 'default'),
'suffix' => env('SQS_SUFFIX'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'after_commit' => false,
],
'redis' => [
'driver' => 'redis',
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
'block_for' => null,
'after_commit' => false,
],
],
/*
|--------------------------------------------------------------------------
| Job Batching
|--------------------------------------------------------------------------
|
| The following options configure the database and table that store job
| batching information. These options can be updated to any database
| connection and table which has been defined by your application.
|
*/
'batching' => [
'database' => env('DB_CONNECTION', 'sqlite'),
'table' => 'job_batches',
],
/*
|--------------------------------------------------------------------------
| Failed Queue Jobs
|--------------------------------------------------------------------------
|
| These options configure the behavior of failed queue job logging so you
| can control how and where failed jobs are stored. Laravel ships with
| support for storing failed jobs in a simple file or in a database.
|
| Supported drivers: "database-uuids", "dynamodb", "file", "null"
|
*/
'failed' => [
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
'database' => env('DB_CONNECTION', 'sqlite'),
'table' => 'failed_jobs',
],
];

38
config/services.php Normal file
View File

@ -0,0 +1,38 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Third Party Services
|--------------------------------------------------------------------------
|
| This file is for storing the credentials for third party services such
| as Mailgun, Postmark, AWS and more. This file provides the de facto
| location for this type of information, allowing packages to have
| a conventional file to locate the various service credentials.
|
*/
'postmark' => [
'token' => env('POSTMARK_TOKEN'),
],
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
'resend' => [
'key' => env('RESEND_KEY'),
],
'slack' => [
'notifications' => [
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
],
],
];

217
config/session.php Normal file
View File

@ -0,0 +1,217 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Session Driver
|--------------------------------------------------------------------------
|
| This option determines the default session driver that is utilized for
| incoming requests. Laravel supports a variety of storage options to
| persist session data. Database storage is a great default choice.
|
| Supported: "file", "cookie", "database", "apc",
| "memcached", "redis", "dynamodb", "array"
|
*/
'driver' => env('SESSION_DRIVER', 'database'),
/*
|--------------------------------------------------------------------------
| Session Lifetime
|--------------------------------------------------------------------------
|
| Here you may specify the number of minutes that you wish the session
| to be allowed to remain idle before it expires. If you want them
| to expire immediately when the browser is closed then you may
| indicate that via the expire_on_close configuration option.
|
*/
'lifetime' => (int) env('SESSION_LIFETIME', 120),
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
/*
|--------------------------------------------------------------------------
| Session Encryption
|--------------------------------------------------------------------------
|
| This option allows you to easily specify that all of your session data
| should be encrypted before it's stored. All encryption is performed
| automatically by Laravel and you may use the session like normal.
|
*/
'encrypt' => env('SESSION_ENCRYPT', false),
/*
|--------------------------------------------------------------------------
| Session File Location
|--------------------------------------------------------------------------
|
| When utilizing the "file" session driver, the session files are placed
| on disk. The default storage location is defined here; however, you
| are free to provide another location where they should be stored.
|
*/
'files' => storage_path('framework/sessions'),
/*
|--------------------------------------------------------------------------
| Session Database Connection
|--------------------------------------------------------------------------
|
| When using the "database" or "redis" session drivers, you may specify a
| connection that should be used to manage these sessions. This should
| correspond to a connection in your database configuration options.
|
*/
'connection' => env('SESSION_CONNECTION'),
/*
|--------------------------------------------------------------------------
| Session Database Table
|--------------------------------------------------------------------------
|
| When using the "database" session driver, you may specify the table to
| be used to store sessions. Of course, a sensible default is defined
| for you; however, you're welcome to change this to another table.
|
*/
'table' => env('SESSION_TABLE', 'sessions'),
/*
|--------------------------------------------------------------------------
| Session Cache Store
|--------------------------------------------------------------------------
|
| When using one of the framework's cache driven session backends, you may
| define the cache store which should be used to store the session data
| between requests. This must match one of your defined cache stores.
|
| Affects: "apc", "dynamodb", "memcached", "redis"
|
*/
'store' => env('SESSION_STORE'),
/*
|--------------------------------------------------------------------------
| Session Sweeping Lottery
|--------------------------------------------------------------------------
|
| Some session drivers must manually sweep their storage location to get
| rid of old sessions from storage. Here are the chances that it will
| happen on a given request. By default, the odds are 2 out of 100.
|
*/
'lottery' => [2, 100],
/*
|--------------------------------------------------------------------------
| Session Cookie Name
|--------------------------------------------------------------------------
|
| Here you may change the name of the session cookie that is created by
| the framework. Typically, you should not need to change this value
| since doing so does not grant a meaningful security improvement.
|
*/
'cookie' => env(
'SESSION_COOKIE',
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
),
/*
|--------------------------------------------------------------------------
| Session Cookie Path
|--------------------------------------------------------------------------
|
| The session cookie path determines the path for which the cookie will
| be regarded as available. Typically, this will be the root path of
| your application, but you're free to change this when necessary.
|
*/
'path' => env('SESSION_PATH', '/'),
/*
|--------------------------------------------------------------------------
| Session Cookie Domain
|--------------------------------------------------------------------------
|
| This value determines the domain and subdomains the session cookie is
| available to. By default, the cookie will be available to the root
| domain and all subdomains. Typically, this shouldn't be changed.
|
*/
'domain' => env('SESSION_DOMAIN'),
/*
|--------------------------------------------------------------------------
| HTTPS Only Cookies
|--------------------------------------------------------------------------
|
| By setting this option to true, session cookies will only be sent back
| to the server if the browser has a HTTPS connection. This will keep
| the cookie from being sent to you when it can't be done securely.
|
*/
'secure' => env('SESSION_SECURE_COOKIE'),
/*
|--------------------------------------------------------------------------
| HTTP Access Only
|--------------------------------------------------------------------------
|
| Setting this value to true will prevent JavaScript from accessing the
| value of the cookie and the cookie will only be accessible through
| the HTTP protocol. It's unlikely you should disable this option.
|
*/
'http_only' => env('SESSION_HTTP_ONLY', true),
/*
|--------------------------------------------------------------------------
| Same-Site Cookies
|--------------------------------------------------------------------------
|
| This option determines how your cookies behave when cross-site requests
| take place, and can be used to mitigate CSRF attacks. By default, we
| will set this value to "lax" to permit secure cross-site requests.
|
| See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
|
| Supported: "lax", "strict", "none", null
|
*/
'same_site' => env('SESSION_SAME_SITE', 'lax'),
/*
|--------------------------------------------------------------------------
| Partitioned Cookies
|--------------------------------------------------------------------------
|
| Setting this value to true will tie the cookie to the top-level site for
| a cross-site context. Partitioned cookies are accepted by the browser
| when flagged "secure" and the Same-Site attribute is set to "none".
|
*/
'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),
];

1
database/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.sqlite*

View File

@ -0,0 +1,44 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}

View File

@ -0,0 +1,49 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('sessions');
}
};

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->primary();
$table->mediumText('value');
$table->integer('expiration');
});
Schema::create('cache_locks', function (Blueprint $table) {
$table->string('key')->primary();
$table->string('owner');
$table->integer('expiration');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('cache');
Schema::dropIfExists('cache_locks');
}
};

View File

@ -0,0 +1,57 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('jobs', function (Blueprint $table) {
$table->id();
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
Schema::create('job_batches', function (Blueprint $table) {
$table->string('id')->primary();
$table->string('name');
$table->integer('total_jobs');
$table->integer('pending_jobs');
$table->integer('failed_jobs');
$table->longText('failed_job_ids');
$table->mediumText('options')->nullable();
$table->integer('cancelled_at')->nullable();
$table->integer('created_at');
$table->integer('finished_at')->nullable();
});
Schema::create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jobs');
Schema::dropIfExists('job_batches');
Schema::dropIfExists('failed_jobs');
}
};

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('sessions', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('sessions');
}
};

View File

@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* バッチログテーブルの作成
*
* SHJ-4C室割当処理を含む全てのバッチ処理のログを記録
*/
public function up(): void
{
Schema::create('batch_log', function (Blueprint $table) {
$table->id();
$table->string('process_name', 50)->comment('プロセス名');
$table->string('status', 20)->comment('ステータス');
$table->datetime('start_time')->comment('開始時刻');
$table->datetime('end_time')->nullable()->comment('終了時刻');
$table->json('parameters')->nullable()->comment('パラメータJSON形式');
$table->text('message')->nullable()->comment('メッセージ');
$table->text('error_details')->nullable()->comment('エラー詳細');
$table->integer('execution_count')->default(0)->comment('実行回数');
$table->integer('success_count')->default(0)->comment('成功回数');
$table->integer('error_count')->default(0)->comment('エラー回数');
$table->timestamps();
// インデックス作成
$table->index(['process_name', 'status']);
$table->index('start_time');
$table->index('status');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('batch_log');
}
};

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('zone', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('zone');
}
};

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('park', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('park');
}
};

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('regular_contract', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('regular_contract');
}
};

View File

@ -0,0 +1,23 @@
<?php
namespace Database\Seeders;
use App\Models\User;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
// User::factory(10)->create();
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
]);
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
/**
* メールテンプレートシーダー
* テスト用のメールテンプレートデータを作成
*/
class MailTemplateSeeder extends Seeder
{
/**
* シーダーを実行
*
* @return void
*/
public function run()
{
// テスト用メールテンプレートデータを挿入
DB::table('mail_template')->insert([
[
'mail_template_id' => 999,
'pg_id' => 1, // 使用プログラムIDテスト用
'internal_id' => 1,
'mgr_cc_flag' => 1, // エリアマネージャー同報有効
'bcc_adrs' => 'manager@so-manager.test.com',
'use_flag' => 1, // 使用フラグ有効
'memo' => 'テスト用メールテンプレート',
'subject' => 'So-Manager システム通知テスト',
'text' => "いつもSo-Managerをご利用いただき、ありがとうございます。\n\nこちらはシステムからの自動送信テストメールです。\n\n何かご不明な点がございましたら、サポートまでお問い合わせください。\n\nSo-Manager運営チーム",
'created_at' => now(),
'updated_at' => now(),
'operator_id' => 1
],
[
'mail_template_id' => 998,
'pg_id' => 2, // 使用プログラムIDテスト用
'internal_id' => 2,
'mgr_cc_flag' => 0, // エリアマネージャー同報無効
'bcc_adrs' => null,
'use_flag' => 1, // 使用フラグ有効
'memo' => 'シンプルなメールテンプレート',
'subject' => 'So-Manager お知らせ',
'text' => "お客様へ\n\nSo-Managerからのお知らせです。\n\nよろしくお願いいたします。",
'created_at' => now(),
'updated_at' => now(),
'operator_id' => 1
]
]);
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
/**
* SHJ-8バッチログテストシーダー
* テスト用のデバイスデータを作成
*/
class ShjBatchLogTestSeeder extends Seeder
{
/**
* シーダーを実行
*
* @return void
*/
public function run()
{
// テスト用デバイスデータを挿入
DB::table('device')->insert([
[
'device_id' => 9999,
'created_at' => now(),
'updated_at' => now(),
'park_id' => 1,
'device_type' => 'test_printer',
'device_subject' => 'テスト用プリンター',
'device_identifier' => 'TEST_PRINTER_001',
'device_work' => '印刷処理',
'device_workstart' => '2025-01-01',
'device_replace' => '2030-01-01',
'device_remarks' => 'SHJ-8テスト用デバイス',
'operator_id' => 1
]
]);
echo "SHJ-8テスト用デバイスID: 9999を作成しました。\n";
}
}

62
docs/TEAM_GUIDE_JA.md Normal file
View File

@ -0,0 +1,62 @@
# チーム開発ガイドSo-Manager / Laravel12
このドキュメントは、Laravel5 旧コードと Laravel12 新コードが混在する現状で、重複実装を避け、保守しやすい構成を保つための最小ルールを示します。
## ディレクトリと責務
- app/Models
- 新規機能/新規テーブル向けのEloquentモデル
- クラス名は単数・StudlyCase例: `AdminUser`)。ファイル名=クラス名。
- 複雑な業務ロジックは置かない(テーブル定義・リレーション・簡易スコープのみ)。
- 共通Traitは `app/Models/Concerns`、基底は `app/Models/BaseModel.php`
- app/Legacy
- 旧システムLaravel5の互換モデル群。テーブル名/主キー/既存メソッド・定数を維持。
- 旧Bladeから参照されるFQCN互換のため、`LegacyServiceProvider` で `class_alias` を設定。
- app/Services
- 画面用のユースケースを実装複数モデルのオーケストレーション、CSV、外部API、トランザクション等
- ControllerはServicesのみを呼び出し、モデルを直接組み合わせない。
- app/Support
- 純粋な技術ユーティリティ(ビジネスロジックを含めない)。
- app/Enums
- 共通定数はPHP列挙型で表現。旧配列定数から段階的に移行。
## 命名と重複回避
- モデルは1テーブル1モデル。`admin_user.php` のような snake_case は禁止。
- 新しい画面・機能で共通取得口が必要な場合、モデルを複製せず `app/Services/*Service.php` に集約。
## 旧→新の互換方針
- 旧Bladeは当面そのまま動かす`\App\OperatorQue::QueClass` 等の直参照を許容)。
- 新規Bladeではモデル直参照を避け、Service経由のDTO/配列を描画。
- 段階的に旧配列定数を `app/Enums` へ置換し、`class_alias` を撤去。
### Legacy を新規で使わない(明記)
- `app/Legacy` 配下は互換専用。新規機能・新規画面・新規サービスで `Legacy` を直接参照しないこと。
- モデルは必ず `app/Models` の正式モデルを使用し、Controller からは `app/Services` 経由で利用する。
## Middleware / Provider / Controller
- Middleware: 横断的関心認証・権限・CSRFなど
- Provider: 起動時の装備DI、イベント、互換エイリアス
- Controller: 薄く保ち、入力検証Service呼び出しのみ。
## トランザクション
- 複数テーブル更新は Service 層で `DB::transaction()` を使用。
## チェックリストPR提出時
- 新規モデル: テーブル/主キーを明記。既存モデルとの重複なし。
- 新規機能: 複雑な処理は Service に実装。Controllerは薄いか。
- Blade: 新規はモデル直参照なし(旧は許容)。
- ルート: 旧名互換を維持(置換時は動作確認)。
以上。段階的に Enum 化と互換撤去を進め、最終的に Legacy 依存を解消します。

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,61 @@
/* iCheck plugin skins
----------------------------------- */
@import url("minimal/_all.css");
/*
@import url("minimal/minimal.css");
@import url("minimal/red.css");
@import url("minimal/green.css");
@import url("minimal/blue.css");
@import url("minimal/aero.css");
@import url("minimal/grey.css");
@import url("minimal/orange.css");
@import url("minimal/yellow.css");
@import url("minimal/pink.css");
@import url("minimal/purple.css");
*/
@import url("square/_all.css");
/*
@import url("square/square.css");
@import url("square/red.css");
@import url("square/green.css");
@import url("square/blue.css");
@import url("square/aero.css");
@import url("square/grey.css");
@import url("square/orange.css");
@import url("square/yellow.css");
@import url("square/pink.css");
@import url("square/purple.css");
*/
@import url("flat/_all.css");
/*
@import url("flat/flat.css");
@import url("flat/red.css");
@import url("flat/green.css");
@import url("flat/blue.css");
@import url("flat/aero.css");
@import url("flat/grey.css");
@import url("flat/orange.css");
@import url("flat/yellow.css");
@import url("flat/pink.css");
@import url("flat/purple.css");
*/
@import url("line/_all.css");
/*
@import url("line/line.css");
@import url("line/red.css");
@import url("line/green.css");
@import url("line/blue.css");
@import url("line/aero.css");
@import url("line/grey.css");
@import url("line/orange.css");
@import url("line/yellow.css");
@import url("line/pink.css");
@import url("line/purple.css");
*/
@import url("polaris/polaris.css");
@import url("futurico/futurico.css");

View File

@ -0,0 +1,9 @@
/*!
* Bootstrap Colorpicker
* http://mjolnic.github.io/bootstrap-colorpicker/
*
* Originally written by (c) 2012 Stefan Petre
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0.txt
*
*/.colorpicker-saturation{float:left;width:100px;height:100px;cursor:crosshair;background-image:url("img/saturation.png")}.colorpicker-saturation i{position:absolute;top:0;left:0;display:block;width:5px;height:5px;margin:-4px 0 0 -4px;border:1px solid #000;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-saturation i b{display:block;width:5px;height:5px;border:1px solid #fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-hue,.colorpicker-alpha{float:left;width:15px;height:100px;margin-bottom:4px;margin-left:4px;cursor:row-resize}.colorpicker-hue i,.colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:100%;height:1px;margin-top:-1px;background:#000;border-top:1px solid #fff}.colorpicker-hue{background-image:url("img/hue.png")}.colorpicker-alpha{display:none;background-image:url("img/alpha.png")}.colorpicker{top:0;left:0;z-index:2500;min-width:130px;padding:4px;margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1}.colorpicker:before,.colorpicker:after{display:table;line-height:0;content:""}.colorpicker:after{clear:both}.colorpicker:before{position:absolute;top:-7px;left:6px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.colorpicker:after{position:absolute;top:-6px;left:7px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.colorpicker div{position:relative}.colorpicker.colorpicker-with-alpha{min-width:140px}.colorpicker.colorpicker-with-alpha .colorpicker-alpha{display:block}.colorpicker-color{height:10px;margin-top:5px;clear:both;background-image:url("img/alpha.png");background-position:0 100%}.colorpicker-color div{height:10px}.colorpicker-element .input-group-addon i{display:block;width:16px;height:16px;cursor:pointer}.colorpicker.colorpicker-inline{position:relative;display:inline-block;float:none}.colorpicker.colorpicker-horizontal{width:110px;height:auto;min-width:110px}.colorpicker.colorpicker-horizontal .colorpicker-saturation{margin-bottom:4px}.colorpicker.colorpicker-horizontal .colorpicker-color{width:100px}.colorpicker.colorpicker-horizontal .colorpicker-hue,.colorpicker.colorpicker-horizontal .colorpicker-alpha{float:left;width:100px;height:15px;margin-bottom:4px;margin-left:0;cursor:col-resize}.colorpicker.colorpicker-horizontal .colorpicker-hue i,.colorpicker.colorpicker-horizontal .colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:1px;height:15px;margin-top:0;background:#fff;border:0}.colorpicker.colorpicker-horizontal .colorpicker-hue{background-image:url("img/hue-horizontal.png")}.colorpicker.colorpicker-horizontal .colorpicker-alpha{background-image:url("img/alpha-horizontal.png")}.colorpicker.colorpicker-hidden{display:none}.colorpicker.colorpicker-visible{display:block}.colorpicker-inline.colorpicker-visible{display:inline-block}

View File

@ -0,0 +1,15 @@
/**
* Japanese translation for bootstrap-datepicker
* Norio Suzuki <https://github.com/suzuki/>
*/
;(function($){
$.fn.datepicker.dates['ja'] = {
days: ["日曜", "月曜", "火曜", "水曜", "木曜", "金曜", "土曜", "日曜"],
daysShort: ["日", "月", "火", "水", "木", "金", "土", "日"],
daysMin: ["日", "月", "火", "水", "木", "金", "土", "日"],
months: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
monthsShort: ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"],
today: "今日",
format: "yyyy/mm/dd"
};
}(jQuery));

View File

@ -0,0 +1,10 @@
/*!
* Timepicker Component for Twitter Bootstrap
*
* Copyright 2013 Joris de Wit
*
* Contributors https://github.com/jdewit/bootstrap-timepicker/graphs/contributors
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/.bootstrap-timepicker{position:relative}.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu{left:auto;right:0}.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:before{left:auto;right:12px}.bootstrap-timepicker.pull-right .bootstrap-timepicker-widget.dropdown-menu:after{left:auto;right:13px}.bootstrap-timepicker .add-on{cursor:pointer}.bootstrap-timepicker .add-on i{display:inline-block;width:16px;height:16px}.bootstrap-timepicker-widget.dropdown-menu{padding:2px 3px 2px 2px}.bootstrap-timepicker-widget.dropdown-menu.open{display:inline-block}.bootstrap-timepicker-widget.dropdown-menu:before{border-bottom:7px solid rgba(0,0,0,0.2);border-left:7px solid transparent;border-right:7px solid transparent;content:"";display:inline-block;left:9px;position:absolute;top:-7px}.bootstrap-timepicker-widget.dropdown-menu:after{border-bottom:6px solid #fff;border-left:6px solid transparent;border-right:6px solid transparent;content:"";display:inline-block;left:10px;position:absolute;top:-6px}.bootstrap-timepicker-widget a.btn,.bootstrap-timepicker-widget input{border-radius:4px}.bootstrap-timepicker-widget table{width:100%;margin:0}.bootstrap-timepicker-widget table td{text-align:center;height:30px;margin:0;padding:2px}.bootstrap-timepicker-widget table td:not(.separator){min-width:30px}.bootstrap-timepicker-widget table td span{width:100%}.bootstrap-timepicker-widget table td a{border:1px transparent solid;width:100%;display:inline-block;margin:0;padding:8px 0;outline:0;color:#333}.bootstrap-timepicker-widget table td a:hover{text-decoration:none;background-color:#eee;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;border-color:#ddd}.bootstrap-timepicker-widget table td a i{margin-top:2px}.bootstrap-timepicker-widget table td input{width:25px;margin:0;text-align:center}.bootstrap-timepicker-widget .modal-content{padding:4px}@media(min-width:767px){.bootstrap-timepicker-widget.modal{width:200px;margin-left:-100px}}@media(max-width:767px){.bootstrap-timepicker{width:100%}.bootstrap-timepicker .dropdown-menu{width:100%}}

View File

@ -0,0 +1,224 @@
/* cyrillic-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7qsDJT9g.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7jsDJT9g.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7rsDJT9g.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7ksDJT9g.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7osDJT9g.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7psDJT9g.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7nsDI.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmhduz8A.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwkxduz8A.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmxduz8A.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlBduz8A.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmBduz8A.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmRduz8A.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNa7lqDY.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK3dSBYKcSV-LCoeQqfX1RYOo3qPK7lqDY.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNK7lqDY.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK3dSBYKcSV-LCoeQqfX1RYOo3qO67lqDY.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK3dSBYKcSV-LCoeQqfX1RYOo3qN67lqDY.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNq7lqDY.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
src: url(https://fonts.gstatic.com/s/sourcesanspro/v22/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View File

@ -0,0 +1,202 @@
table.dataTable {
clear: both;
margin-top: 6px !important;
margin-bottom: 6px !important;
max-width: none !important;
border-collapse: separate !important;
}
table.dataTable td,
table.dataTable th {
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
table.dataTable td.dataTables_empty,
table.dataTable th.dataTables_empty {
text-align: center;
}
table.dataTable.nowrap th,
table.dataTable.nowrap td {
white-space: nowrap;
}
div.dataTables_wrapper div.dataTables_length label {
font-weight: normal;
text-align: left;
white-space: nowrap;
}
div.dataTables_wrapper div.dataTables_length select {
width: 75px;
display: inline-block;
}
div.dataTables_wrapper div.dataTables_filter {
text-align: right;
}
div.dataTables_wrapper div.dataTables_filter label {
font-weight: normal;
white-space: nowrap;
text-align: left;
}
div.dataTables_wrapper div.dataTables_filter input {
margin-left: 0.5em;
display: inline-block;
width: auto;
}
div.dataTables_wrapper div.dataTables_info {
padding-top: 0.85em;
white-space: nowrap;
}
div.dataTables_wrapper div.dataTables_paginate {
margin: 0;
white-space: nowrap;
text-align: right;
}
div.dataTables_wrapper div.dataTables_paginate ul.pagination {
margin: 2px 0;
white-space: nowrap;
justify-content: flex-end;
}
div.dataTables_wrapper div.dataTables_processing {
position: absolute;
top: 50%;
left: 50%;
width: 200px;
margin-left: -100px;
margin-top: -26px;
text-align: center;
padding: 1em 0;
}
table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting,
table.dataTable thead > tr > td.sorting_asc,
table.dataTable thead > tr > td.sorting_desc,
table.dataTable thead > tr > td.sorting {
padding-right: 30px;
}
table.dataTable thead > tr > th:active,
table.dataTable thead > tr > td:active {
outline: none;
}
table.dataTable thead .sorting,
table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc,
table.dataTable thead .sorting_asc_disabled,
table.dataTable thead .sorting_desc_disabled {
cursor: pointer;
position: relative;
}
table.dataTable thead .sorting:before, table.dataTable thead .sorting:after,
table.dataTable thead .sorting_asc:before,
table.dataTable thead .sorting_asc:after,
table.dataTable thead .sorting_desc:before,
table.dataTable thead .sorting_desc:after,
table.dataTable thead .sorting_asc_disabled:before,
table.dataTable thead .sorting_asc_disabled:after,
table.dataTable thead .sorting_desc_disabled:before,
table.dataTable thead .sorting_desc_disabled:after {
position: absolute;
bottom: 0.9em;
display: block;
opacity: 0.3;
}
table.dataTable thead .sorting:before,
table.dataTable thead .sorting_asc:before,
table.dataTable thead .sorting_desc:before,
table.dataTable thead .sorting_asc_disabled:before,
table.dataTable thead .sorting_desc_disabled:before {
right: 1em;
content: "\2191";
}
table.dataTable thead .sorting:after,
table.dataTable thead .sorting_asc:after,
table.dataTable thead .sorting_desc:after,
table.dataTable thead .sorting_asc_disabled:after,
table.dataTable thead .sorting_desc_disabled:after {
right: 0.5em;
content: "\2193";
}
table.dataTable thead .sorting_asc:before,
table.dataTable thead .sorting_desc:after {
opacity: 1;
}
table.dataTable thead .sorting_asc_disabled:before,
table.dataTable thead .sorting_desc_disabled:after {
opacity: 0;
}
div.dataTables_scrollHead table.dataTable {
margin-bottom: 0 !important;
}
div.dataTables_scrollBody table {
border-top: none;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
div.dataTables_scrollBody table thead .sorting:after,
div.dataTables_scrollBody table thead .sorting_asc:after,
div.dataTables_scrollBody table thead .sorting_desc:after {
display: none;
}
div.dataTables_scrollBody table tbody tr:first-child th,
div.dataTables_scrollBody table tbody tr:first-child td {
border-top: none;
}
div.dataTables_scrollFoot > .dataTables_scrollFootInner {
box-sizing: content-box;
}
div.dataTables_scrollFoot > .dataTables_scrollFootInner > table {
margin-top: 0 !important;
border-top: none;
}
@media screen and (max-width: 767px) {
div.dataTables_wrapper div.dataTables_length,
div.dataTables_wrapper div.dataTables_filter,
div.dataTables_wrapper div.dataTables_info,
div.dataTables_wrapper div.dataTables_paginate {
text-align: center;
}
}
table.dataTable.table-sm > thead > tr > th {
padding-right: 20px;
}
table.dataTable.table-sm .sorting:before,
table.dataTable.table-sm .sorting_asc:before,
table.dataTable.table-sm .sorting_desc:before {
top: 5px;
right: 0.85em;
}
table.dataTable.table-sm .sorting:after,
table.dataTable.table-sm .sorting_asc:after,
table.dataTable.table-sm .sorting_desc:after {
top: 5px;
}
table.table-bordered.dataTable th,
table.table-bordered.dataTable td {
border-left-width: 0;
}
table.table-bordered.dataTable th:last-child, table.table-bordered.dataTable th:last-child,
table.table-bordered.dataTable td:last-child,
table.table-bordered.dataTable td:last-child {
border-right-width: 0;
}
table.table-bordered.dataTable tbody th,
table.table-bordered.dataTable tbody td {
border-bottom-width: 0;
}
div.dataTables_scrollHead table.table-bordered {
border-bottom-width: 0;
}
div.table-responsive > div.dataTables_wrapper > div.row {
margin: 0;
}
div.table-responsive > div.dataTables_wrapper > div.row > div[class^="col-"]:first-child {
padding-left: 0;
}
div.table-responsive > div.dataTables_wrapper > div.row > div[class^="col-"]:last-child {
padding-right: 0;
}

View File

@ -0,0 +1,790 @@
/*!
* Datepicker for Bootstrap
*
* Copyright 2012 Stefan Petre
* Improvements by Andrew Rowls
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
*/
.datepicker {
padding: 4px;
border-radius: 4px;
direction: ltr;
/*.dow {
border-top: 1px solid #ddd !important;
}*/
}
.datepicker-inline {
width: 100%;
}
.datepicker.datepicker-rtl {
direction: rtl;
}
.datepicker.datepicker-rtl table tr td span {
float: right;
}
.datepicker-dropdown {
top: 0;
left: 0;
}
.datepicker-dropdown:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-top: 0;
border-bottom-color: rgba(0, 0, 0, 0.2);
position: absolute;
}
.datepicker-dropdown:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-top: 0;
position: absolute;
}
.datepicker-dropdown.datepicker-orient-left:before {
left: 6px;
}
.datepicker-dropdown.datepicker-orient-left:after {
left: 7px;
}
.datepicker-dropdown.datepicker-orient-right:before {
right: 6px;
}
.datepicker-dropdown.datepicker-orient-right:after {
right: 7px;
}
.datepicker-dropdown.datepicker-orient-top:before {
top: -7px;
}
.datepicker-dropdown.datepicker-orient-top:after {
top: -6px;
}
.datepicker-dropdown.datepicker-orient-bottom:before {
bottom: -7px;
border-bottom: 0;
border-top: 7px solid #999;
}
.datepicker-dropdown.datepicker-orient-bottom:after {
bottom: -6px;
border-bottom: 0;
border-top: 6px solid #fff;
}
.datepicker > div {
display: none;
}
.datepicker.days div.datepicker-days {
display: block;
}
.datepicker.months div.datepicker-months {
display: block;
}
.datepicker.years div.datepicker-years {
display: block;
}
.datepicker table {
margin: 0;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.datepicker table tr td,
.datepicker table tr th {
text-align: center;
width: 30px;
height: 30px;
border-radius: 4px;
border: none;
}
.table-striped .datepicker table tr td,
.table-striped .datepicker table tr th {
background-color: transparent;
}
.datepicker table tr td.day:hover,
.datepicker table tr td.day.focused {
background: rgba(0,0,0,0.2);
cursor: pointer;
}
.datepicker table tr td.old,
.datepicker table tr td.new {
color: #777;
}
.datepicker table tr td.disabled,
.datepicker table tr td.disabled:hover {
background: none;
color: #444;
cursor: default;
}
.datepicker table tr td.today,
.datepicker table tr td.today:hover,
.datepicker table tr td.today.disabled,
.datepicker table tr td.today.disabled:hover {
color: #000000;
background: rgba(0,0,0,0.2);
border-color: #ffb733;
}
.datepicker table tr td.today:hover,
.datepicker table tr td.today:hover:hover,
.datepicker table tr td.today.disabled:hover,
.datepicker table tr td.today.disabled:hover:hover,
.datepicker table tr td.today:focus,
.datepicker table tr td.today:hover:focus,
.datepicker table tr td.today.disabled:focus,
.datepicker table tr td.today.disabled:hover:focus,
.datepicker table tr td.today:active,
.datepicker table tr td.today:hover:active,
.datepicker table tr td.today.disabled:active,
.datepicker table tr td.today.disabled:hover:active,
.datepicker table tr td.today.active,
.datepicker table tr td.today:hover.active,
.datepicker table tr td.today.disabled.active,
.datepicker table tr td.today.disabled:hover.active,
.open .dropdown-toggle.datepicker table tr td.today,
.open .dropdown-toggle.datepicker table tr td.today:hover,
.open .dropdown-toggle.datepicker table tr td.today.disabled,
.open .dropdown-toggle.datepicker table tr td.today.disabled:hover {
color: #000000;
background: rgba(0,0,0,0.2);
border-color: #f59e00;
}
.datepicker table tr td.today:active,
.datepicker table tr td.today:hover:active,
.datepicker table tr td.today.disabled:active,
.datepicker table tr td.today.disabled:hover:active,
.datepicker table tr td.today.active,
.datepicker table tr td.today:hover.active,
.datepicker table tr td.today.disabled.active,
.datepicker table tr td.today.disabled:hover.active,
.open .dropdown-toggle.datepicker table tr td.today,
.open .dropdown-toggle.datepicker table tr td.today:hover,
.open .dropdown-toggle.datepicker table tr td.today.disabled,
.open .dropdown-toggle.datepicker table tr td.today.disabled:hover {
background-image: none;
}
.datepicker table tr td.today.disabled,
.datepicker table tr td.today:hover.disabled,
.datepicker table tr td.today.disabled.disabled,
.datepicker table tr td.today.disabled:hover.disabled,
.datepicker table tr td.today[disabled],
.datepicker table tr td.today:hover[disabled],
.datepicker table tr td.today.disabled[disabled],
.datepicker table tr td.today.disabled:hover[disabled],
fieldset[disabled] .datepicker table tr td.today,
fieldset[disabled] .datepicker table tr td.today:hover,
fieldset[disabled] .datepicker table tr td.today.disabled,
fieldset[disabled] .datepicker table tr td.today.disabled:hover,
.datepicker table tr td.today.disabled:hover,
.datepicker table tr td.today:hover.disabled:hover,
.datepicker table tr td.today.disabled.disabled:hover,
.datepicker table tr td.today.disabled:hover.disabled:hover,
.datepicker table tr td.today[disabled]:hover,
.datepicker table tr td.today:hover[disabled]:hover,
.datepicker table tr td.today.disabled[disabled]:hover,
.datepicker table tr td.today.disabled:hover[disabled]:hover,
fieldset[disabled] .datepicker table tr td.today:hover,
fieldset[disabled] .datepicker table tr td.today:hover:hover,
fieldset[disabled] .datepicker table tr td.today.disabled:hover,
fieldset[disabled] .datepicker table tr td.today.disabled:hover:hover,
.datepicker table tr td.today.disabled:focus,
.datepicker table tr td.today:hover.disabled:focus,
.datepicker table tr td.today.disabled.disabled:focus,
.datepicker table tr td.today.disabled:hover.disabled:focus,
.datepicker table tr td.today[disabled]:focus,
.datepicker table tr td.today:hover[disabled]:focus,
.datepicker table tr td.today.disabled[disabled]:focus,
.datepicker table tr td.today.disabled:hover[disabled]:focus,
fieldset[disabled] .datepicker table tr td.today:focus,
fieldset[disabled] .datepicker table tr td.today:hover:focus,
fieldset[disabled] .datepicker table tr td.today.disabled:focus,
fieldset[disabled] .datepicker table tr td.today.disabled:hover:focus,
.datepicker table tr td.today.disabled:active,
.datepicker table tr td.today:hover.disabled:active,
.datepicker table tr td.today.disabled.disabled:active,
.datepicker table tr td.today.disabled:hover.disabled:active,
.datepicker table tr td.today[disabled]:active,
.datepicker table tr td.today:hover[disabled]:active,
.datepicker table tr td.today.disabled[disabled]:active,
.datepicker table tr td.today.disabled:hover[disabled]:active,
fieldset[disabled] .datepicker table tr td.today:active,
fieldset[disabled] .datepicker table tr td.today:hover:active,
fieldset[disabled] .datepicker table tr td.today.disabled:active,
fieldset[disabled] .datepicker table tr td.today.disabled:hover:active,
.datepicker table tr td.today.disabled.active,
.datepicker table tr td.today:hover.disabled.active,
.datepicker table tr td.today.disabled.disabled.active,
.datepicker table tr td.today.disabled:hover.disabled.active,
.datepicker table tr td.today[disabled].active,
.datepicker table tr td.today:hover[disabled].active,
.datepicker table tr td.today.disabled[disabled].active,
.datepicker table tr td.today.disabled:hover[disabled].active,
fieldset[disabled] .datepicker table tr td.today.active,
fieldset[disabled] .datepicker table tr td.today:hover.active,
fieldset[disabled] .datepicker table tr td.today.disabled.active,
fieldset[disabled] .datepicker table tr td.today.disabled:hover.active {
background: rgba(0,0,0,0.2);
border-color: #ffb733;
}
.datepicker table tr td.today:hover:hover {
color: #000;
}
.datepicker table tr td.today.active:hover {
color: #fff;
}
.datepicker table tr td.range,
.datepicker table tr td.range:hover,
.datepicker table tr td.range.disabled,
.datepicker table tr td.range.disabled:hover {
background: rgba(0,0,0,0.2);
border-radius: 0;
}
.datepicker table tr td.range.today,
.datepicker table tr td.range.today:hover,
.datepicker table tr td.range.today.disabled,
.datepicker table tr td.range.today.disabled:hover {
color: #000000;
background: rgba(0,0,0,0.2);
border-color: #f1a417;
border-radius: 0;
}
.datepicker table tr td.range.today:hover,
.datepicker table tr td.range.today:hover:hover,
.datepicker table tr td.range.today.disabled:hover,
.datepicker table tr td.range.today.disabled:hover:hover,
.datepicker table tr td.range.today:focus,
.datepicker table tr td.range.today:hover:focus,
.datepicker table tr td.range.today.disabled:focus,
.datepicker table tr td.range.today.disabled:hover:focus,
.datepicker table tr td.range.today:active,
.datepicker table tr td.range.today:hover:active,
.datepicker table tr td.range.today.disabled:active,
.datepicker table tr td.range.today.disabled:hover:active,
.datepicker table tr td.range.today.active,
.datepicker table tr td.range.today:hover.active,
.datepicker table tr td.range.today.disabled.active,
.datepicker table tr td.range.today.disabled:hover.active,
.open .dropdown-toggle.datepicker table tr td.range.today,
.open .dropdown-toggle.datepicker table tr td.range.today:hover,
.open .dropdown-toggle.datepicker table tr td.range.today.disabled,
.open .dropdown-toggle.datepicker table tr td.range.today.disabled:hover {
color: #000000;
background: rgba(0,0,0,0.2);
border-color: #bf800c;
}
.datepicker table tr td.range.today:active,
.datepicker table tr td.range.today:hover:active,
.datepicker table tr td.range.today.disabled:active,
.datepicker table tr td.range.today.disabled:hover:active,
.datepicker table tr td.range.today.active,
.datepicker table tr td.range.today:hover.active,
.datepicker table tr td.range.today.disabled.active,
.datepicker table tr td.range.today.disabled:hover.active,
.open .dropdown-toggle.datepicker table tr td.range.today,
.open .dropdown-toggle.datepicker table tr td.range.today:hover,
.open .dropdown-toggle.datepicker table tr td.range.today.disabled,
.open .dropdown-toggle.datepicker table tr td.range.today.disabled:hover {
background-image: none;
}
.datepicker table tr td.range.today.disabled,
.datepicker table tr td.range.today:hover.disabled,
.datepicker table tr td.range.today.disabled.disabled,
.datepicker table tr td.range.today.disabled:hover.disabled,
.datepicker table tr td.range.today[disabled],
.datepicker table tr td.range.today:hover[disabled],
.datepicker table tr td.range.today.disabled[disabled],
.datepicker table tr td.range.today.disabled:hover[disabled],
fieldset[disabled] .datepicker table tr td.range.today,
fieldset[disabled] .datepicker table tr td.range.today:hover,
fieldset[disabled] .datepicker table tr td.range.today.disabled,
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover,
.datepicker table tr td.range.today.disabled:hover,
.datepicker table tr td.range.today:hover.disabled:hover,
.datepicker table tr td.range.today.disabled.disabled:hover,
.datepicker table tr td.range.today.disabled:hover.disabled:hover,
.datepicker table tr td.range.today[disabled]:hover,
.datepicker table tr td.range.today:hover[disabled]:hover,
.datepicker table tr td.range.today.disabled[disabled]:hover,
.datepicker table tr td.range.today.disabled:hover[disabled]:hover,
fieldset[disabled] .datepicker table tr td.range.today:hover,
fieldset[disabled] .datepicker table tr td.range.today:hover:hover,
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover,
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:hover,
.datepicker table tr td.range.today.disabled:focus,
.datepicker table tr td.range.today:hover.disabled:focus,
.datepicker table tr td.range.today.disabled.disabled:focus,
.datepicker table tr td.range.today.disabled:hover.disabled:focus,
.datepicker table tr td.range.today[disabled]:focus,
.datepicker table tr td.range.today:hover[disabled]:focus,
.datepicker table tr td.range.today.disabled[disabled]:focus,
.datepicker table tr td.range.today.disabled:hover[disabled]:focus,
fieldset[disabled] .datepicker table tr td.range.today:focus,
fieldset[disabled] .datepicker table tr td.range.today:hover:focus,
fieldset[disabled] .datepicker table tr td.range.today.disabled:focus,
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:focus,
.datepicker table tr td.range.today.disabled:active,
.datepicker table tr td.range.today:hover.disabled:active,
.datepicker table tr td.range.today.disabled.disabled:active,
.datepicker table tr td.range.today.disabled:hover.disabled:active,
.datepicker table tr td.range.today[disabled]:active,
.datepicker table tr td.range.today:hover[disabled]:active,
.datepicker table tr td.range.today.disabled[disabled]:active,
.datepicker table tr td.range.today.disabled:hover[disabled]:active,
fieldset[disabled] .datepicker table tr td.range.today:active,
fieldset[disabled] .datepicker table tr td.range.today:hover:active,
fieldset[disabled] .datepicker table tr td.range.today.disabled:active,
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:active,
.datepicker table tr td.range.today.disabled.active,
.datepicker table tr td.range.today:hover.disabled.active,
.datepicker table tr td.range.today.disabled.disabled.active,
.datepicker table tr td.range.today.disabled:hover.disabled.active,
.datepicker table tr td.range.today[disabled].active,
.datepicker table tr td.range.today:hover[disabled].active,
.datepicker table tr td.range.today.disabled[disabled].active,
.datepicker table tr td.range.today.disabled:hover[disabled].active,
fieldset[disabled] .datepicker table tr td.range.today.active,
fieldset[disabled] .datepicker table tr td.range.today:hover.active,
fieldset[disabled] .datepicker table tr td.range.today.disabled.active,
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover.active {
background: rgba(0,0,0,0.2);
border-color: #f1a417;
}
.datepicker table tr td.selected,
.datepicker table tr td.selected:hover,
.datepicker table tr td.selected.disabled,
.datepicker table tr td.selected.disabled:hover {
color: #ffffff;
background: rgba(0,0,0,0.2);
border-color: #555555;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td.selected:hover,
.datepicker table tr td.selected:hover:hover,
.datepicker table tr td.selected.disabled:hover,
.datepicker table tr td.selected.disabled:hover:hover,
.datepicker table tr td.selected:focus,
.datepicker table tr td.selected:hover:focus,
.datepicker table tr td.selected.disabled:focus,
.datepicker table tr td.selected.disabled:hover:focus,
.datepicker table tr td.selected:active,
.datepicker table tr td.selected:hover:active,
.datepicker table tr td.selected.disabled:active,
.datepicker table tr td.selected.disabled:hover:active,
.datepicker table tr td.selected.active,
.datepicker table tr td.selected:hover.active,
.datepicker table tr td.selected.disabled.active,
.datepicker table tr td.selected.disabled:hover.active,
.open .dropdown-toggle.datepicker table tr td.selected,
.open .dropdown-toggle.datepicker table tr td.selected:hover,
.open .dropdown-toggle.datepicker table tr td.selected.disabled,
.open .dropdown-toggle.datepicker table tr td.selected.disabled:hover {
color: #ffffff;
background: rgba(0,0,0,0.2);
border-color: #373737;
}
.datepicker table tr td.selected:active,
.datepicker table tr td.selected:hover:active,
.datepicker table tr td.selected.disabled:active,
.datepicker table tr td.selected.disabled:hover:active,
.datepicker table tr td.selected.active,
.datepicker table tr td.selected:hover.active,
.datepicker table tr td.selected.disabled.active,
.datepicker table tr td.selected.disabled:hover.active,
.open .dropdown-toggle.datepicker table tr td.selected,
.open .dropdown-toggle.datepicker table tr td.selected:hover,
.open .dropdown-toggle.datepicker table tr td.selected.disabled,
.open .dropdown-toggle.datepicker table tr td.selected.disabled:hover {
background-image: none;
}
.datepicker table tr td.selected.disabled,
.datepicker table tr td.selected:hover.disabled,
.datepicker table tr td.selected.disabled.disabled,
.datepicker table tr td.selected.disabled:hover.disabled,
.datepicker table tr td.selected[disabled],
.datepicker table tr td.selected:hover[disabled],
.datepicker table tr td.selected.disabled[disabled],
.datepicker table tr td.selected.disabled:hover[disabled],
fieldset[disabled] .datepicker table tr td.selected,
fieldset[disabled] .datepicker table tr td.selected:hover,
fieldset[disabled] .datepicker table tr td.selected.disabled,
fieldset[disabled] .datepicker table tr td.selected.disabled:hover,
.datepicker table tr td.selected.disabled:hover,
.datepicker table tr td.selected:hover.disabled:hover,
.datepicker table tr td.selected.disabled.disabled:hover,
.datepicker table tr td.selected.disabled:hover.disabled:hover,
.datepicker table tr td.selected[disabled]:hover,
.datepicker table tr td.selected:hover[disabled]:hover,
.datepicker table tr td.selected.disabled[disabled]:hover,
.datepicker table tr td.selected.disabled:hover[disabled]:hover,
fieldset[disabled] .datepicker table tr td.selected:hover,
fieldset[disabled] .datepicker table tr td.selected:hover:hover,
fieldset[disabled] .datepicker table tr td.selected.disabled:hover,
fieldset[disabled] .datepicker table tr td.selected.disabled:hover:hover,
.datepicker table tr td.selected.disabled:focus,
.datepicker table tr td.selected:hover.disabled:focus,
.datepicker table tr td.selected.disabled.disabled:focus,
.datepicker table tr td.selected.disabled:hover.disabled:focus,
.datepicker table tr td.selected[disabled]:focus,
.datepicker table tr td.selected:hover[disabled]:focus,
.datepicker table tr td.selected.disabled[disabled]:focus,
.datepicker table tr td.selected.disabled:hover[disabled]:focus,
fieldset[disabled] .datepicker table tr td.selected:focus,
fieldset[disabled] .datepicker table tr td.selected:hover:focus,
fieldset[disabled] .datepicker table tr td.selected.disabled:focus,
fieldset[disabled] .datepicker table tr td.selected.disabled:hover:focus,
.datepicker table tr td.selected.disabled:active,
.datepicker table tr td.selected:hover.disabled:active,
.datepicker table tr td.selected.disabled.disabled:active,
.datepicker table tr td.selected.disabled:hover.disabled:active,
.datepicker table tr td.selected[disabled]:active,
.datepicker table tr td.selected:hover[disabled]:active,
.datepicker table tr td.selected.disabled[disabled]:active,
.datepicker table tr td.selected.disabled:hover[disabled]:active,
fieldset[disabled] .datepicker table tr td.selected:active,
fieldset[disabled] .datepicker table tr td.selected:hover:active,
fieldset[disabled] .datepicker table tr td.selected.disabled:active,
fieldset[disabled] .datepicker table tr td.selected.disabled:hover:active,
.datepicker table tr td.selected.disabled.active,
.datepicker table tr td.selected:hover.disabled.active,
.datepicker table tr td.selected.disabled.disabled.active,
.datepicker table tr td.selected.disabled:hover.disabled.active,
.datepicker table tr td.selected[disabled].active,
.datepicker table tr td.selected:hover[disabled].active,
.datepicker table tr td.selected.disabled[disabled].active,
.datepicker table tr td.selected.disabled:hover[disabled].active,
fieldset[disabled] .datepicker table tr td.selected.active,
fieldset[disabled] .datepicker table tr td.selected:hover.active,
fieldset[disabled] .datepicker table tr td.selected.disabled.active,
fieldset[disabled] .datepicker table tr td.selected.disabled:hover.active {
background: rgba(0,0,0,0.2);
border-color: #555555;
}
.datepicker table tr td.active,
.datepicker table tr td.active:hover,
.datepicker table tr td.active.disabled,
.datepicker table tr td.active.disabled:hover {
color: #ffffff;
background: rgba(0,0,0,0.2);
border-color: #357ebd;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td.active:hover,
.datepicker table tr td.active:hover:hover,
.datepicker table tr td.active.disabled:hover,
.datepicker table tr td.active.disabled:hover:hover,
.datepicker table tr td.active:focus,
.datepicker table tr td.active:hover:focus,
.datepicker table tr td.active.disabled:focus,
.datepicker table tr td.active.disabled:hover:focus,
.datepicker table tr td.active:active,
.datepicker table tr td.active:hover:active,
.datepicker table tr td.active.disabled:active,
.datepicker table tr td.active.disabled:hover:active,
.datepicker table tr td.active.active,
.datepicker table tr td.active:hover.active,
.datepicker table tr td.active.disabled.active,
.datepicker table tr td.active.disabled:hover.active,
.open .dropdown-toggle.datepicker table tr td.active,
.open .dropdown-toggle.datepicker table tr td.active:hover,
.open .dropdown-toggle.datepicker table tr td.active.disabled,
.open .dropdown-toggle.datepicker table tr td.active.disabled:hover {
color: #ffffff;
background: rgba(0,0,0,0.5);
border-color: #285e8e;
}
.datepicker table tr td.active:active,
.datepicker table tr td.active:hover:active,
.datepicker table tr td.active.disabled:active,
.datepicker table tr td.active.disabled:hover:active,
.datepicker table tr td.active.active,
.datepicker table tr td.active:hover.active,
.datepicker table tr td.active.disabled.active,
.datepicker table tr td.active.disabled:hover.active,
.open .dropdown-toggle.datepicker table tr td.active,
.open .dropdown-toggle.datepicker table tr td.active:hover,
.open .dropdown-toggle.datepicker table tr td.active.disabled,
.open .dropdown-toggle.datepicker table tr td.active.disabled:hover {
background-image: none;
}
.datepicker table tr td.active.disabled,
.datepicker table tr td.active:hover.disabled,
.datepicker table tr td.active.disabled.disabled,
.datepicker table tr td.active.disabled:hover.disabled,
.datepicker table tr td.active[disabled],
.datepicker table tr td.active:hover[disabled],
.datepicker table tr td.active.disabled[disabled],
.datepicker table tr td.active.disabled:hover[disabled],
fieldset[disabled] .datepicker table tr td.active,
fieldset[disabled] .datepicker table tr td.active:hover,
fieldset[disabled] .datepicker table tr td.active.disabled,
fieldset[disabled] .datepicker table tr td.active.disabled:hover,
.datepicker table tr td.active.disabled:hover,
.datepicker table tr td.active:hover.disabled:hover,
.datepicker table tr td.active.disabled.disabled:hover,
.datepicker table tr td.active.disabled:hover.disabled:hover,
.datepicker table tr td.active[disabled]:hover,
.datepicker table tr td.active:hover[disabled]:hover,
.datepicker table tr td.active.disabled[disabled]:hover,
.datepicker table tr td.active.disabled:hover[disabled]:hover,
fieldset[disabled] .datepicker table tr td.active:hover,
fieldset[disabled] .datepicker table tr td.active:hover:hover,
fieldset[disabled] .datepicker table tr td.active.disabled:hover,
fieldset[disabled] .datepicker table tr td.active.disabled:hover:hover,
.datepicker table tr td.active.disabled:focus,
.datepicker table tr td.active:hover.disabled:focus,
.datepicker table tr td.active.disabled.disabled:focus,
.datepicker table tr td.active.disabled:hover.disabled:focus,
.datepicker table tr td.active[disabled]:focus,
.datepicker table tr td.active:hover[disabled]:focus,
.datepicker table tr td.active.disabled[disabled]:focus,
.datepicker table tr td.active.disabled:hover[disabled]:focus,
fieldset[disabled] .datepicker table tr td.active:focus,
fieldset[disabled] .datepicker table tr td.active:hover:focus,
fieldset[disabled] .datepicker table tr td.active.disabled:focus,
fieldset[disabled] .datepicker table tr td.active.disabled:hover:focus,
.datepicker table tr td.active.disabled:active,
.datepicker table tr td.active:hover.disabled:active,
.datepicker table tr td.active.disabled.disabled:active,
.datepicker table tr td.active.disabled:hover.disabled:active,
.datepicker table tr td.active[disabled]:active,
.datepicker table tr td.active:hover[disabled]:active,
.datepicker table tr td.active.disabled[disabled]:active,
.datepicker table tr td.active.disabled:hover[disabled]:active,
fieldset[disabled] .datepicker table tr td.active:active,
fieldset[disabled] .datepicker table tr td.active:hover:active,
fieldset[disabled] .datepicker table tr td.active.disabled:active,
fieldset[disabled] .datepicker table tr td.active.disabled:hover:active,
.datepicker table tr td.active.disabled.active,
.datepicker table tr td.active:hover.disabled.active,
.datepicker table tr td.active.disabled.disabled.active,
.datepicker table tr td.active.disabled:hover.disabled.active,
.datepicker table tr td.active[disabled].active,
.datepicker table tr td.active:hover[disabled].active,
.datepicker table tr td.active.disabled[disabled].active,
.datepicker table tr td.active.disabled:hover[disabled].active,
fieldset[disabled] .datepicker table tr td.active.active,
fieldset[disabled] .datepicker table tr td.active:hover.active,
fieldset[disabled] .datepicker table tr td.active.disabled.active,
fieldset[disabled] .datepicker table tr td.active.disabled:hover.active {
background-color: #428bca;
border-color: #357ebd;
}
.datepicker table tr td span {
display: block;
width: 23%;
height: 54px;
line-height: 54px;
float: left;
margin: 1%;
cursor: pointer;
border-radius: 4px;
}
.datepicker table tr td span:hover {
background: rgba(0,0,0,0.2);
}
.datepicker table tr td span.disabled,
.datepicker table tr td span.disabled:hover {
background: none;
color: #444;
cursor: default;
}
.datepicker table tr td span.active,
.datepicker table tr td span.active:hover,
.datepicker table tr td span.active.disabled,
.datepicker table tr td span.active.disabled:hover {
color: #ffffff;
background-color: #428bca;
border-color: #357ebd;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td span.active:hover,
.datepicker table tr td span.active:hover:hover,
.datepicker table tr td span.active.disabled:hover,
.datepicker table tr td span.active.disabled:hover:hover,
.datepicker table tr td span.active:focus,
.datepicker table tr td span.active:hover:focus,
.datepicker table tr td span.active.disabled:focus,
.datepicker table tr td span.active.disabled:hover:focus,
.datepicker table tr td span.active:active,
.datepicker table tr td span.active:hover:active,
.datepicker table tr td span.active.disabled:active,
.datepicker table tr td span.active.disabled:hover:active,
.datepicker table tr td span.active.active,
.datepicker table tr td span.active:hover.active,
.datepicker table tr td span.active.disabled.active,
.datepicker table tr td span.active.disabled:hover.active,
.open .dropdown-toggle.datepicker table tr td span.active,
.open .dropdown-toggle.datepicker table tr td span.active:hover,
.open .dropdown-toggle.datepicker table tr td span.active.disabled,
.open .dropdown-toggle.datepicker table tr td span.active.disabled:hover {
color: #ffffff;
background-color: #3276b1;
border-color: #285e8e;
}
.datepicker table tr td span.active:active,
.datepicker table tr td span.active:hover:active,
.datepicker table tr td span.active.disabled:active,
.datepicker table tr td span.active.disabled:hover:active,
.datepicker table tr td span.active.active,
.datepicker table tr td span.active:hover.active,
.datepicker table tr td span.active.disabled.active,
.datepicker table tr td span.active.disabled:hover.active,
.open .dropdown-toggle.datepicker table tr td span.active,
.open .dropdown-toggle.datepicker table tr td span.active:hover,
.open .dropdown-toggle.datepicker table tr td span.active.disabled,
.open .dropdown-toggle.datepicker table tr td span.active.disabled:hover {
background-image: none;
}
.datepicker table tr td span.active.disabled,
.datepicker table tr td span.active:hover.disabled,
.datepicker table tr td span.active.disabled.disabled,
.datepicker table tr td span.active.disabled:hover.disabled,
.datepicker table tr td span.active[disabled],
.datepicker table tr td span.active:hover[disabled],
.datepicker table tr td span.active.disabled[disabled],
.datepicker table tr td span.active.disabled:hover[disabled],
fieldset[disabled] .datepicker table tr td span.active,
fieldset[disabled] .datepicker table tr td span.active:hover,
fieldset[disabled] .datepicker table tr td span.active.disabled,
fieldset[disabled] .datepicker table tr td span.active.disabled:hover,
.datepicker table tr td span.active.disabled:hover,
.datepicker table tr td span.active:hover.disabled:hover,
.datepicker table tr td span.active.disabled.disabled:hover,
.datepicker table tr td span.active.disabled:hover.disabled:hover,
.datepicker table tr td span.active[disabled]:hover,
.datepicker table tr td span.active:hover[disabled]:hover,
.datepicker table tr td span.active.disabled[disabled]:hover,
.datepicker table tr td span.active.disabled:hover[disabled]:hover,
fieldset[disabled] .datepicker table tr td span.active:hover,
fieldset[disabled] .datepicker table tr td span.active:hover:hover,
fieldset[disabled] .datepicker table tr td span.active.disabled:hover,
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:hover,
.datepicker table tr td span.active.disabled:focus,
.datepicker table tr td span.active:hover.disabled:focus,
.datepicker table tr td span.active.disabled.disabled:focus,
.datepicker table tr td span.active.disabled:hover.disabled:focus,
.datepicker table tr td span.active[disabled]:focus,
.datepicker table tr td span.active:hover[disabled]:focus,
.datepicker table tr td span.active.disabled[disabled]:focus,
.datepicker table tr td span.active.disabled:hover[disabled]:focus,
fieldset[disabled] .datepicker table tr td span.active:focus,
fieldset[disabled] .datepicker table tr td span.active:hover:focus,
fieldset[disabled] .datepicker table tr td span.active.disabled:focus,
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:focus,
.datepicker table tr td span.active.disabled:active,
.datepicker table tr td span.active:hover.disabled:active,
.datepicker table tr td span.active.disabled.disabled:active,
.datepicker table tr td span.active.disabled:hover.disabled:active,
.datepicker table tr td span.active[disabled]:active,
.datepicker table tr td span.active:hover[disabled]:active,
.datepicker table tr td span.active.disabled[disabled]:active,
.datepicker table tr td span.active.disabled:hover[disabled]:active,
fieldset[disabled] .datepicker table tr td span.active:active,
fieldset[disabled] .datepicker table tr td span.active:hover:active,
fieldset[disabled] .datepicker table tr td span.active.disabled:active,
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:active,
.datepicker table tr td span.active.disabled.active,
.datepicker table tr td span.active:hover.disabled.active,
.datepicker table tr td span.active.disabled.disabled.active,
.datepicker table tr td span.active.disabled:hover.disabled.active,
.datepicker table tr td span.active[disabled].active,
.datepicker table tr td span.active:hover[disabled].active,
.datepicker table tr td span.active.disabled[disabled].active,
.datepicker table tr td span.active.disabled:hover[disabled].active,
fieldset[disabled] .datepicker table tr td span.active.active,
fieldset[disabled] .datepicker table tr td span.active:hover.active,
fieldset[disabled] .datepicker table tr td span.active.disabled.active,
fieldset[disabled] .datepicker table tr td span.active.disabled:hover.active {
background-color: #428bca;
border-color: #357ebd;
}
.datepicker table tr td span.old,
.datepicker table tr td span.new {
color: #444;
}
.datepicker th.datepicker-switch {
width: 145px;
}
.datepicker thead tr:first-child th,
.datepicker tfoot tr th {
cursor: pointer;
}
.datepicker thead tr:first-child th:hover,
.datepicker tfoot tr th:hover {
background: rgba(0,0,0,0.2);
}
.datepicker .cw {
font-size: 10px;
width: 12px;
padding: 0 2px 0 5px;
vertical-align: middle;
}
.datepicker thead tr:first-child th.cw {
cursor: default;
background-color: transparent;
}
.input-group.date .input-group-addon i {
cursor: pointer;
width: 16px;
height: 16px;
}
.input-daterange input {
text-align: center;
}
.input-daterange input:first-child {
border-radius: 3px 0 0 3px;
}
.input-daterange input:last-child {
border-radius: 0 3px 3px 0;
}
.input-daterange .input-group-addon {
width: auto;
min-width: 16px;
padding: 4px 5px;
font-weight: normal;
line-height: 1.428571429;
text-align: center;
text-shadow: 0 1px 0 #fff;
vertical-align: middle;
background-color: #eeeeee;
border: solid #cccccc;
border-width: 1px 0;
margin-left: -5px;
margin-right: -5px;
}
.datepicker.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
float: left;
display: none;
min-width: 160px;
list-style: none;
background-color: #ffffff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 5px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
*border-right-width: 2px;
*border-bottom-width: 2px;
color: #333333;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 13px;
line-height: 1.428571429;
}
.datepicker.dropdown-menu th,
.datepicker.dropdown-menu td {
padding: 4px 5px;
}

View File

@ -0,0 +1,335 @@
/*!
* Stylesheet for the Date Range Picker, for use with Bootstrap 3.x
*
* Copyright 2013-2015 Dan Grossman ( http://www.dangrossman.info )
* Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
*
* Built for http://www.improvely.com
*/
.daterangepicker.dropdown-menu {
max-width: none;
z-index: 3000;
}
.daterangepicker.opensleft .ranges, .daterangepicker.opensleft .calendar {
float: left;
margin: 4px;
}
.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar,
.daterangepicker.openscenter .ranges, .daterangepicker.openscenter .calendar {
float: right;
margin: 4px;
}
.daterangepicker.single .ranges, .daterangepicker.single .calendar {
float: none;
}
.daterangepicker .ranges {
width: 160px;
text-align: left;
}
.daterangepicker .ranges .range_inputs>div {
float: left;
}
.daterangepicker .ranges .range_inputs>div:nth-child(2) {
padding-left: 11px;
}
.daterangepicker .calendar {
display: none;
max-width: 270px;
}
.daterangepicker.show-calendar .calendar {
display: block;
}
.daterangepicker .calendar.single .calendar-date {
border: none;
}
.daterangepicker .calendar th, .daterangepicker .calendar td {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
white-space: nowrap;
text-align: center;
min-width: 32px;
}
.daterangepicker .daterangepicker_start_input label,
.daterangepicker .daterangepicker_end_input label {
color: #333;
display: block;
font-size: 11px;
font-weight: normal;
height: 20px;
line-height: 20px;
margin-bottom: 2px;
text-shadow: #fff 1px 1px 0px;
text-transform: uppercase;
width: 74px;
}
.daterangepicker .ranges input {
font-size: 11px;
}
.daterangepicker .ranges .input-mini {
border: 1px solid #ccc;
border-radius: 4px;
color: #555;
display: block;
font-size: 11px;
height: 30px;
line-height: 30px;
vertical-align: middle;
margin: 0 0 10px 0;
padding: 0 6px;
width: 74px;
}
.daterangepicker .ranges ul {
list-style: none;
margin: 0;
padding: 0;
}
.daterangepicker .ranges li {
font-size: 13px;
background: #f5f5f5;
border: 1px solid #f5f5f5;
color: #08c;
padding: 3px 12px;
margin-bottom: 8px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
cursor: pointer;
}
.daterangepicker .ranges li.active, .daterangepicker .ranges li:hover {
background: #08c;
border: 1px solid #08c;
color: #fff;
}
.daterangepicker .calendar-date {
border: 1px solid #ddd;
padding: 4px;
border-radius: 4px;
background: #fff;
}
.daterangepicker .calendar-time {
text-align: center;
margin: 8px auto 0 auto;
line-height: 30px;
}
.daterangepicker {
position: absolute;
background: #fff;
top: 100px;
left: 20px;
padding: 4px;
margin-top: 1px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.daterangepicker.opensleft:before {
position: absolute;
top: -7px;
right: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensleft:after {
position: absolute;
top: -6px;
right: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.openscenter:before {
position: absolute;
top: -7px;
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.openscenter:after {
position: absolute;
top: -6px;
left: 0;
right: 0;
width: 0;
margin-left: auto;
margin-right: auto;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.opensright:before {
position: absolute;
top: -7px;
left: 9px;
display: inline-block;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-left: 7px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.2);
content: '';
}
.daterangepicker.opensright:after {
position: absolute;
top: -6px;
left: 10px;
display: inline-block;
border-right: 6px solid transparent;
border-bottom: 6px solid #fff;
border-left: 6px solid transparent;
content: '';
}
.daterangepicker.dropup{
margin-top: -5px;
}
.daterangepicker.dropup:before{
top: initial;
bottom:-7px;
border-bottom: initial;
border-top: 7px solid #ccc;
}
.daterangepicker.dropup:after{
top: initial;
bottom:-6px;
border-bottom: initial;
border-top: 6px solid #fff;
}
.daterangepicker table {
width: 100%;
margin: 0;
}
.daterangepicker td, .daterangepicker th {
text-align: center;
width: 20px;
height: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
cursor: pointer;
white-space: nowrap;
}
.daterangepicker td.off {
color: #999;
}
.daterangepicker td.disabled, .daterangepicker option.disabled {
color: #999;
}
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
background: #eee;
}
.daterangepicker td.in-range {
background: #ebf4f8;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.daterangepicker td.start-date {
-webkit-border-radius: 4px 0 0 4px;
-moz-border-radius: 4px 0 0 4px;
border-radius: 4px 0 0 4px;
}
.daterangepicker td.end-date {
-webkit-border-radius: 0 4px 4px 0;
-moz-border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
}
.daterangepicker td.start-date.end-date {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.daterangepicker td.active, .daterangepicker td.active:hover {
background-color: #357ebd;
border-color: #3071a9;
color: #fff;
}
.daterangepicker td.week, .daterangepicker th.week {
font-size: 80%;
color: #ccc;
}
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
font-size: 12px;
padding: 1px;
height: auto;
margin: 0;
cursor: default;
}
.daterangepicker select.monthselect {
margin-right: 2%;
width: 56%;
}
.daterangepicker select.yearselect {
width: 40%;
}
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
width: 50px;
margin-bottom: 0;
}
.daterangepicker_start_input {
float: left;
}
.daterangepicker_end_input {
float: left;
padding-left: 11px
}
.daterangepicker th.month {
width: auto;
}

View File

@ -0,0 +1,196 @@
/**
* AdminLTE Demo Menu
* ------------------
* You should not use this file in production.
* This file is for demo purposes only.
*/
(function ($) {
'use strict'
var $sidebar = $('.control-sidebar')
var $container = $('<div />', {
class: 'p-3'
})
$sidebar.append($container)
var navbar_dark_skins = [
'bg-primary',
'bg-info',
'bg-success',
'bg-danger'
]
var navbar_light_skins = [
'bg-warning',
'bg-white',
'bg-gray-light'
]
$container.append(
'<h5>Customize AdminLTE</h5><hr class="mb-2"/>'
+ '<h6>Navbar Variants</h6>'
)
var $navbar_variants = $('<div />', {
'class': 'd-flex'
})
var navbar_all_colors = navbar_dark_skins.concat(navbar_light_skins)
var $navbar_variants_colors = createSkinBlock(navbar_all_colors, function (e) {
var color = $(this).data('color')
console.log('Adding ' + color)
var $main_header = $('.main-header')
$main_header.removeClass('navbar-dark').removeClass('navbar-light')
navbar_all_colors.map(function (color) {
$main_header.removeClass(color)
})
if (navbar_dark_skins.indexOf(color) > -1) {
$main_header.addClass('navbar-dark')
console.log('AND navbar-dark')
} else {
console.log('AND navbar-light')
$main_header.addClass('navbar-light')
}
$main_header.addClass(color)
})
$navbar_variants.append($navbar_variants_colors)
$container.append($navbar_variants)
var $checkbox_container = $('<div />', {
'class': 'mb-4'
})
var $navbar_border = $('<input />', {
type : 'checkbox',
value : 1,
checked: $('.main-header').hasClass('border-bottom'),
'class': 'mr-1'
}).on('click', function () {
if ($(this).is(':checked')) {
$('.main-header').addClass('border-bottom')
} else {
$('.main-header').removeClass('border-bottom')
}
})
$checkbox_container.append($navbar_border)
$checkbox_container.append('<span>Navbar border</span>')
$container.append($checkbox_container)
var sidebar_colors = [
'bg-primary',
'bg-warning',
'bg-info',
'bg-danger',
'bg-success'
]
var sidebar_skins = [
'sidebar-dark-primary',
'sidebar-dark-warning',
'sidebar-dark-info',
'sidebar-dark-danger',
'sidebar-dark-success',
'sidebar-light-primary',
'sidebar-light-warning',
'sidebar-light-info',
'sidebar-light-danger',
'sidebar-light-success'
]
$container.append('<h6>Dark Sidebar Variants</h6>')
var $sidebar_variants = $('<div />', {
'class': 'd-flex'
})
$container.append($sidebar_variants)
$container.append(createSkinBlock(sidebar_colors, function () {
var color = $(this).data('color')
var sidebar_class = 'sidebar-dark-' + color.replace('bg-', '')
var $sidebar = $('.main-sidebar')
sidebar_skins.map(function (skin) {
$sidebar.removeClass(skin)
})
$sidebar.addClass(sidebar_class)
}))
$container.append('<h6>Light Sidebar Variants</h6>')
var $sidebar_variants = $('<div />', {
'class': 'd-flex'
})
$container.append($sidebar_variants)
$container.append(createSkinBlock(sidebar_colors, function () {
var color = $(this).data('color')
var sidebar_class = 'sidebar-light-' + color.replace('bg-', '')
var $sidebar = $('.main-sidebar')
sidebar_skins.map(function (skin) {
$sidebar.removeClass(skin)
})
$sidebar.addClass(sidebar_class)
}))
var logo_skins = navbar_all_colors
$container.append('<h6>Brand Logo Variants</h6>')
var $logo_variants = $('<div />', {
'class': 'd-flex'
})
$container.append($logo_variants)
var $clear_btn = $('<a />', {
href: 'javascript:void(0)'
}).text('clear').on('click', function () {
var $logo = $('.brand-link')
logo_skins.map(function (skin) {
$logo.removeClass(skin)
})
})
$container.append(createSkinBlock(logo_skins, function () {
var color = $(this).data('color')
var $logo = $('.brand-link')
logo_skins.map(function (skin) {
$logo.removeClass(skin)
})
$logo.addClass(color)
}).append($clear_btn))
function createSkinBlock(colors, callback) {
var $block = $('<div />', {
'class': 'd-flex flex-wrap mb-3'
})
colors.map(function (color) {
var $color = $('<div />', {
'class': (typeof color === 'object' ? color.join(' ') : color) + ' elevation-2'
})
$block.append($color)
$color.data('color', color)
$color.css({
width : '40px',
height : '20px',
borderRadius: '25px',
marginRight : 10,
marginBottom: 10,
opacity : 0.8,
cursor : 'pointer'
})
$color.hover(function () {
$(this).css({ opacity: 1 }).removeClass('elevation-2').addClass('elevation-4')
}, function () {
$(this).css({ opacity: 0.8 }).removeClass('elevation-4').addClass('elevation-2')
})
if (callback) {
$color.on('click', callback)
}
})
return $block
}
})(jQuery)

View File

@ -0,0 +1,841 @@
;(function () {
'use strict';
/**
* @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.
*
* @codingstandard ftlabs-jsv2
* @copyright The Financial Times Limited [All Rights Reserved]
* @license MIT License (see LICENSE.txt)
*/
/*jslint browser:true, node:true*/
/*global define, Event, Node*/
/**
* Instantiate fast-clicking listeners on the specified layer.
*
* @constructor
* @param {Element} layer The layer to listen on
* @param {Object} [options={}] The options to override the defaults
*/
function FastClick(layer, options) {
var oldOnClick;
options = options || {};
/**
* Whether a click is currently being tracked.
*
* @type boolean
*/
this.trackingClick = false;
/**
* Timestamp for when click tracking started.
*
* @type number
*/
this.trackingClickStart = 0;
/**
* The element being tracked for a click.
*
* @type EventTarget
*/
this.targetElement = null;
/**
* X-coordinate of touch start event.
*
* @type number
*/
this.touchStartX = 0;
/**
* Y-coordinate of touch start event.
*
* @type number
*/
this.touchStartY = 0;
/**
* ID of the last touch, retrieved from Touch.identifier.
*
* @type number
*/
this.lastTouchIdentifier = 0;
/**
* Touchmove boundary, beyond which a click will be cancelled.
*
* @type number
*/
this.touchBoundary = options.touchBoundary || 10;
/**
* The FastClick layer.
*
* @type Element
*/
this.layer = layer;
/**
* The minimum time between tap(touchstart and touchend) events
*
* @type number
*/
this.tapDelay = options.tapDelay || 200;
/**
* The maximum time for a tap
*
* @type number
*/
this.tapTimeout = options.tapTimeout || 700;
if (FastClick.notNeeded(layer)) {
return;
}
// Some old versions of Android don't have Function.prototype.bind
function bind(method, context) {
return function() { return method.apply(context, arguments); };
}
var methods = ['onMouse', 'onClick', 'onTouchStart', 'onTouchMove', 'onTouchEnd', 'onTouchCancel'];
var context = this;
for (var i = 0, l = methods.length; i < l; i++) {
context[methods[i]] = bind(context[methods[i]], context);
}
// Set up event handlers as required
if (deviceIsAndroid) {
layer.addEventListener('mouseover', this.onMouse, true);
layer.addEventListener('mousedown', this.onMouse, true);
layer.addEventListener('mouseup', this.onMouse, true);
}
layer.addEventListener('click', this.onClick, true);
layer.addEventListener('touchstart', this.onTouchStart, false);
layer.addEventListener('touchmove', this.onTouchMove, false);
layer.addEventListener('touchend', this.onTouchEnd, false);
layer.addEventListener('touchcancel', this.onTouchCancel, false);
// Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
// which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick
// layer when they are cancelled.
if (!Event.prototype.stopImmediatePropagation) {
layer.removeEventListener = function(type, callback, capture) {
var rmv = Node.prototype.removeEventListener;
if (type === 'click') {
rmv.call(layer, type, callback.hijacked || callback, capture);
} else {
rmv.call(layer, type, callback, capture);
}
};
layer.addEventListener = function(type, callback, capture) {
var adv = Node.prototype.addEventListener;
if (type === 'click') {
adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
if (!event.propagationStopped) {
callback(event);
}
}), capture);
} else {
adv.call(layer, type, callback, capture);
}
};
}
// If a handler is already declared in the element's onclick attribute, it will be fired before
// FastClick's onClick handler. Fix this by pulling out the user-defined handler function and
// adding it as listener.
if (typeof layer.onclick === 'function') {
// Android browser on at least 3.2 requires a new reference to the function in layer.onclick
// - the old one won't work if passed to addEventListener directly.
oldOnClick = layer.onclick;
layer.addEventListener('click', function(event) {
oldOnClick(event);
}, false);
layer.onclick = null;
}
}
/**
* Windows Phone 8.1 fakes user agent string to look like Android and iPhone.
*
* @type boolean
*/
var deviceIsWindowsPhone = navigator.userAgent.indexOf("Windows Phone") >= 0;
/**
* Android requires exceptions.
*
* @type boolean
*/
var deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0 && !deviceIsWindowsPhone;
/**
* iOS requires exceptions.
*
* @type boolean
*/
var deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent) && !deviceIsWindowsPhone;
/**
* iOS 4 requires an exception for select elements.
*
* @type boolean
*/
var deviceIsIOS4 = deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent);
/**
* iOS 6.0-7.* requires the target element to be manually derived
*
* @type boolean
*/
var deviceIsIOSWithBadTarget = deviceIsIOS && (/OS [6-7]_\d/).test(navigator.userAgent);
/**
* BlackBerry requires exceptions.
*
* @type boolean
*/
var deviceIsBlackBerry10 = navigator.userAgent.indexOf('BB10') > 0;
/**
* Determine whether a given element requires a native click.
*
* @param {EventTarget|Element} target Target DOM element
* @returns {boolean} Returns true if the element needs a native click
*/
FastClick.prototype.needsClick = function(target) {
switch (target.nodeName.toLowerCase()) {
// Don't send a synthetic click to disabled inputs (issue #62)
case 'button':
case 'select':
case 'textarea':
if (target.disabled) {
return true;
}
break;
case 'input':
// File inputs need real clicks on iOS 6 due to a browser bug (issue #68)
if ((deviceIsIOS && target.type === 'file') || target.disabled) {
return true;
}
break;
case 'label':
case 'iframe': // iOS8 homescreen apps can prevent events bubbling into frames
case 'video':
return true;
}
return (/\bneedsclick\b/).test(target.className);
};
/**
* Determine whether a given element requires a call to focus to simulate click into element.
*
* @param {EventTarget|Element} target Target DOM element
* @returns {boolean} Returns true if the element requires a call to focus to simulate native click.
*/
FastClick.prototype.needsFocus = function(target) {
switch (target.nodeName.toLowerCase()) {
case 'textarea':
return true;
case 'select':
return !deviceIsAndroid;
case 'input':
switch (target.type) {
case 'button':
case 'checkbox':
case 'file':
case 'image':
case 'radio':
case 'submit':
return false;
}
// No point in attempting to focus disabled inputs
return !target.disabled && !target.readOnly;
default:
return (/\bneedsfocus\b/).test(target.className);
}
};
/**
* Send a click event to the specified element.
*
* @param {EventTarget|Element} targetElement
* @param {Event} event
*/
FastClick.prototype.sendClick = function(targetElement, event) {
var clickEvent, touch;
// On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
if (document.activeElement && document.activeElement !== targetElement) {
document.activeElement.blur();
}
touch = event.changedTouches[0];
// Synthesise a click event, with an extra attribute so it can be tracked
clickEvent = document.createEvent('MouseEvents');
clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
clickEvent.forwardedTouchEvent = true;
targetElement.dispatchEvent(clickEvent);
};
FastClick.prototype.determineEventType = function(targetElement) {
//Issue #159: Android Chrome Select Box does not open with a synthetic click event
if (deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {
return 'mousedown';
}
return 'click';
};
/**
* @param {EventTarget|Element} targetElement
*/
FastClick.prototype.focus = function(targetElement) {
var length;
// Issue #160: on iOS 7, some input elements (e.g. date datetime month) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724.
if (deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time' && targetElement.type !== 'month') {
length = targetElement.value.length;
targetElement.setSelectionRange(length, length);
} else {
targetElement.focus();
}
};
/**
* Check whether the given target element is a child of a scrollable layer and if so, set a flag on it.
*
* @param {EventTarget|Element} targetElement
*/
FastClick.prototype.updateScrollParent = function(targetElement) {
var scrollParent, parentElement;
scrollParent = targetElement.fastClickScrollParent;
// Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the
// target element was moved to another parent.
if (!scrollParent || !scrollParent.contains(targetElement)) {
parentElement = targetElement;
do {
if (parentElement.scrollHeight > parentElement.offsetHeight) {
scrollParent = parentElement;
targetElement.fastClickScrollParent = parentElement;
break;
}
parentElement = parentElement.parentElement;
} while (parentElement);
}
// Always update the scroll top tracker if possible.
if (scrollParent) {
scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;
}
};
/**
* @param {EventTarget} targetElement
* @returns {Element|EventTarget}
*/
FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {
// On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node.
if (eventTarget.nodeType === Node.TEXT_NODE) {
return eventTarget.parentNode;
}
return eventTarget;
};
/**
* On touch start, record the position and scroll offset.
*
* @param {Event} event
* @returns {boolean}
*/
FastClick.prototype.onTouchStart = function(event) {
var targetElement, touch, selection;
// Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111).
if (event.targetTouches.length > 1) {
return true;
}
targetElement = this.getTargetElementFromEventTarget(event.target);
touch = event.targetTouches[0];
if (deviceIsIOS) {
// Only trusted events will deselect text on iOS (issue #49)
selection = window.getSelection();
if (selection.rangeCount && !selection.isCollapsed) {
return true;
}
if (!deviceIsIOS4) {
// Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23):
// when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
// with the same identifier as the touch event that previously triggered the click that triggered the alert.
// Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an
// immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.
// Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,
// which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,
// random integers, it's safe to to continue if the identifier is 0 here.
if (touch.identifier && touch.identifier === this.lastTouchIdentifier) {
event.preventDefault();
return false;
}
this.lastTouchIdentifier = touch.identifier;
// If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:
// 1) the user does a fling scroll on the scrollable layer
// 2) the user stops the fling scroll with another tap
// then the event.target of the last 'touchend' event will be the element that was under the user's finger
// when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check
// is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).
this.updateScrollParent(targetElement);
}
}
this.trackingClick = true;
this.trackingClickStart = event.timeStamp;
this.targetElement = targetElement;
this.touchStartX = touch.pageX;
this.touchStartY = touch.pageY;
// Prevent phantom clicks on fast double-tap (issue #36)
if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
event.preventDefault();
}
return true;
};
/**
* Based on a touchmove event object, check whether the touch has moved past a boundary since it started.
*
* @param {Event} event
* @returns {boolean}
*/
FastClick.prototype.touchHasMoved = function(event) {
var touch = event.changedTouches[0], boundary = this.touchBoundary;
if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {
return true;
}
return false;
};
/**
* Update the last position.
*
* @param {Event} event
* @returns {boolean}
*/
FastClick.prototype.onTouchMove = function(event) {
if (!this.trackingClick) {
return true;
}
// If the touch has moved, cancel the click tracking
if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {
this.trackingClick = false;
this.targetElement = null;
}
return true;
};
/**
* Attempt to find the labelled control for the given label element.
*
* @param {EventTarget|HTMLLabelElement} labelElement
* @returns {Element|null}
*/
FastClick.prototype.findControl = function(labelElement) {
// Fast path for newer browsers supporting the HTML5 control attribute
if (labelElement.control !== undefined) {
return labelElement.control;
}
// All browsers under test that support touch events also support the HTML5 htmlFor attribute
if (labelElement.htmlFor) {
return document.getElementById(labelElement.htmlFor);
}
// If no for attribute exists, attempt to retrieve the first labellable descendant element
// the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label
return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea');
};
/**
* On touch end, determine whether to send a click event at once.
*
* @param {Event} event
* @returns {boolean}
*/
FastClick.prototype.onTouchEnd = function(event) {
var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;
if (!this.trackingClick) {
return true;
}
// Prevent phantom clicks on fast double-tap (issue #36)
if ((event.timeStamp - this.lastClickTime) < this.tapDelay) {
this.cancelNextClick = true;
return true;
}
if ((event.timeStamp - this.trackingClickStart) > this.tapTimeout) {
return true;
}
// Reset to prevent wrong click cancel on input (issue #156).
this.cancelNextClick = false;
this.lastClickTime = event.timeStamp;
trackingClickStart = this.trackingClickStart;
this.trackingClick = false;
this.trackingClickStart = 0;
// On some iOS devices, the targetElement supplied with the event is invalid if the layer
// is performing a transition or scroll, and has to be re-detected manually. Note that
// for this to function correctly, it must be called *after* the event target is checked!
// See issue #57; also filed as rdar://13048589 .
if (deviceIsIOSWithBadTarget) {
touch = event.changedTouches[0];
// In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null
targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
}
targetTagName = targetElement.tagName.toLowerCase();
if (targetTagName === 'label') {
forElement = this.findControl(targetElement);
if (forElement) {
this.focus(targetElement);
if (deviceIsAndroid) {
return false;
}
targetElement = forElement;
}
} else if (this.needsFocus(targetElement)) {
// Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through.
// Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37).
if ((event.timeStamp - trackingClickStart) > 100 || (deviceIsIOS && window.top !== window && targetTagName === 'input')) {
this.targetElement = null;
return false;
}
this.focus(targetElement);
this.sendClick(targetElement, event);
// Select elements need the event to go through on iOS 4, otherwise the selector menu won't open.
// Also this breaks opening selects when VoiceOver is active on iOS6, iOS7 (and possibly others)
if (!deviceIsIOS || targetTagName !== 'select') {
this.targetElement = null;
event.preventDefault();
}
return false;
}
if (deviceIsIOS && !deviceIsIOS4) {
// Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled
// and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).
scrollParent = targetElement.fastClickScrollParent;
if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
return true;
}
}
// Prevent the actual click from going though - unless the target node is marked as requiring
// real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
if (!this.needsClick(targetElement)) {
event.preventDefault();
this.sendClick(targetElement, event);
}
return false;
};
/**
* On touch cancel, stop tracking the click.
*
* @returns {void}
*/
FastClick.prototype.onTouchCancel = function() {
this.trackingClick = false;
this.targetElement = null;
};
/**
* Determine mouse events which should be permitted.
*
* @param {Event} event
* @returns {boolean}
*/
FastClick.prototype.onMouse = function(event) {
// If a target element was never set (because a touch event was never fired) allow the event
if (!this.targetElement) {
return true;
}
if (event.forwardedTouchEvent) {
return true;
}
// Programmatically generated events targeting a specific element should be permitted
if (!event.cancelable) {
return true;
}
// Derive and check the target element to see whether the mouse event needs to be permitted;
// unless explicitly enabled, prevent non-touch click events from triggering actions,
// to prevent ghost/doubleclicks.
if (!this.needsClick(this.targetElement) || this.cancelNextClick) {
// Prevent any user-added listeners declared on FastClick element from being fired.
if (event.stopImmediatePropagation) {
event.stopImmediatePropagation();
} else {
// Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
event.propagationStopped = true;
}
// Cancel the event
event.stopPropagation();
event.preventDefault();
return false;
}
// If the mouse event is permitted, return true for the action to go through.
return true;
};
/**
* On actual clicks, determine whether this is a touch-generated click, a click action occurring
* naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or
* an actual click which should be permitted.
*
* @param {Event} event
* @returns {boolean}
*/
FastClick.prototype.onClick = function(event) {
var permitted;
// It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.
if (this.trackingClick) {
this.targetElement = null;
this.trackingClick = false;
return true;
}
// Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.
if (event.target.type === 'submit' && event.detail === 0) {
return true;
}
permitted = this.onMouse(event);
// Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.
if (!permitted) {
this.targetElement = null;
}
// If clicks are permitted, return true for the action to go through.
return permitted;
};
/**
* Remove all FastClick's event listeners.
*
* @returns {void}
*/
FastClick.prototype.destroy = function() {
var layer = this.layer;
if (deviceIsAndroid) {
layer.removeEventListener('mouseover', this.onMouse, true);
layer.removeEventListener('mousedown', this.onMouse, true);
layer.removeEventListener('mouseup', this.onMouse, true);
}
layer.removeEventListener('click', this.onClick, true);
layer.removeEventListener('touchstart', this.onTouchStart, false);
layer.removeEventListener('touchmove', this.onTouchMove, false);
layer.removeEventListener('touchend', this.onTouchEnd, false);
layer.removeEventListener('touchcancel', this.onTouchCancel, false);
};
/**
* Check whether FastClick is needed.
*
* @param {Element} layer The layer to listen on
*/
FastClick.notNeeded = function(layer) {
var metaViewport;
var chromeVersion;
var blackberryVersion;
var firefoxVersion;
// Devices that don't support touch don't need FastClick
if (typeof window.ontouchstart === 'undefined') {
return true;
}
// Chrome version - zero for other browsers
chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
if (chromeVersion) {
if (deviceIsAndroid) {
metaViewport = document.querySelector('meta[name=viewport]');
if (metaViewport) {
// Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89)
if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
return true;
}
// Chrome 32 and above with width=device-width or less don't need FastClick
if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) {
return true;
}
}
// Chrome desktop doesn't need FastClick (issue #15)
} else {
return true;
}
}
if (deviceIsBlackBerry10) {
blackberryVersion = navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/);
// BlackBerry 10.3+ does not require Fastclick library.
// https://github.com/ftlabs/fastclick/issues/251
if (blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3) {
metaViewport = document.querySelector('meta[name=viewport]');
if (metaViewport) {
// user-scalable=no eliminates click delay.
if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
return true;
}
// width=device-width (or less than device-width) eliminates click delay.
if (document.documentElement.scrollWidth <= window.outerWidth) {
return true;
}
}
}
}
// IE10 with -ms-touch-action: none or manipulation, which disables double-tap-to-zoom (issue #97)
if (layer.style.msTouchAction === 'none' || layer.style.touchAction === 'manipulation') {
return true;
}
// Firefox version - zero for other browsers
firefoxVersion = +(/Firefox\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];
if (firefoxVersion >= 27) {
// Firefox 27+ does not have tap delay if the content is not zoomable - https://bugzilla.mozilla.org/show_bug.cgi?id=922896
metaViewport = document.querySelector('meta[name=viewport]');
if (metaViewport && (metaViewport.content.indexOf('user-scalable=no') !== -1 || document.documentElement.scrollWidth <= window.outerWidth)) {
return true;
}
}
// IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version
// http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx
if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {
return true;
}
return false;
};
/**
* Factory method for creating a FastClick object
*
* @param {Element} layer The layer to listen on
* @param {Object} [options={}] The options to override the defaults
*/
FastClick.attach = function(layer, options) {
return new FastClick(layer, options);
};
if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
// AMD. Register as an anonymous module.
define(function() {
return FastClick;
});
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = FastClick.attach;
module.exports.FastClick = FastClick;
} else {
window.FastClick = FastClick;
}
}());

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,804 @@
/**
* RWD Table with freezing head and columns for jQuery
*
* @author Nick Tsai <myintaer@gmail.com>
* @version 1.3.0
* @see https://github.com/yidas/jquery-freeze-table
*/
(function ($, window) {
'use strict';
/**
* Main object
*
* @param {element} element
* @param {object} options
*/
var FreezeTable = function(element, options) {
// Target element initialization
this.$tableWrapper = $(element).first();
// Options
this.options = options || {};
this.namespace = this.options.namespace || 'freeze-table';
this.callback;
this.scrollBarHeight;
this.shadow;
this.fastMode;
this.backgroundColor;
this.scrollable;
// Caches
this.$table = this.$tableWrapper.children("table");
this.$container = ((typeof this.options.container !== 'undefined') && this.options.container && $(this.options.container).length) ? $(this.options.container) : $(window);
this.$headTableWrap;
this.$columnTableWrap;
this.$columnHeadTableWrap;
this.$scrollBarWrap;
this.fixedNavbarHeight;
this.isWindowScrollX = false;
// Static class names for clone wraps
this.headWrapClass = 'clone-head-table-wrap';
this.columnWrapClass = 'clone-column-table-wrap';
this.columnHeadWrapClass = 'clone-column-head-table-wrap';
this.scrollBarWrapClass = 'clone-scroll-bar-wrap';
this.init();
return this;
}
/**
* Initialization
*/
FreezeTable.prototype.init = function() {
// Element check
if (!this.$table.length) {
throw "The element must contain a table dom";
}
/**
* Update Mode
*/
if (this.options==='update') {
this.destroy();
this.options = this.$tableWrapper.data('freeze-table-data');
}
else if (this.options==='resize') {
this.options = this.$tableWrapper.data('freeze-table-data');
// Get selected FreezeTable's namespace
this.namespace = this.options.namespace || this.namespace;
this.resize();
// Skip init for better performance usage
return;
}
else {
// Save to DOM data
this.$tableWrapper.data('freeze-table-data', this.options);
}
/**
* Options Setting
*/
var options = this.options;
var freezeHead = (typeof options.freezeHead !== 'undefined') ? options.freezeHead : true;
var freezeColumn = (typeof options.freezeColumn !== 'undefined') ? options.freezeColumn : true;
var freezeColumnHead = (typeof options.freezeColumnHead !== 'undefined') ? options.freezeColumnHead : true;
var scrollBar = (typeof options.scrollBar !== 'undefined') ? options.scrollBar : false;
var fixedNavbar = options.fixedNavbar || '.navbar-fixed-top';
var callback = options.callback || null;
this.namespace = this.options.namespace || this.namespace;
// Default to get window scroll bar height
this.scrollBarHeight = ($.isNumeric(options.scrollBarHeight)) ? options.scrollBarHeight : (window.innerWidth - document.documentElement.clientWidth);
this.shadow = (typeof options.shadow !== 'undefined') ? options.shadow : false;
this.fastMode = (typeof options.fastMode !== 'undefined') ? options.fastMode : false;
this.backgroundColor = (typeof options.backgroundColor !== 'undefined') ? options.backgroundColor : 'white';
this.scrollable = (typeof options.scrollable !== 'undefined') ? options.scrollable : false;
// Get navbar height for keeping fixed navbar
this.fixedNavbarHeight = (fixedNavbar) ? $(fixedNavbar).outerHeight() || 0 : 0;
// Check existence
if (this.isInit()) {
this.destroy();
}
// Release height of the table wrapper
if (!this.scrollable) {
this.$tableWrapper.css('height', '100%')
.css('min-height', '100%')
.css('max-height', '100%');
}
/**
* Building
*/
// Switch for freezeHead
if (freezeHead) {
this.buildHeadTable();
}
// Switch for freezeColumn
if (freezeColumn) {
this.buildColumnTable();
// X scroll bar
this.$tableWrapper.css('overflow-x', 'scroll');
}
// Switch for freezeColumnHead
if (freezeColumnHead && freezeHead && freezeColumn) {
this.buildColumnHeadTable();
}
// Switch for scrollBar
if (scrollBar) {
this.buildScrollBar();
}
// Body scroll-x prevention
var detectWindowScroll = (function (){
// If body scroll-x is opened, close library to prevent Invalid usage
if (this.$container.scrollLeft() > 0) {
// Mark
this.isWindowScrollX = true;
// Hide all components
if (this.$headTableWrap) {
this.$headTableWrap.css('visibility', 'hidden');
}
if (this.$columnTableWrap) {
this.$columnTableWrap.css('visibility', 'hidden');
}
if (this.$columnHeadTableWrap) {
this.$columnHeadTableWrap.css('visibility', 'hidden');
}
if (this.$scrollBarWrap) {
this.$scrollBarWrap.css('visibility', 'hidden');
}
} else {
// Unmark
this.isWindowScrollX = false;
}
}).bind(this);
// Listener of Body scroll-x prevention
this.$container.on('scroll.'+this.namespace, function () {
detectWindowScroll();
});
// Initialization
this.resize();
// Callback
if (typeof callback === 'function') {
callback();
}
}
/**
* Freeze thead table
*/
FreezeTable.prototype.buildHeadTable = function() {
var that = this;
// Clone the table as Fixed thead
var $headTable = this.clone(this.$table);
// Fast Mode
if (this.fastMode) {
var $headTable = this.simplifyHead($headTable);
}
var headWrapStyles = this.options.headWrapStyles || null;
// Wrap the Fixed Column table
this.$headTableWrap = $('<div class="'+this.headWrapClass+'"></div>')
.append($headTable)
.css('position', 'fixed')
.css('overflow', 'hidden')
.css('visibility', 'hidden')
.css('top', 0 + this.fixedNavbarHeight)
.css('z-index', 2);
// Shadow option
if (this.shadow) {
this.$headTableWrap.css('box-shadow', '0px 6px 10px -5px rgba(159, 159, 160, 0.8)');
}
// Styles option
if (headWrapStyles && typeof headWrapStyles === "object") {
$.each(headWrapStyles, function(key, value) {
that.$headTableWrap.css(key, value);
});
}
// Add into target table wrap
this.$tableWrapper.append(this.$headTableWrap);
/**
* Listener - Table scroll for effecting Freeze Column
*/
this.$tableWrapper.on('scroll.'+this.namespace, function() {
// this.$headTableWrap.css('left', this.$table.offset().left);
that.$headTableWrap.scrollLeft($(this).scrollLeft());
});
// Scrollable option
if (this.scrollable) {
var handler = function (window, that) {
var top = that.$tableWrapper.offset().top;
// Detect Current container's top is in the table scope
if (that.$tableWrapper.scrollTop() > 0 && top > that.fixedNavbarHeight) {
that.$headTableWrap.offset({top: top});
that.$headTableWrap.css('visibility', 'visible');
} else {
that.$headTableWrap.css('visibility', 'hidden');
}
}
/**
* Listener - Window scroll for effecting freeze head table
*/
this.$tableWrapper.on('scroll.'+this.namespace, function() {
handler(window, that);
});
this.$container.on('scroll.'+this.namespace, function() {
handler(window, that);
});
}
// Default with window container
else if ($.isWindow(that.$container.get(0))) {
/**
* Listener - Window scroll for effecting freeze head table
*/
this.$container.on('scroll.'+this.namespace, function() {
// Current container's top position
var topPosition = that.$container.scrollTop() + that.fixedNavbarHeight;
var tableTop = that.$table.offset().top - 1;
// Detect Current container's top is in the table scope
if (tableTop - 1 <= topPosition && (tableTop + that.$table.outerHeight() - 1) >= topPosition) {
that.$headTableWrap.css('visibility', 'visible');
} else {
that.$headTableWrap.css('visibility', 'hidden');
}
});
}
// Container setting
else {
/**
* Listener - Window scroll for effecting freeze head table
*/
this.$container.on('scroll.'+this.namespace, function() {
var windowTop = $(window).scrollTop();
var tableTop = that.$table.offset().top - 1;
// Detect Current container's top is in the table scope
if (tableTop <= windowTop && (tableTop + that.$table.outerHeight() - 1) >= windowTop) {
that.$headTableWrap.offset({top: windowTop});
that.$headTableWrap.css('visibility', 'visible');
} else {
that.$headTableWrap.css('visibility', 'hidden');
}
});
}
/**
* Listener - Window resize for effecting freeze head table
*/
this.$container.on('resize.'+this.namespace, function() {
// Scrollable check and prevention
var headTableWrapWidth = (that.scrollable) ? that.$tableWrapper.width() - that.scrollBarHeight : that.$tableWrapper.width();
headTableWrapWidth = (headTableWrapWidth > 0) ? headTableWrapWidth : that.$tableWrapper.width();
that.$headTableWrap.css('width', headTableWrapWidth);
that.$headTableWrap.css('height', that.$table.find("thead").outerHeight());
});
}
/**
* Freeze column table
*/
FreezeTable.prototype.buildColumnTable = function() {
var that = this;
/**
* Setting
*/
var columnWrapStyles = this.options.columnWrapStyles || null;
var columnNum = this.options.columnNum || 1;
var columnKeep = (typeof this.options.columnKeep !== 'undefined') ? this.options.columnKeep : false;
// Shadow option
var defaultColumnBorderWidth = (this.shadow) ? 0 : 1;
var columnBorderWidth = (typeof this.options.columnBorderWidth !== 'undefined') ? this.options.columnBorderWidth : defaultColumnBorderWidth;
// Clone the table as Fixed Column table
var $columnTable = this.clone(this.$table);
// Wrap the Fixed Column table
this.$columnTableWrap = $('<div class="'+this.columnWrapClass+'"></div>')
.append($columnTable)
.css('position', 'fixed')
.css('overflow', 'hidden')
.css('visibility', 'hidden')
.css('z-index', 1);
// Shadow option
if (this.shadow) {
this.$columnTableWrap.css('box-shadow', '6px 0px 10px -5px rgba(159, 159, 160, 0.8)');
}
// Styles option
if (columnWrapStyles && typeof columnWrapStyles === "object") {
$.each(columnWrapStyles, function(key, value) {
that.$columnTableWrap.css(key, value);
});
}
// Scrollable
if (this.scrollable) {
// Scrollable check and prevention
var columnTableWrapHeight = this.$tableWrapper.height() - this.scrollBarHeight;
columnTableWrapHeight = (columnTableWrapHeight > 0) ? columnTableWrapHeight : this.$tableWrapper.height();
this.$columnTableWrap.height(columnTableWrapHeight);
}
// Add into target table wrap
this.$tableWrapper.append(this.$columnTableWrap);
/**
* localize the column wrap to current top
*/
var localizeWrap = function () {
that.$columnTableWrap.offset({top: that.$tableWrapper.offset().top});
}
// Column keep option
if (columnKeep) {
this.$columnTableWrap.css('visibility', 'visible');
} else {
// Scrollable option
if (that.scrollable) {
/**
* Listener - Table scroll for effecting Freeze Column
*/
this.$tableWrapper.on('scroll.'+this.namespace, function() {
// Detect for horizontal scroll
if ($(this).scrollLeft() > 0) {
// Scrollable localization
that.$columnTableWrap.scrollTop(that.$tableWrapper.scrollTop());
that.$columnTableWrap.css('visibility', 'visible');
} else {
that.$columnTableWrap.css('visibility', 'hidden');
}
});
} else {
/**
* Listener - Table scroll for effecting Freeze Column
*/
this.$tableWrapper.on('scroll.'+this.namespace, function() {
// Disable while isWindowScrollX
if (that.isWindowScrollX)
return;
// Detect for horizontal scroll
if ($(this).scrollLeft() > 0) {
that.$columnTableWrap.css('visibility', 'visible');
} else {
that.$columnTableWrap.css('visibility', 'hidden');
}
});
}
}
/**
* Listener - Window resize for effecting tables
*/
this.$container.on('resize.'+this.namespace, function() {
// Follows origin table's width
$columnTable.width(that.$table.width());
/**
* Dynamic column calculation
*/
// Get width by fixed column with number setting
var width = 0 + columnBorderWidth;
for (var i = 1; i <= columnNum; i++) {
// th/td detection
var th = that.$table.find('th:nth-child('+i+')').outerWidth();
var addWidth = (th > 0) ? th : that.$table.find('td:nth-child('+i+')').outerWidth();
width += addWidth;
}
that.$columnTableWrap.width(width);
localizeWrap();
});
/**
* Listener - Window scroll for effecting freeze column table
*/
this.$container.on('scroll.'+this.namespace, function() {
localizeWrap();
});
}
/**
* Freeze column thead table
*/
FreezeTable.prototype.buildColumnHeadTable = function() {
var that = this;
// Clone head table wrap
this.$columnHeadTableWrap = this.clone(this.$headTableWrap);
// Fast Mode
if (this.fastMode) {
this.$columnHeadTableWrap = this.simplifyHead(this.$columnHeadTableWrap);
}
var columnHeadWrapStyles = this.options.columnHeadWrapStyles || null;
this.$columnHeadTableWrap.removeClass(this.namespace)
.addClass(this.columnHeadWrapClass)
.css('z-index', 3);
// Shadow option
if (this.shadow) {
this.$columnHeadTableWrap.css('box-shadow', 'none');
}
// Styles option
if (columnHeadWrapStyles && typeof columnHeadWrapStyles === "object") {
$.each(columnHeadWrapStyles, function(key, value) {
this.$columnHeadTableWrap.css(key, value);
});
}
// Add into target table wrap
this.$tableWrapper.append(this.$columnHeadTableWrap);
// Scrollable option
if (this.scrollable) {
var detect = function () {
var top = that.$tableWrapper.offset().top;
// Detect Current container's top is in the table scope
if (that.$tableWrapper.scrollTop() > 0 && top > that.fixedNavbarHeight) {
that.$columnHeadTableWrap.offset({top: top});
that.$columnHeadTableWrap.css('visibility', 'visible');
} else {
that.$columnHeadTableWrap.css('visibility', 'hidden');
}
}
/**
* Listener - Window scroll for effecting freeze head table
*/
$(this.$tableWrapper).on('scroll.'+this.namespace, function() {
detect();
});
}
// Default with window container
else if ($.isWindow(this.$container.get(0))) {
var detect = function () {
// Current container's top position
var topPosition = that.$container.scrollTop() + that.fixedNavbarHeight;
var tableTop = that.$table.offset().top - 1;
// Detect Current container's top is in the table scope
if (tableTop - 1 <= topPosition && (tableTop + that.$table.outerHeight() - 1) >= topPosition && that.$tableWrapper.scrollLeft() > 0) {
that.$columnHeadTableWrap.css('visibility', 'visible');
} else {
that.$columnHeadTableWrap.css('visibility', 'hidden');
}
}
}
// Container setting
else {
var detect = function () {
var windowTop = $(window).scrollTop();
var tableTop = that.$table.offset().top - 1;
// Detect Current container's top is in the table scope
if (tableTop <= windowTop && (tableTop + that.$table.outerHeight() - 1) >= windowTop && that.$tableWrapper.scrollLeft() > 0) {
that.$columnHeadTableWrap.offset({top: windowTop});
that.$columnHeadTableWrap.css('visibility', 'visible');
} else {
that.$columnHeadTableWrap.css('visibility', 'hidden');
}
}
}
/**
* Listener - Window scroll for effecting Freeze column-head table
*/
this.$container.on('scroll.'+this.namespace, function() {
detect();
});
/**
* Listener - Table scroll for effecting Freeze column-head table
*/
this.$tableWrapper.on('scroll.'+this.namespace, function() {
// Disable while isWindowScrollX
if (that.isWindowScrollX)
return;
detect();
});
/**
* Listener - Window resize for effecting freeze column-head table
*/
this.$container.on('resize.'+this.namespace, function() {
// Table synchronism
that.$columnHeadTableWrap.find("> table").css('width', that.$table.width());
that.$columnHeadTableWrap.css('width', that.$columnTableWrap.width());
that.$columnHeadTableWrap.css('height', that.$table.find("thead").outerHeight());
});
}
/**
* Freeze scroll bar
*/
FreezeTable.prototype.buildScrollBar = function() {
var that = this;
var theadHeight = this.$table.find("thead").outerHeight();
// Scroll wrap container
var $scrollBarContainer = $('<div class="'+this.scrollBarWrapClass+'"></div>')
.css('width', this.$table.width())
.css('height', 1);
// Wrap the Fixed Column table
this.$scrollBarWrap = $('<div class="'+this.scrollBarWrapClass+'"></div>')
.css('position', 'fixed')
.css('overflow-x', 'scroll')
.css('visibility', 'hidden')
.css('bottom', 0)
.css('z-index', 2)
.css('width', this.$tableWrapper.width())
.css('height', this.scrollBarHeight);
// Add into target table wrap
this.$scrollBarWrap.append($scrollBarContainer);
this.$tableWrapper.append(this.$scrollBarWrap);
/**
* Listener - Freeze scroll bar effected Table
*/
this.$scrollBarWrap.on('scroll.'+this.namespace, function() {
that.$tableWrapper.scrollLeft($(this).scrollLeft());
});
/**
* Listener - Table scroll for effecting Freeze scroll bar
*/
this.$tableWrapper.on('scroll.'+this.namespace, function() {
// this.$headTableWrap.css('left', $table.offset().left);
that.$scrollBarWrap.scrollLeft($(this).scrollLeft());
});
/**
* Listener - Window scroll for effecting scroll bar
*/
this.$container.on('scroll.'+this.namespace, function() {
// Current container's top position
var bottomPosition = that.$container.scrollTop() + that.$container.height() - theadHeight + that.fixedNavbarHeight;
// Detect Current container's top is in the table scope
if (that.$table.offset().top - 1 <= bottomPosition && (that.$table.offset().top + that.$table.outerHeight() - 1) >= bottomPosition) {
that.$scrollBarWrap.css('visibility', 'visible');
} else {
that.$scrollBarWrap.css('visibility', 'hidden');
}
});
/**
* Listener - Window resize for effecting scroll bar
*/
this.$container.on('resize.'+this.namespace, function() {
// Update width
$scrollBarContainer.css('width', that.$table.width())
// Update Wrap
that.$scrollBarWrap.css('width', that.$tableWrapper.width());
});
}
/**
* Clone element
*
* @param {element} element
*/
FreezeTable.prototype.clone = function (element) {
var $clone = $(element).clone()
.removeAttr('id') // Remove ID
// Bootstrap background-color transparent problem
if (this.backgroundColor) {
$clone.css('background-color', this.backgroundColor);
}
return $clone;
}
/**
* simplify cloned head table
*
* @param {element} table Table element
*/
FreezeTable.prototype.simplifyHead = function (table) {
var that = this;
var $headTable = $(table);
// Remove non-display DOM but keeping first row for accuracy
$headTable.find("> tr, > tbody > tr, tfoot > tr").not(':first').remove();
// Each th/td width synchronism
$.each($headTable.find("> thead > tr:nth-child(1) >"), function (key, value) {
var width = that.$table.find("> thead > tr:nth-child(1) > :nth-child("+parseInt(key+1)+")").outerWidth();
$(this).css('width', width);
});
return $headTable;
}
/**
* Detect is already initialized
*/
FreezeTable.prototype.isInit = function() {
// Check existence DOM
if (this.$tableWrapper.find("."+this.headWrapClass).length)
return true;
if (this.$tableWrapper.find("."+this.columnWrapClass).length)
return true;
if (this.$tableWrapper.find("."+this.columnHeadWrapClass).length)
return true;
if (this.$tableWrapper.find("."+this.scrollBarWrapClass).length)
return true;
return false;
}
/**
* Unbind all events by same namespace
*/
FreezeTable.prototype.unbind = function() {
this.$container.off('resize.'+this.namespace);
this.$container.off('scroll.'+this.namespace);
this.$tableWrapper.off('scroll.'+this.namespace);
}
/**
* Destroy Freeze Table by same namespace
*/
FreezeTable.prototype.destroy = function() {
this.unbind();
this.$tableWrapper.find("."+this.headWrapClass).remove();
this.$tableWrapper.find("."+this.columnWrapClass).remove();
this.$tableWrapper.find("."+this.columnHeadWrapClass).remove();
this.$tableWrapper.find("."+this.scrollBarWrapClass).remove();
}
/**
* Resize trigger for current same namespace
*/
FreezeTable.prototype.resize = function() {
this.$container.trigger('resize.'+this.namespace);
this.$container.trigger('scroll.'+this.namespace);
this.$tableWrapper.trigger('scroll.'+this.namespace);
return true;
}
/**
* Update for Dynamic Content
*/
FreezeTable.prototype.update = function() {
// Same as re-new object
this.options = 'update';
this.init();
return this;
}
/**
* Interface
*/
// Class for single element
window.FreezeTable = FreezeTable;
// jQuery interface
$.fn.freezeTable = function (options) {
// Single/Multiple mode
if (this.length === 1) {
return new FreezeTable(this, options)
}
else if (this.length > 1) {
var result = [];
// Multiple elements bundle
this.each(function () {
result.push(new FreezeTable(this, options));
});
return result;
}
return false;
}
})(jQuery, window);

View File

@ -0,0 +1,10 @@
/*! iCheck v1.0.1 by Damir Sultanov, http://git.io/arlzeA, MIT Licensed */
(function(h){function F(a,b,d){var c=a[0],e=/er/.test(d)?m:/bl/.test(d)?s:l,f=d==H?{checked:c[l],disabled:c[s],indeterminate:"true"==a.attr(m)||"false"==a.attr(w)}:c[e];if(/^(ch|di|in)/.test(d)&&!f)D(a,e);else if(/^(un|en|de)/.test(d)&&f)t(a,e);else if(d==H)for(e in f)f[e]?D(a,e,!0):t(a,e,!0);else if(!b||"toggle"==d){if(!b)a[p]("ifClicked");f?c[n]!==u&&t(a,e):D(a,e)}}function D(a,b,d){var c=a[0],e=a.parent(),f=b==l,A=b==m,B=b==s,K=A?w:f?E:"enabled",p=k(a,K+x(c[n])),N=k(a,b+x(c[n]));if(!0!==c[b]){if(!d&&
b==l&&c[n]==u&&c.name){var C=a.closest("form"),r='input[name="'+c.name+'"]',r=C.length?C.find(r):h(r);r.each(function(){this!==c&&h(this).data(q)&&t(h(this),b)})}A?(c[b]=!0,c[l]&&t(a,l,"force")):(d||(c[b]=!0),f&&c[m]&&t(a,m,!1));L(a,f,b,d)}c[s]&&k(a,y,!0)&&e.find("."+I).css(y,"default");e[v](N||k(a,b)||"");B?e.attr("aria-disabled","true"):e.attr("aria-checked",A?"mixed":"true");e[z](p||k(a,K)||"")}function t(a,b,d){var c=a[0],e=a.parent(),f=b==l,h=b==m,q=b==s,p=h?w:f?E:"enabled",t=k(a,p+x(c[n])),
u=k(a,b+x(c[n]));if(!1!==c[b]){if(h||!d||"force"==d)c[b]=!1;L(a,f,p,d)}!c[s]&&k(a,y,!0)&&e.find("."+I).css(y,"pointer");e[z](u||k(a,b)||"");q?e.attr("aria-disabled","false"):e.attr("aria-checked","false");e[v](t||k(a,p)||"")}function M(a,b){if(a.data(q)){a.parent().html(a.attr("style",a.data(q).s||""));if(b)a[p](b);a.off(".i").unwrap();h(G+'[for="'+a[0].id+'"]').add(a.closest(G)).off(".i")}}function k(a,b,d){if(a.data(q))return a.data(q).o[b+(d?"":"Class")]}function x(a){return a.charAt(0).toUpperCase()+
a.slice(1)}function L(a,b,d,c){if(!c){if(b)a[p]("ifToggled");a[p]("ifChanged")[p]("if"+x(d))}}var q="iCheck",I=q+"-helper",u="radio",l="checked",E="un"+l,s="disabled",w="determinate",m="in"+w,H="update",n="type",v="addClass",z="removeClass",p="trigger",G="label",y="cursor",J=/ipad|iphone|ipod|android|blackberry|windows phone|opera mini|silk/i.test(navigator.userAgent);h.fn[q]=function(a,b){var d='input[type="checkbox"], input[type="'+u+'"]',c=h(),e=function(a){a.each(function(){var a=h(this);c=a.is(d)?
c.add(a):c.add(a.find(d))})};if(/^(check|uncheck|toggle|indeterminate|determinate|disable|enable|update|destroy)$/i.test(a))return a=a.toLowerCase(),e(this),c.each(function(){var c=h(this);"destroy"==a?M(c,"ifDestroyed"):F(c,!0,a);h.isFunction(b)&&b()});if("object"!=typeof a&&a)return this;var f=h.extend({checkedClass:l,disabledClass:s,indeterminateClass:m,labelHover:!0,aria:!1},a),k=f.handle,B=f.hoverClass||"hover",x=f.focusClass||"focus",w=f.activeClass||"active",y=!!f.labelHover,C=f.labelHoverClass||
"hover",r=(""+f.increaseArea).replace("%","")|0;if("checkbox"==k||k==u)d='input[type="'+k+'"]';-50>r&&(r=-50);e(this);return c.each(function(){var a=h(this);M(a);var c=this,b=c.id,e=-r+"%",d=100+2*r+"%",d={position:"absolute",top:e,left:e,display:"block",width:d,height:d,margin:0,padding:0,background:"#fff",border:0,opacity:0},e=J?{position:"absolute",visibility:"hidden"}:r?d:{position:"absolute",opacity:0},k="checkbox"==c[n]?f.checkboxClass||"icheckbox":f.radioClass||"i"+u,m=h(G+'[for="'+b+'"]').add(a.closest(G)),
A=!!f.aria,E=q+"-"+Math.random().toString(36).replace("0.",""),g='<div class="'+k+'" '+(A?'role="'+c[n]+'" ':"");m.length&&A&&m.each(function(){g+='aria-labelledby="';this.id?g+=this.id:(this.id=E,g+=E);g+='"'});g=a.wrap(g+"/>")[p]("ifCreated").parent().append(f.insert);d=h('<ins class="'+I+'"/>').css(d).appendTo(g);a.data(q,{o:f,s:a.attr("style")}).css(e);f.inheritClass&&g[v](c.className||"");f.inheritID&&b&&g.attr("id",q+"-"+b);"static"==g.css("position")&&g.css("position","relative");F(a,!0,H);
if(m.length)m.on("click.i mouseover.i mouseout.i touchbegin.i touchend.i",function(b){var d=b[n],e=h(this);if(!c[s]){if("click"==d){if(h(b.target).is("a"))return;F(a,!1,!0)}else y&&(/ut|nd/.test(d)?(g[z](B),e[z](C)):(g[v](B),e[v](C)));if(J)b.stopPropagation();else return!1}});a.on("click.i focus.i blur.i keyup.i keydown.i keypress.i",function(b){var d=b[n];b=b.keyCode;if("click"==d)return!1;if("keydown"==d&&32==b)return c[n]==u&&c[l]||(c[l]?t(a,l):D(a,l)),!1;if("keyup"==d&&c[n]==u)!c[l]&&D(a,l);else if(/us|ur/.test(d))g["blur"==
d?z:v](x)});d.on("click mousedown mouseup mouseover mouseout touchbegin.i touchend.i",function(b){var d=b[n],e=/wn|up/.test(d)?w:B;if(!c[s]){if("click"==d)F(a,!1,!0);else{if(/wn|er|in/.test(d))g[v](e);else g[z](e+" "+w);if(m.length&&y&&e==B)m[/ut|nd/.test(d)?z:v](C)}if(J)b.stopPropagation();else return!1}})})}})(window.jQuery||window.Zepto);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,160 @@
/*! DataTables 1.10.7
* ©2008-2015 SpryMedia Ltd - datatables.net/license
*/
(function(Ea,Q,k){var P=function(h){function W(a){var b,c,e={};h.each(a,function(d){if((b=d.match(/^([^A-Z]+?)([A-Z])/))&&-1!=="a aa ai ao as b fn i m o s ".indexOf(b[1]+" "))c=d.replace(b[0],b[2].toLowerCase()),e[c]=d,"o"===b[1]&&W(a[d])});a._hungarianMap=e}function H(a,b,c){a._hungarianMap||W(a);var e;h.each(b,function(d){e=a._hungarianMap[d];if(e!==k&&(c||b[e]===k))"o"===e.charAt(0)?(b[e]||(b[e]={}),h.extend(!0,b[e],b[d]),H(a[e],b[e],c)):b[e]=b[d]})}function P(a){var b=m.defaults.oLanguage,c=a.sZeroRecords;
!a.sEmptyTable&&(c&&"No data available in table"===b.sEmptyTable)&&E(a,a,"sZeroRecords","sEmptyTable");!a.sLoadingRecords&&(c&&"Loading..."===b.sLoadingRecords)&&E(a,a,"sZeroRecords","sLoadingRecords");a.sInfoThousands&&(a.sThousands=a.sInfoThousands);(a=a.sDecimal)&&db(a)}function eb(a){A(a,"ordering","bSort");A(a,"orderMulti","bSortMulti");A(a,"orderClasses","bSortClasses");A(a,"orderCellsTop","bSortCellsTop");A(a,"order","aaSorting");A(a,"orderFixed","aaSortingFixed");A(a,"paging","bPaginate");
A(a,"pagingType","sPaginationType");A(a,"pageLength","iDisplayLength");A(a,"searching","bFilter");if(a=a.aoSearchCols)for(var b=0,c=a.length;b<c;b++)a[b]&&H(m.models.oSearch,a[b])}function fb(a){A(a,"orderable","bSortable");A(a,"orderData","aDataSort");A(a,"orderSequence","asSorting");A(a,"orderDataType","sortDataType");var b=a.aDataSort;b&&!h.isArray(b)&&(a.aDataSort=[b])}function gb(a){var a=a.oBrowser,b=h("<div/>").css({position:"absolute",top:0,left:0,height:1,width:1,overflow:"hidden"}).append(h("<div/>").css({position:"absolute",
top:1,left:1,width:100,overflow:"scroll"}).append(h('<div class="test"/>').css({width:"100%",height:10}))).appendTo("body"),c=b.find(".test");a.bScrollOversize=100===c[0].offsetWidth;a.bScrollbarLeft=1!==Math.round(c.offset().left);b.remove()}function hb(a,b,c,e,d,f){var g,j=!1;c!==k&&(g=c,j=!0);for(;e!==d;)a.hasOwnProperty(e)&&(g=j?b(g,a[e],e,a):a[e],j=!0,e+=f);return g}function Fa(a,b){var c=m.defaults.column,e=a.aoColumns.length,c=h.extend({},m.models.oColumn,c,{nTh:b?b:Q.createElement("th"),sTitle:c.sTitle?
c.sTitle:b?b.innerHTML:"",aDataSort:c.aDataSort?c.aDataSort:[e],mData:c.mData?c.mData:e,idx:e});a.aoColumns.push(c);c=a.aoPreSearchCols;c[e]=h.extend({},m.models.oSearch,c[e]);ka(a,e,h(b).data())}function ka(a,b,c){var b=a.aoColumns[b],e=a.oClasses,d=h(b.nTh);if(!b.sWidthOrig){b.sWidthOrig=d.attr("width")||null;var f=(d.attr("style")||"").match(/width:\s*(\d+[pxem%]+)/);f&&(b.sWidthOrig=f[1])}c!==k&&null!==c&&(fb(c),H(m.defaults.column,c),c.mDataProp!==k&&!c.mData&&(c.mData=c.mDataProp),c.sType&&
(b._sManualType=c.sType),c.className&&!c.sClass&&(c.sClass=c.className),h.extend(b,c),E(b,c,"sWidth","sWidthOrig"),c.iDataSort!==k&&(b.aDataSort=[c.iDataSort]),E(b,c,"aDataSort"));var g=b.mData,j=R(g),i=b.mRender?R(b.mRender):null,c=function(a){return"string"===typeof a&&-1!==a.indexOf("@")};b._bAttrSrc=h.isPlainObject(g)&&(c(g.sort)||c(g.type)||c(g.filter));b.fnGetData=function(a,b,c){var e=j(a,b,k,c);return i&&b?i(e,b,a,c):e};b.fnSetData=function(a,b,c){return S(g)(a,b,c)};"number"!==typeof g&&
(a._rowReadObject=!0);a.oFeatures.bSort||(b.bSortable=!1,d.addClass(e.sSortableNone));a=-1!==h.inArray("asc",b.asSorting);c=-1!==h.inArray("desc",b.asSorting);!b.bSortable||!a&&!c?(b.sSortingClass=e.sSortableNone,b.sSortingClassJUI=""):a&&!c?(b.sSortingClass=e.sSortableAsc,b.sSortingClassJUI=e.sSortJUIAscAllowed):!a&&c?(b.sSortingClass=e.sSortableDesc,b.sSortingClassJUI=e.sSortJUIDescAllowed):(b.sSortingClass=e.sSortable,b.sSortingClassJUI=e.sSortJUI)}function X(a){if(!1!==a.oFeatures.bAutoWidth){var b=
a.aoColumns;Ga(a);for(var c=0,e=b.length;c<e;c++)b[c].nTh.style.width=b[c].sWidth}b=a.oScroll;(""!==b.sY||""!==b.sX)&&Y(a);w(a,null,"column-sizing",[a])}function la(a,b){var c=Z(a,"bVisible");return"number"===typeof c[b]?c[b]:null}function $(a,b){var c=Z(a,"bVisible"),c=h.inArray(b,c);return-1!==c?c:null}function aa(a){return Z(a,"bVisible").length}function Z(a,b){var c=[];h.map(a.aoColumns,function(a,d){a[b]&&c.push(d)});return c}function Ha(a){var b=a.aoColumns,c=a.aoData,e=m.ext.type.detect,d,
f,g,j,i,h,l,q,n;d=0;for(f=b.length;d<f;d++)if(l=b[d],n=[],!l.sType&&l._sManualType)l.sType=l._sManualType;else if(!l.sType){g=0;for(j=e.length;g<j;g++){i=0;for(h=c.length;i<h;i++){n[i]===k&&(n[i]=x(a,i,d,"type"));q=e[g](n[i],a);if(!q&&g!==e.length-1)break;if("html"===q)break}if(q){l.sType=q;break}}l.sType||(l.sType="string")}}function ib(a,b,c,e){var d,f,g,j,i,o,l=a.aoColumns;if(b)for(d=b.length-1;0<=d;d--){o=b[d];var q=o.targets!==k?o.targets:o.aTargets;h.isArray(q)||(q=[q]);f=0;for(g=q.length;f<
g;f++)if("number"===typeof q[f]&&0<=q[f]){for(;l.length<=q[f];)Fa(a);e(q[f],o)}else if("number"===typeof q[f]&&0>q[f])e(l.length+q[f],o);else if("string"===typeof q[f]){j=0;for(i=l.length;j<i;j++)("_all"==q[f]||h(l[j].nTh).hasClass(q[f]))&&e(j,o)}}if(c){d=0;for(a=c.length;d<a;d++)e(d,c[d])}}function K(a,b,c,e){var d=a.aoData.length,f=h.extend(!0,{},m.models.oRow,{src:c?"dom":"data"});f._aData=b;a.aoData.push(f);for(var b=a.aoColumns,f=0,g=b.length;f<g;f++)c&&Ia(a,d,f,x(a,d,f)),b[f].sType=null;a.aiDisplayMaster.push(d);
(c||!a.oFeatures.bDeferRender)&&Ja(a,d,c,e);return d}function ma(a,b){var c;b instanceof h||(b=h(b));return b.map(function(b,d){c=na(a,d);return K(a,c.data,d,c.cells)})}function x(a,b,c,e){var d=a.iDraw,f=a.aoColumns[c],g=a.aoData[b]._aData,j=f.sDefaultContent,c=f.fnGetData(g,e,{settings:a,row:b,col:c});if(c===k)return a.iDrawError!=d&&null===j&&(I(a,0,"Requested unknown parameter "+("function"==typeof f.mData?"{function}":"'"+f.mData+"'")+" for row "+b,4),a.iDrawError=d),j;if((c===g||null===c)&&
null!==j)c=j;else if("function"===typeof c)return c.call(g);return null===c&&"display"==e?"":c}function Ia(a,b,c,e){a.aoColumns[c].fnSetData(a.aoData[b]._aData,e,{settings:a,row:b,col:c})}function Ka(a){return h.map(a.match(/(\\.|[^\.])+/g),function(a){return a.replace(/\\./g,".")})}function R(a){if(h.isPlainObject(a)){var b={};h.each(a,function(a,c){c&&(b[a]=R(c))});return function(a,c,f,g){var j=b[c]||b._;return j!==k?j(a,c,f,g):a}}if(null===a)return function(a){return a};if("function"===typeof a)return function(b,
c,f,g){return a(b,c,f,g)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var c=function(a,b,f){var g,j;if(""!==f){j=Ka(f);for(var i=0,h=j.length;i<h;i++){f=j[i].match(ba);g=j[i].match(T);if(f){j[i]=j[i].replace(ba,"");""!==j[i]&&(a=a[j[i]]);g=[];j.splice(0,i+1);j=j.join(".");i=0;for(h=a.length;i<h;i++)g.push(c(a[i],b,j));a=f[0].substring(1,f[0].length-1);a=""===a?g:g.join(a);break}else if(g){j[i]=j[i].replace(T,"");a=a[j[i]]();continue}if(null===a||a[j[i]]===
k)return k;a=a[j[i]]}}return a};return function(b,d){return c(b,d,a)}}return function(b){return b[a]}}function S(a){if(h.isPlainObject(a))return S(a._);if(null===a)return function(){};if("function"===typeof a)return function(b,e,d){a(b,"set",e,d)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var b=function(a,e,d){var d=Ka(d),f;f=d[d.length-1];for(var g,j,i=0,h=d.length-1;i<h;i++){g=d[i].match(ba);j=d[i].match(T);if(g){d[i]=d[i].replace(ba,"");a[d[i]]=[];
f=d.slice();f.splice(0,i+1);g=f.join(".");j=0;for(h=e.length;j<h;j++)f={},b(f,e[j],g),a[d[i]].push(f);return}j&&(d[i]=d[i].replace(T,""),a=a[d[i]](e));if(null===a[d[i]]||a[d[i]]===k)a[d[i]]={};a=a[d[i]]}if(f.match(T))a[f.replace(T,"")](e);else a[f.replace(ba,"")]=e};return function(c,e){return b(c,e,a)}}return function(b,e){b[a]=e}}function La(a){return D(a.aoData,"_aData")}function oa(a){a.aoData.length=0;a.aiDisplayMaster.length=0;a.aiDisplay.length=0}function pa(a,b,c){for(var e=-1,d=0,f=a.length;d<
f;d++)a[d]==b?e=d:a[d]>b&&a[d]--; -1!=e&&c===k&&a.splice(e,1)}function ca(a,b,c,e){var d=a.aoData[b],f,g=function(c,f){for(;c.childNodes.length;)c.removeChild(c.firstChild);c.innerHTML=x(a,b,f,"display")};if("dom"===c||(!c||"auto"===c)&&"dom"===d.src)d._aData=na(a,d,e,e===k?k:d._aData).data;else{var j=d.anCells;if(j)if(e!==k)g(j[e],e);else{c=0;for(f=j.length;c<f;c++)g(j[c],c)}}d._aSortData=null;d._aFilterData=null;g=a.aoColumns;if(e!==k)g[e].sType=null;else{c=0;for(f=g.length;c<f;c++)g[c].sType=null;
Ma(d)}}function na(a,b,c,e){var d=[],f=b.firstChild,g,j=0,i,o=a.aoColumns,l=a._rowReadObject,e=e||l?{}:[],q=function(a,b){if("string"===typeof a){var c=a.indexOf("@");-1!==c&&(c=a.substring(c+1),S(a)(e,b.getAttribute(c)))}},a=function(a){if(c===k||c===j)g=o[j],i=h.trim(a.innerHTML),g&&g._bAttrSrc?(S(g.mData._)(e,i),q(g.mData.sort,a),q(g.mData.type,a),q(g.mData.filter,a)):l?(g._setter||(g._setter=S(g.mData)),g._setter(e,i)):e[j]=i;j++};if(f)for(;f;){b=f.nodeName.toUpperCase();if("TD"==b||"TH"==b)a(f),
d.push(f);f=f.nextSibling}else{d=b.anCells;f=0;for(b=d.length;f<b;f++)a(d[f])}return{data:e,cells:d}}function Ja(a,b,c,e){var d=a.aoData[b],f=d._aData,g=[],j,i,h,l,q;if(null===d.nTr){j=c||Q.createElement("tr");d.nTr=j;d.anCells=g;j._DT_RowIndex=b;Ma(d);l=0;for(q=a.aoColumns.length;l<q;l++){h=a.aoColumns[l];i=c?e[l]:Q.createElement(h.sCellType);g.push(i);if(!c||h.mRender||h.mData!==l)i.innerHTML=x(a,b,l,"display");h.sClass&&(i.className+=" "+h.sClass);h.bVisible&&!c?j.appendChild(i):!h.bVisible&&c&&
i.parentNode.removeChild(i);h.fnCreatedCell&&h.fnCreatedCell.call(a.oInstance,i,x(a,b,l),f,b,l)}w(a,"aoRowCreatedCallback",null,[j,f,b])}d.nTr.setAttribute("role","row")}function Ma(a){var b=a.nTr,c=a._aData;if(b){c.DT_RowId&&(b.id=c.DT_RowId);if(c.DT_RowClass){var e=c.DT_RowClass.split(" ");a.__rowc=a.__rowc?Na(a.__rowc.concat(e)):e;h(b).removeClass(a.__rowc.join(" ")).addClass(c.DT_RowClass)}c.DT_RowAttr&&h(b).attr(c.DT_RowAttr);c.DT_RowData&&h(b).data(c.DT_RowData)}}function jb(a){var b,c,e,d,
f,g=a.nTHead,j=a.nTFoot,i=0===h("th, td",g).length,o=a.oClasses,l=a.aoColumns;i&&(d=h("<tr/>").appendTo(g));b=0;for(c=l.length;b<c;b++)f=l[b],e=h(f.nTh).addClass(f.sClass),i&&e.appendTo(d),a.oFeatures.bSort&&(e.addClass(f.sSortingClass),!1!==f.bSortable&&(e.attr("tabindex",a.iTabIndex).attr("aria-controls",a.sTableId),Oa(a,f.nTh,b))),f.sTitle!=e.html()&&e.html(f.sTitle),Pa(a,"header")(a,e,f,o);i&&da(a.aoHeader,g);h(g).find(">tr").attr("role","row");h(g).find(">tr>th, >tr>td").addClass(o.sHeaderTH);
h(j).find(">tr>th, >tr>td").addClass(o.sFooterTH);if(null!==j){a=a.aoFooter[0];b=0;for(c=a.length;b<c;b++)f=l[b],f.nTf=a[b].cell,f.sClass&&h(f.nTf).addClass(f.sClass)}}function ea(a,b,c){var e,d,f,g=[],j=[],i=a.aoColumns.length,o;if(b){c===k&&(c=!1);e=0;for(d=b.length;e<d;e++){g[e]=b[e].slice();g[e].nTr=b[e].nTr;for(f=i-1;0<=f;f--)!a.aoColumns[f].bVisible&&!c&&g[e].splice(f,1);j.push([])}e=0;for(d=g.length;e<d;e++){if(a=g[e].nTr)for(;f=a.firstChild;)a.removeChild(f);f=0;for(b=g[e].length;f<b;f++)if(o=
i=1,j[e][f]===k){a.appendChild(g[e][f].cell);for(j[e][f]=1;g[e+i]!==k&&g[e][f].cell==g[e+i][f].cell;)j[e+i][f]=1,i++;for(;g[e][f+o]!==k&&g[e][f].cell==g[e][f+o].cell;){for(c=0;c<i;c++)j[e+c][f+o]=1;o++}h(g[e][f].cell).attr("rowspan",i).attr("colspan",o)}}}}function M(a){var b=w(a,"aoPreDrawCallback","preDraw",[a]);if(-1!==h.inArray(!1,b))C(a,!1);else{var b=[],c=0,e=a.asStripeClasses,d=e.length,f=a.oLanguage,g=a.iInitDisplayStart,j="ssp"==B(a),i=a.aiDisplay;a.bDrawing=!0;g!==k&&-1!==g&&(a._iDisplayStart=
j?g:g>=a.fnRecordsDisplay()?0:g,a.iInitDisplayStart=-1);var g=a._iDisplayStart,o=a.fnDisplayEnd();if(a.bDeferLoading)a.bDeferLoading=!1,a.iDraw++,C(a,!1);else if(j){if(!a.bDestroying&&!kb(a))return}else a.iDraw++;if(0!==i.length){f=j?a.aoData.length:o;for(j=j?0:g;j<f;j++){var l=i[j],q=a.aoData[l];null===q.nTr&&Ja(a,l);l=q.nTr;if(0!==d){var n=e[c%d];q._sRowStripe!=n&&(h(l).removeClass(q._sRowStripe).addClass(n),q._sRowStripe=n)}w(a,"aoRowCallback",null,[l,q._aData,c,j]);b.push(l);c++}}else c=f.sZeroRecords,
1==a.iDraw&&"ajax"==B(a)?c=f.sLoadingRecords:f.sEmptyTable&&0===a.fnRecordsTotal()&&(c=f.sEmptyTable),b[0]=h("<tr/>",{"class":d?e[0]:""}).append(h("<td />",{valign:"top",colSpan:aa(a),"class":a.oClasses.sRowEmpty}).html(c))[0];w(a,"aoHeaderCallback","header",[h(a.nTHead).children("tr")[0],La(a),g,o,i]);w(a,"aoFooterCallback","footer",[h(a.nTFoot).children("tr")[0],La(a),g,o,i]);e=h(a.nTBody);e.children().detach();e.append(h(b));w(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=
!1}}function N(a,b){var c=a.oFeatures,e=c.bFilter;c.bSort&&lb(a);e?fa(a,a.oPreviousSearch):a.aiDisplay=a.aiDisplayMaster.slice();!0!==b&&(a._iDisplayStart=0);a._drawHold=b;M(a);a._drawHold=!1}function mb(a){var b=a.oClasses,c=h(a.nTable),c=h("<div/>").insertBefore(c),e=a.oFeatures,d=h("<div/>",{id:a.sTableId+"_wrapper","class":b.sWrapper+(a.nTFoot?"":" "+b.sNoFooter)});a.nHolding=c[0];a.nTableWrapper=d[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var f=a.sDom.split(""),g,j,i,o,l,q,n=0;n<f.length;n++){g=
null;j=f[n];if("<"==j){i=h("<div/>")[0];o=f[n+1];if("'"==o||'"'==o){l="";for(q=2;f[n+q]!=o;)l+=f[n+q],q++;"H"==l?l=b.sJUIHeader:"F"==l&&(l=b.sJUIFooter);-1!=l.indexOf(".")?(o=l.split("."),i.id=o[0].substr(1,o[0].length-1),i.className=o[1]):"#"==l.charAt(0)?i.id=l.substr(1,l.length-1):i.className=l;n+=q}d.append(i);d=h(i)}else if(">"==j)d=d.parent();else if("l"==j&&e.bPaginate&&e.bLengthChange)g=nb(a);else if("f"==j&&e.bFilter)g=ob(a);else if("r"==j&&e.bProcessing)g=pb(a);else if("t"==j)g=qb(a);else if("i"==
j&&e.bInfo)g=rb(a);else if("p"==j&&e.bPaginate)g=sb(a);else if(0!==m.ext.feature.length){i=m.ext.feature;q=0;for(o=i.length;q<o;q++)if(j==i[q].cFeature){g=i[q].fnInit(a);break}}g&&(i=a.aanFeatures,i[j]||(i[j]=[]),i[j].push(g),d.append(g))}c.replaceWith(d)}function da(a,b){var c=h(b).children("tr"),e,d,f,g,j,i,o,l,q,n;a.splice(0,a.length);f=0;for(i=c.length;f<i;f++)a.push([]);f=0;for(i=c.length;f<i;f++){e=c[f];for(d=e.firstChild;d;){if("TD"==d.nodeName.toUpperCase()||"TH"==d.nodeName.toUpperCase()){l=
1*d.getAttribute("colspan");q=1*d.getAttribute("rowspan");l=!l||0===l||1===l?1:l;q=!q||0===q||1===q?1:q;g=0;for(j=a[f];j[g];)g++;o=g;n=1===l?!0:!1;for(j=0;j<l;j++)for(g=0;g<q;g++)a[f+g][o+j]={cell:d,unique:n},a[f+g].nTr=e}d=d.nextSibling}}}function qa(a,b,c){var e=[];c||(c=a.aoHeader,b&&(c=[],da(c,b)));for(var b=0,d=c.length;b<d;b++)for(var f=0,g=c[b].length;f<g;f++)if(c[b][f].unique&&(!e[f]||!a.bSortCellsTop))e[f]=c[b][f].cell;return e}function ra(a,b,c){w(a,"aoServerParams","serverParams",[b]);
if(b&&h.isArray(b)){var e={},d=/(.*?)\[\]$/;h.each(b,function(a,b){var c=b.name.match(d);c?(c=c[0],e[c]||(e[c]=[]),e[c].push(b.value)):e[b.name]=b.value});b=e}var f,g=a.ajax,j=a.oInstance,i=function(b){w(a,null,"xhr",[a,b,a.jqXHR]);c(b)};if(h.isPlainObject(g)&&g.data){f=g.data;var o=h.isFunction(f)?f(b,a):f,b=h.isFunction(f)&&o?o:h.extend(!0,b,o);delete g.data}o={data:b,success:function(b){var c=b.error||b.sError;c&&I(a,0,c);a.json=b;i(b)},dataType:"json",cache:!1,type:a.sServerMethod,error:function(b,
c){var f=w(a,null,"xhr",[a,null,a.jqXHR]);-1===h.inArray(!0,f)&&("parsererror"==c?I(a,0,"Invalid JSON response",1):4===b.readyState&&I(a,0,"Ajax error",7));C(a,!1)}};a.oAjaxData=b;w(a,null,"preXhr",[a,b]);a.fnServerData?a.fnServerData.call(j,a.sAjaxSource,h.map(b,function(a,b){return{name:b,value:a}}),i,a):a.sAjaxSource||"string"===typeof g?a.jqXHR=h.ajax(h.extend(o,{url:g||a.sAjaxSource})):h.isFunction(g)?a.jqXHR=g.call(j,b,i,a):(a.jqXHR=h.ajax(h.extend(o,g)),g.data=f)}function kb(a){return a.bAjaxDataGet?
(a.iDraw++,C(a,!0),ra(a,tb(a),function(b){ub(a,b)}),!1):!0}function tb(a){var b=a.aoColumns,c=b.length,e=a.oFeatures,d=a.oPreviousSearch,f=a.aoPreSearchCols,g,j=[],i,o,l,q=U(a);g=a._iDisplayStart;i=!1!==e.bPaginate?a._iDisplayLength:-1;var n=function(a,b){j.push({name:a,value:b})};n("sEcho",a.iDraw);n("iColumns",c);n("sColumns",D(b,"sName").join(","));n("iDisplayStart",g);n("iDisplayLength",i);var k={draw:a.iDraw,columns:[],order:[],start:g,length:i,search:{value:d.sSearch,regex:d.bRegex}};for(g=
0;g<c;g++)o=b[g],l=f[g],i="function"==typeof o.mData?"function":o.mData,k.columns.push({data:i,name:o.sName,searchable:o.bSearchable,orderable:o.bSortable,search:{value:l.sSearch,regex:l.bRegex}}),n("mDataProp_"+g,i),e.bFilter&&(n("sSearch_"+g,l.sSearch),n("bRegex_"+g,l.bRegex),n("bSearchable_"+g,o.bSearchable)),e.bSort&&n("bSortable_"+g,o.bSortable);e.bFilter&&(n("sSearch",d.sSearch),n("bRegex",d.bRegex));e.bSort&&(h.each(q,function(a,b){k.order.push({column:b.col,dir:b.dir});n("iSortCol_"+a,b.col);
n("sSortDir_"+a,b.dir)}),n("iSortingCols",q.length));b=m.ext.legacy.ajax;return null===b?a.sAjaxSource?j:k:b?j:k}function ub(a,b){var c=sa(a,b),e=b.sEcho!==k?b.sEcho:b.draw,d=b.iTotalRecords!==k?b.iTotalRecords:b.recordsTotal,f=b.iTotalDisplayRecords!==k?b.iTotalDisplayRecords:b.recordsFiltered;if(e){if(1*e<a.iDraw)return;a.iDraw=1*e}oa(a);a._iRecordsTotal=parseInt(d,10);a._iRecordsDisplay=parseInt(f,10);e=0;for(d=c.length;e<d;e++)K(a,c[e]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=!1;
M(a);a._bInitComplete||ta(a,b);a.bAjaxDataGet=!0;C(a,!1)}function sa(a,b){var c=h.isPlainObject(a.ajax)&&a.ajax.dataSrc!==k?a.ajax.dataSrc:a.sAjaxDataProp;return"data"===c?b.aaData||b[c]:""!==c?R(c)(b):b}function ob(a){var b=a.oClasses,c=a.sTableId,e=a.oLanguage,d=a.oPreviousSearch,f=a.aanFeatures,g='<input type="search" class="'+b.sFilterInput+'"/>',j=e.sSearch,j=j.match(/_INPUT_/)?j.replace("_INPUT_",g):j+g,b=h("<div/>",{id:!f.f?c+"_filter":null,"class":b.sFilter}).append(h("<label/>").append(j)),
f=function(){var b=!this.value?"":this.value;b!=d.sSearch&&(fa(a,{sSearch:b,bRegex:d.bRegex,bSmart:d.bSmart,bCaseInsensitive:d.bCaseInsensitive}),a._iDisplayStart=0,M(a))},g=null!==a.searchDelay?a.searchDelay:"ssp"===B(a)?400:0,i=h("input",b).val(d.sSearch).attr("placeholder",e.sSearchPlaceholder).bind("keyup.DT search.DT input.DT paste.DT cut.DT",g?ua(f,g):f).bind("keypress.DT",function(a){if(13==a.keyCode)return!1}).attr("aria-controls",c);h(a.nTable).on("search.dt.DT",function(b,c){if(a===c)try{i[0]!==
Q.activeElement&&i.val(d.sSearch)}catch(f){}});return b[0]}function fa(a,b,c){var e=a.oPreviousSearch,d=a.aoPreSearchCols,f=function(a){e.sSearch=a.sSearch;e.bRegex=a.bRegex;e.bSmart=a.bSmart;e.bCaseInsensitive=a.bCaseInsensitive};Ha(a);if("ssp"!=B(a)){vb(a,b.sSearch,c,b.bEscapeRegex!==k?!b.bEscapeRegex:b.bRegex,b.bSmart,b.bCaseInsensitive);f(b);for(b=0;b<d.length;b++)wb(a,d[b].sSearch,b,d[b].bEscapeRegex!==k?!d[b].bEscapeRegex:d[b].bRegex,d[b].bSmart,d[b].bCaseInsensitive);xb(a)}else f(b);a.bFiltered=
!0;w(a,null,"search",[a])}function xb(a){for(var b=m.ext.search,c=a.aiDisplay,e,d,f=0,g=b.length;f<g;f++){for(var j=[],i=0,h=c.length;i<h;i++)d=c[i],e=a.aoData[d],b[f](a,e._aFilterData,d,e._aData,i)&&j.push(d);c.length=0;c.push.apply(c,j)}}function wb(a,b,c,e,d,f){if(""!==b)for(var g=a.aiDisplay,e=Qa(b,e,d,f),d=g.length-1;0<=d;d--)b=a.aoData[g[d]]._aFilterData[c],e.test(b)||g.splice(d,1)}function vb(a,b,c,e,d,f){var e=Qa(b,e,d,f),d=a.oPreviousSearch.sSearch,f=a.aiDisplayMaster,g;0!==m.ext.search.length&&
(c=!0);g=yb(a);if(0>=b.length)a.aiDisplay=f.slice();else{if(g||c||d.length>b.length||0!==b.indexOf(d)||a.bSorted)a.aiDisplay=f.slice();b=a.aiDisplay;for(c=b.length-1;0<=c;c--)e.test(a.aoData[b[c]]._sFilterRow)||b.splice(c,1)}}function Qa(a,b,c,e){a=b?a:va(a);c&&(a="^(?=.*?"+h.map(a.match(/"[^"]+"|[^ ]+/g)||[""],function(a){if('"'===a.charAt(0))var b=a.match(/^"(.*)"$/),a=b?b[1]:a;return a.replace('"',"")}).join(")(?=.*?")+").*$");return RegExp(a,e?"i":"")}function va(a){return a.replace(Yb,"\\$1")}
function yb(a){var b=a.aoColumns,c,e,d,f,g,j,i,h,l=m.ext.type.search;c=!1;e=0;for(f=a.aoData.length;e<f;e++)if(h=a.aoData[e],!h._aFilterData){j=[];d=0;for(g=b.length;d<g;d++)c=b[d],c.bSearchable?(i=x(a,e,d,"filter"),l[c.sType]&&(i=l[c.sType](i)),null===i&&(i=""),"string"!==typeof i&&i.toString&&(i=i.toString())):i="",i.indexOf&&-1!==i.indexOf("&")&&(wa.innerHTML=i,i=Zb?wa.textContent:wa.innerText),i.replace&&(i=i.replace(/[\r\n]/g,"")),j.push(i);h._aFilterData=j;h._sFilterRow=j.join(" ");c=!0}return c}
function zb(a){return{search:a.sSearch,smart:a.bSmart,regex:a.bRegex,caseInsensitive:a.bCaseInsensitive}}function Ab(a){return{sSearch:a.search,bSmart:a.smart,bRegex:a.regex,bCaseInsensitive:a.caseInsensitive}}function rb(a){var b=a.sTableId,c=a.aanFeatures.i,e=h("<div/>",{"class":a.oClasses.sInfo,id:!c?b+"_info":null});c||(a.aoDrawCallback.push({fn:Bb,sName:"information"}),e.attr("role","status").attr("aria-live","polite"),h(a.nTable).attr("aria-describedby",b+"_info"));return e[0]}function Bb(a){var b=
a.aanFeatures.i;if(0!==b.length){var c=a.oLanguage,e=a._iDisplayStart+1,d=a.fnDisplayEnd(),f=a.fnRecordsTotal(),g=a.fnRecordsDisplay(),j=g?c.sInfo:c.sInfoEmpty;g!==f&&(j+=" "+c.sInfoFiltered);j+=c.sInfoPostFix;j=Cb(a,j);c=c.fnInfoCallback;null!==c&&(j=c.call(a.oInstance,a,e,d,f,g,j));h(b).html(j)}}function Cb(a,b){var c=a.fnFormatNumber,e=a._iDisplayStart+1,d=a._iDisplayLength,f=a.fnRecordsDisplay(),g=-1===d;return b.replace(/_START_/g,c.call(a,e)).replace(/_END_/g,c.call(a,a.fnDisplayEnd())).replace(/_MAX_/g,
c.call(a,a.fnRecordsTotal())).replace(/_TOTAL_/g,c.call(a,f)).replace(/_PAGE_/g,c.call(a,g?1:Math.ceil(e/d))).replace(/_PAGES_/g,c.call(a,g?1:Math.ceil(f/d)))}function ga(a){var b,c,e=a.iInitDisplayStart,d=a.aoColumns,f;c=a.oFeatures;if(a.bInitialised){mb(a);jb(a);ea(a,a.aoHeader);ea(a,a.aoFooter);C(a,!0);c.bAutoWidth&&Ga(a);b=0;for(c=d.length;b<c;b++)f=d[b],f.sWidth&&(f.nTh.style.width=s(f.sWidth));N(a);d=B(a);"ssp"!=d&&("ajax"==d?ra(a,[],function(c){var f=sa(a,c);for(b=0;b<f.length;b++)K(a,f[b]);
a.iInitDisplayStart=e;N(a);C(a,!1);ta(a,c)},a):(C(a,!1),ta(a)))}else setTimeout(function(){ga(a)},200)}function ta(a,b){a._bInitComplete=!0;b&&X(a);w(a,"aoInitComplete","init",[a,b])}function Ra(a,b){var c=parseInt(b,10);a._iDisplayLength=c;Sa(a);w(a,null,"length",[a,c])}function nb(a){for(var b=a.oClasses,c=a.sTableId,e=a.aLengthMenu,d=h.isArray(e[0]),f=d?e[0]:e,e=d?e[1]:e,d=h("<select/>",{name:c+"_length","aria-controls":c,"class":b.sLengthSelect}),g=0,j=f.length;g<j;g++)d[0][g]=new Option(e[g],
f[g]);var i=h("<div><label/></div>").addClass(b.sLength);a.aanFeatures.l||(i[0].id=c+"_length");i.children().append(a.oLanguage.sLengthMenu.replace("_MENU_",d[0].outerHTML));h("select",i).val(a._iDisplayLength).bind("change.DT",function(){Ra(a,h(this).val());M(a)});h(a.nTable).bind("length.dt.DT",function(b,c,f){a===c&&h("select",i).val(f)});return i[0]}function sb(a){var b=a.sPaginationType,c=m.ext.pager[b],e="function"===typeof c,d=function(a){M(a)},b=h("<div/>").addClass(a.oClasses.sPaging+b)[0],
f=a.aanFeatures;e||c.fnInit(a,b,d);f.p||(b.id=a.sTableId+"_paginate",a.aoDrawCallback.push({fn:function(a){if(e){var b=a._iDisplayStart,i=a._iDisplayLength,h=a.fnRecordsDisplay(),l=-1===i,b=l?0:Math.ceil(b/i),i=l?1:Math.ceil(h/i),h=c(b,i),q,l=0;for(q=f.p.length;l<q;l++)Pa(a,"pageButton")(a,f.p[l],l,h,b,i)}else c.fnUpdate(a,d)},sName:"pagination"}));return b}function Ta(a,b,c){var e=a._iDisplayStart,d=a._iDisplayLength,f=a.fnRecordsDisplay();0===f||-1===d?e=0:"number"===typeof b?(e=b*d,e>f&&(e=0)):
"first"==b?e=0:"previous"==b?(e=0<=d?e-d:0,0>e&&(e=0)):"next"==b?e+d<f&&(e+=d):"last"==b?e=Math.floor((f-1)/d)*d:I(a,0,"Unknown paging action: "+b,5);b=a._iDisplayStart!==e;a._iDisplayStart=e;b&&(w(a,null,"page",[a]),c&&M(a));return b}function pb(a){return h("<div/>",{id:!a.aanFeatures.r?a.sTableId+"_processing":null,"class":a.oClasses.sProcessing}).html(a.oLanguage.sProcessing).insertBefore(a.nTable)[0]}function C(a,b){a.oFeatures.bProcessing&&h(a.aanFeatures.r).css("display",b?"block":"none");w(a,
null,"processing",[a,b])}function qb(a){var b=h(a.nTable);b.attr("role","grid");var c=a.oScroll;if(""===c.sX&&""===c.sY)return a.nTable;var e=c.sX,d=c.sY,f=a.oClasses,g=b.children("caption"),j=g.length?g[0]._captionSide:null,i=h(b[0].cloneNode(!1)),o=h(b[0].cloneNode(!1)),l=b.children("tfoot");c.sX&&"100%"===b.attr("width")&&b.removeAttr("width");l.length||(l=null);c=h("<div/>",{"class":f.sScrollWrapper}).append(h("<div/>",{"class":f.sScrollHead}).css({overflow:"hidden",position:"relative",border:0,
width:e?!e?null:s(e):"100%"}).append(h("<div/>",{"class":f.sScrollHeadInner}).css({"box-sizing":"content-box",width:c.sXInner||"100%"}).append(i.removeAttr("id").css("margin-left",0).append("top"===j?g:null).append(b.children("thead"))))).append(h("<div/>",{"class":f.sScrollBody}).css({overflow:"auto",height:!d?null:s(d),width:!e?null:s(e)}).append(b));l&&c.append(h("<div/>",{"class":f.sScrollFoot}).css({overflow:"hidden",border:0,width:e?!e?null:s(e):"100%"}).append(h("<div/>",{"class":f.sScrollFootInner}).append(o.removeAttr("id").css("margin-left",
0).append("bottom"===j?g:null).append(b.children("tfoot")))));var b=c.children(),q=b[0],f=b[1],n=l?b[2]:null;if(e)h(f).on("scroll.DT",function(){var a=this.scrollLeft;q.scrollLeft=a;l&&(n.scrollLeft=a)});a.nScrollHead=q;a.nScrollBody=f;a.nScrollFoot=n;a.aoDrawCallback.push({fn:Y,sName:"scrolling"});return c[0]}function Y(a){var b=a.oScroll,c=b.sX,e=b.sXInner,d=b.sY,f=b.iBarWidth,g=h(a.nScrollHead),j=g[0].style,i=g.children("div"),o=i[0].style,l=i.children("table"),i=a.nScrollBody,q=h(i),n=i.style,
k=h(a.nScrollFoot).children("div"),p=k.children("table"),m=h(a.nTHead),r=h(a.nTable),t=r[0],O=t.style,L=a.nTFoot?h(a.nTFoot):null,ha=a.oBrowser,w=ha.bScrollOversize,v,u,y,x,z,A=[],B=[],C=[],D,E=function(a){a=a.style;a.paddingTop="0";a.paddingBottom="0";a.borderTopWidth="0";a.borderBottomWidth="0";a.height=0};r.children("thead, tfoot").remove();z=m.clone().prependTo(r);v=m.find("tr");y=z.find("tr");z.find("th, td").removeAttr("tabindex");L&&(x=L.clone().prependTo(r),u=L.find("tr"),x=x.find("tr"));
c||(n.width="100%",g[0].style.width="100%");h.each(qa(a,z),function(b,c){D=la(a,b);c.style.width=a.aoColumns[D].sWidth});L&&G(function(a){a.style.width=""},x);b.bCollapse&&""!==d&&(n.height=q[0].offsetHeight+m[0].offsetHeight+"px");g=r.outerWidth();if(""===c){if(O.width="100%",w&&(r.find("tbody").height()>i.offsetHeight||"scroll"==q.css("overflow-y")))O.width=s(r.outerWidth()-f)}else""!==e?O.width=s(e):g==q.width()&&q.height()<r.height()?(O.width=s(g-f),r.outerWidth()>g-f&&(O.width=s(g))):O.width=
s(g);g=r.outerWidth();G(E,y);G(function(a){C.push(a.innerHTML);A.push(s(h(a).css("width")))},y);G(function(a,b){a.style.width=A[b]},v);h(y).height(0);L&&(G(E,x),G(function(a){B.push(s(h(a).css("width")))},x),G(function(a,b){a.style.width=B[b]},u),h(x).height(0));G(function(a,b){a.innerHTML='<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+C[b]+"</div>";a.style.width=A[b]},y);L&&G(function(a,b){a.innerHTML="";a.style.width=B[b]},x);if(r.outerWidth()<g){u=i.scrollHeight>i.offsetHeight||
"scroll"==q.css("overflow-y")?g+f:g;if(w&&(i.scrollHeight>i.offsetHeight||"scroll"==q.css("overflow-y")))O.width=s(u-f);(""===c||""!==e)&&I(a,1,"Possible column misalignment",6)}else u="100%";n.width=s(u);j.width=s(u);L&&(a.nScrollFoot.style.width=s(u));!d&&w&&(n.height=s(t.offsetHeight+f));d&&b.bCollapse&&(n.height=s(d),b=c&&t.offsetWidth>i.offsetWidth?f:0,t.offsetHeight<i.offsetHeight&&(n.height=s(t.offsetHeight+b)));b=r.outerWidth();l[0].style.width=s(b);o.width=s(b);l=r.height()>i.clientHeight||
"scroll"==q.css("overflow-y");ha="padding"+(ha.bScrollbarLeft?"Left":"Right");o[ha]=l?f+"px":"0px";L&&(p[0].style.width=s(b),k[0].style.width=s(b),k[0].style[ha]=l?f+"px":"0px");q.scroll();if((a.bSorted||a.bFiltered)&&!a._drawHold)i.scrollTop=0}function G(a,b,c){for(var e=0,d=0,f=b.length,g,j;d<f;){g=b[d].firstChild;for(j=c?c[d].firstChild:null;g;)1===g.nodeType&&(c?a(g,j,e):a(g,e),e++),g=g.nextSibling,j=c?j.nextSibling:null;d++}}function Ga(a){var b=a.nTable,c=a.aoColumns,e=a.oScroll,d=e.sY,f=e.sX,
g=e.sXInner,j=c.length,e=Z(a,"bVisible"),i=h("th",a.nTHead),o=b.getAttribute("width"),l=b.parentNode,k=!1,n,m;(n=b.style.width)&&-1!==n.indexOf("%")&&(o=n);for(n=0;n<e.length;n++)m=c[e[n]],null!==m.sWidth&&(m.sWidth=Db(m.sWidthOrig,l),k=!0);if(!k&&!f&&!d&&j==aa(a)&&j==i.length)for(n=0;n<j;n++)c[n].sWidth=s(i.eq(n).width());else{j=h(b).clone().css("visibility","hidden").removeAttr("id");j.find("tbody tr").remove();var p=h("<tr/>").appendTo(j.find("tbody"));j.find("tfoot th, tfoot td").css("width",
"");i=qa(a,j.find("thead")[0]);for(n=0;n<e.length;n++)m=c[e[n]],i[n].style.width=null!==m.sWidthOrig&&""!==m.sWidthOrig?s(m.sWidthOrig):"";if(a.aoData.length)for(n=0;n<e.length;n++)k=e[n],m=c[k],h(Eb(a,k)).clone(!1).append(m.sContentPadding).appendTo(p);j.appendTo(l);f&&g?j.width(g):f?(j.css("width","auto"),j.width()<l.offsetWidth&&j.width(l.offsetWidth)):d?j.width(l.offsetWidth):o&&j.width(o);Fb(a,j[0]);if(f){for(n=g=0;n<e.length;n++)m=c[e[n]],d=h(i[n]).outerWidth(),g+=null===m.sWidthOrig?d:parseInt(m.sWidth,
10)+d-h(i[n]).width();j.width(s(g));b.style.width=s(g)}for(n=0;n<e.length;n++)if(m=c[e[n]],d=h(i[n]).width())m.sWidth=s(d);b.style.width=s(j.css("width"));j.remove()}o&&(b.style.width=s(o));if((o||f)&&!a._reszEvt)b=function(){h(Ea).bind("resize.DT-"+a.sInstance,ua(function(){X(a)}))},a.oBrowser.bScrollOversize?setTimeout(b,1E3):b(),a._reszEvt=!0}function ua(a,b){var c=b!==k?b:200,e,d;return function(){var b=this,g=+new Date,j=arguments;e&&g<e+c?(clearTimeout(d),d=setTimeout(function(){e=k;a.apply(b,
j)},c)):(e=g,a.apply(b,j))}}function Db(a,b){if(!a)return 0;var c=h("<div/>").css("width",s(a)).appendTo(b||Q.body),e=c[0].offsetWidth;c.remove();return e}function Fb(a,b){var c=a.oScroll;if(c.sX||c.sY)c=!c.sX?c.iBarWidth:0,b.style.width=s(h(b).outerWidth()-c)}function Eb(a,b){var c=Gb(a,b);if(0>c)return null;var e=a.aoData[c];return!e.nTr?h("<td/>").html(x(a,c,b,"display"))[0]:e.anCells[b]}function Gb(a,b){for(var c,e=-1,d=-1,f=0,g=a.aoData.length;f<g;f++)c=x(a,f,b,"display")+"",c=c.replace($b,""),
c.length>e&&(e=c.length,d=f);return d}function s(a){return null===a?"0px":"number"==typeof a?0>a?"0px":a+"px":a.match(/\d$/)?a+"px":a}function Hb(){var a=m.__scrollbarWidth;if(a===k){var b=h("<p/>").css({position:"absolute",top:0,left:0,width:"100%",height:150,padding:0,overflow:"scroll",visibility:"hidden"}).appendTo("body"),a=b[0].offsetWidth-b[0].clientWidth;m.__scrollbarWidth=a;b.remove()}return a}function U(a){var b,c,e=[],d=a.aoColumns,f,g,j,i;b=a.aaSortingFixed;c=h.isPlainObject(b);var o=[];
f=function(a){a.length&&!h.isArray(a[0])?o.push(a):o.push.apply(o,a)};h.isArray(b)&&f(b);c&&b.pre&&f(b.pre);f(a.aaSorting);c&&b.post&&f(b.post);for(a=0;a<o.length;a++){i=o[a][0];f=d[i].aDataSort;b=0;for(c=f.length;b<c;b++)g=f[b],j=d[g].sType||"string",o[a]._idx===k&&(o[a]._idx=h.inArray(o[a][1],d[g].asSorting)),e.push({src:i,col:g,dir:o[a][1],index:o[a]._idx,type:j,formatter:m.ext.type.order[j+"-pre"]})}return e}function lb(a){var b,c,e=[],d=m.ext.type.order,f=a.aoData,g=0,j,i=a.aiDisplayMaster,h;
Ha(a);h=U(a);b=0;for(c=h.length;b<c;b++)j=h[b],j.formatter&&g++,Ib(a,j.col);if("ssp"!=B(a)&&0!==h.length){b=0;for(c=i.length;b<c;b++)e[i[b]]=b;g===h.length?i.sort(function(a,b){var c,d,g,j,i=h.length,k=f[a]._aSortData,m=f[b]._aSortData;for(g=0;g<i;g++)if(j=h[g],c=k[j.col],d=m[j.col],c=c<d?-1:c>d?1:0,0!==c)return"asc"===j.dir?c:-c;c=e[a];d=e[b];return c<d?-1:c>d?1:0}):i.sort(function(a,b){var c,g,j,i,k=h.length,m=f[a]._aSortData,r=f[b]._aSortData;for(j=0;j<k;j++)if(i=h[j],c=m[i.col],g=r[i.col],i=d[i.type+
"-"+i.dir]||d["string-"+i.dir],c=i(c,g),0!==c)return c;c=e[a];g=e[b];return c<g?-1:c>g?1:0})}a.bSorted=!0}function Jb(a){for(var b,c,e=a.aoColumns,d=U(a),a=a.oLanguage.oAria,f=0,g=e.length;f<g;f++){c=e[f];var j=c.asSorting;b=c.sTitle.replace(/<.*?>/g,"");var i=c.nTh;i.removeAttribute("aria-sort");c.bSortable&&(0<d.length&&d[0].col==f?(i.setAttribute("aria-sort","asc"==d[0].dir?"ascending":"descending"),c=j[d[0].index+1]||j[0]):c=j[0],b+="asc"===c?a.sSortAscending:a.sSortDescending);i.setAttribute("aria-label",
b)}}function Ua(a,b,c,e){var d=a.aaSorting,f=a.aoColumns[b].asSorting,g=function(a,b){var c=a._idx;c===k&&(c=h.inArray(a[1],f));return c+1<f.length?c+1:b?null:0};"number"===typeof d[0]&&(d=a.aaSorting=[d]);c&&a.oFeatures.bSortMulti?(c=h.inArray(b,D(d,"0")),-1!==c?(b=g(d[c],!0),null===b&&1===d.length&&(b=0),null===b?d.splice(c,1):(d[c][1]=f[b],d[c]._idx=b)):(d.push([b,f[0],0]),d[d.length-1]._idx=0)):d.length&&d[0][0]==b?(b=g(d[0]),d.length=1,d[0][1]=f[b],d[0]._idx=b):(d.length=0,d.push([b,f[0]]),d[0]._idx=
0);N(a);"function"==typeof e&&e(a)}function Oa(a,b,c,e){var d=a.aoColumns[c];Va(b,{},function(b){!1!==d.bSortable&&(a.oFeatures.bProcessing?(C(a,!0),setTimeout(function(){Ua(a,c,b.shiftKey,e);"ssp"!==B(a)&&C(a,!1)},0)):Ua(a,c,b.shiftKey,e))})}function xa(a){var b=a.aLastSort,c=a.oClasses.sSortColumn,e=U(a),d=a.oFeatures,f,g;if(d.bSort&&d.bSortClasses){d=0;for(f=b.length;d<f;d++)g=b[d].src,h(D(a.aoData,"anCells",g)).removeClass(c+(2>d?d+1:3));d=0;for(f=e.length;d<f;d++)g=e[d].src,h(D(a.aoData,"anCells",
g)).addClass(c+(2>d?d+1:3))}a.aLastSort=e}function Ib(a,b){var c=a.aoColumns[b],e=m.ext.order[c.sSortDataType],d;e&&(d=e.call(a.oInstance,a,b,$(a,b)));for(var f,g=m.ext.type.order[c.sType+"-pre"],j=0,i=a.aoData.length;j<i;j++)if(c=a.aoData[j],c._aSortData||(c._aSortData=[]),!c._aSortData[b]||e)f=e?d[j]:x(a,j,b,"sort"),c._aSortData[b]=g?g(f):f}function ya(a){if(a.oFeatures.bStateSave&&!a.bDestroying){var b={time:+new Date,start:a._iDisplayStart,length:a._iDisplayLength,order:h.extend(!0,[],a.aaSorting),
search:zb(a.oPreviousSearch),columns:h.map(a.aoColumns,function(b,e){return{visible:b.bVisible,search:zb(a.aoPreSearchCols[e])}})};w(a,"aoStateSaveParams","stateSaveParams",[a,b]);a.oSavedState=b;a.fnStateSaveCallback.call(a.oInstance,a,b)}}function Kb(a){var b,c,e=a.aoColumns;if(a.oFeatures.bStateSave){var d=a.fnStateLoadCallback.call(a.oInstance,a);if(d&&d.time&&(b=w(a,"aoStateLoadParams","stateLoadParams",[a,d]),-1===h.inArray(!1,b)&&(b=a.iStateDuration,!(0<b&&d.time<+new Date-1E3*b)&&e.length===
d.columns.length))){a.oLoadedState=h.extend(!0,{},d);d.start!==k&&(a._iDisplayStart=d.start,a.iInitDisplayStart=d.start);d.length!==k&&(a._iDisplayLength=d.length);d.order!==k&&(a.aaSorting=[],h.each(d.order,function(b,c){a.aaSorting.push(c[0]>=e.length?[0,c[1]]:c)}));d.search!==k&&h.extend(a.oPreviousSearch,Ab(d.search));b=0;for(c=d.columns.length;b<c;b++){var f=d.columns[b];f.visible!==k&&(e[b].bVisible=f.visible);f.search!==k&&h.extend(a.aoPreSearchCols[b],Ab(f.search))}w(a,"aoStateLoaded","stateLoaded",
[a,d])}}}function za(a){var b=m.settings,a=h.inArray(a,D(b,"nTable"));return-1!==a?b[a]:null}function I(a,b,c,e){c="DataTables warning: "+(null!==a?"table id="+a.sTableId+" - ":"")+c;e&&(c+=". For more information about this error, please see http://datatables.net/tn/"+e);if(b)Ea.console&&console.log&&console.log(c);else if(b=m.ext,b=b.sErrMode||b.errMode,w(a,null,"error",[a,e,c]),"alert"==b)alert(c);else{if("throw"==b)throw Error(c);"function"==typeof b&&b(a,e,c)}}function E(a,b,c,e){h.isArray(c)?
h.each(c,function(c,f){h.isArray(f)?E(a,b,f[0],f[1]):E(a,b,f)}):(e===k&&(e=c),b[c]!==k&&(a[e]=b[c]))}function Lb(a,b,c){var e,d;for(d in b)b.hasOwnProperty(d)&&(e=b[d],h.isPlainObject(e)?(h.isPlainObject(a[d])||(a[d]={}),h.extend(!0,a[d],e)):a[d]=c&&"data"!==d&&"aaData"!==d&&h.isArray(e)?e.slice():e);return a}function Va(a,b,c){h(a).bind("click.DT",b,function(b){a.blur();c(b)}).bind("keypress.DT",b,function(a){13===a.which&&(a.preventDefault(),c(a))}).bind("selectstart.DT",function(){return!1})}function z(a,
b,c,e){c&&a[b].push({fn:c,sName:e})}function w(a,b,c,e){var d=[];b&&(d=h.map(a[b].slice().reverse(),function(b){return b.fn.apply(a.oInstance,e)}));null!==c&&(b=h.Event(c+".dt"),h(a.nTable).trigger(b,e),d.push(b.result));return d}function Sa(a){var b=a._iDisplayStart,c=a.fnDisplayEnd(),e=a._iDisplayLength;b>=c&&(b=c-e);b-=b%e;if(-1===e||0>b)b=0;a._iDisplayStart=b}function Pa(a,b){var c=a.renderer,e=m.ext.renderer[b];return h.isPlainObject(c)&&c[b]?e[c[b]]||e._:"string"===typeof c?e[c]||e._:e._}function B(a){return a.oFeatures.bServerSide?
"ssp":a.ajax||a.sAjaxSource?"ajax":"dom"}function Wa(a,b){var c=[],c=Mb.numbers_length,e=Math.floor(c/2);b<=c?c=V(0,b):a<=e?(c=V(0,c-2),c.push("ellipsis"),c.push(b-1)):(a>=b-1-e?c=V(b-(c-2),b):(c=V(a-e+2,a+e-1),c.push("ellipsis"),c.push(b-1)),c.splice(0,0,"ellipsis"),c.splice(0,0,0));c.DT_el="span";return c}function db(a){h.each({num:function(b){return Aa(b,a)},"num-fmt":function(b){return Aa(b,a,Xa)},"html-num":function(b){return Aa(b,a,Ba)},"html-num-fmt":function(b){return Aa(b,a,Ba,Xa)}},function(b,
c){u.type.order[b+a+"-pre"]=c;b.match(/^html\-/)&&(u.type.search[b+a]=u.type.search.html)})}function Nb(a){return function(){var b=[za(this[m.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return m.ext.internal[a].apply(this,b)}}var m,u,t,r,v,Ya={},Ob=/[\r\n]/g,Ba=/<.*?>/g,ac=/^[\w\+\-]/,bc=/[\w\+\-]$/,Yb=RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"),Xa=/[',$\u00a3\u20ac\u00a5%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi,J=function(a){return!a||!0===a||
"-"===a?!0:!1},Pb=function(a){var b=parseInt(a,10);return!isNaN(b)&&isFinite(a)?b:null},Qb=function(a,b){Ya[b]||(Ya[b]=RegExp(va(b),"g"));return"string"===typeof a&&"."!==b?a.replace(/\./g,"").replace(Ya[b],"."):a},Za=function(a,b,c){var e="string"===typeof a;if(J(a))return!0;b&&e&&(a=Qb(a,b));c&&e&&(a=a.replace(Xa,""));return!isNaN(parseFloat(a))&&isFinite(a)},Rb=function(a,b,c){return J(a)?!0:!(J(a)||"string"===typeof a)?null:Za(a.replace(Ba,""),b,c)?!0:null},D=function(a,b,c){var e=[],d=0,f=a.length;
if(c!==k)for(;d<f;d++)a[d]&&a[d][b]&&e.push(a[d][b][c]);else for(;d<f;d++)a[d]&&e.push(a[d][b]);return e},ia=function(a,b,c,e){var d=[],f=0,g=b.length;if(e!==k)for(;f<g;f++)a[b[f]][c]&&d.push(a[b[f]][c][e]);else for(;f<g;f++)d.push(a[b[f]][c]);return d},V=function(a,b){var c=[],e;b===k?(b=0,e=a):(e=b,b=a);for(var d=b;d<e;d++)c.push(d);return c},Sb=function(a){for(var b=[],c=0,e=a.length;c<e;c++)a[c]&&b.push(a[c]);return b},Na=function(a){var b=[],c,e,d=a.length,f,g=0;e=0;a:for(;e<d;e++){c=a[e];for(f=
0;f<g;f++)if(b[f]===c)continue a;b.push(c);g++}return b},A=function(a,b,c){a[b]!==k&&(a[c]=a[b])},ba=/\[.*?\]$/,T=/\(\)$/,wa=h("<div>")[0],Zb=wa.textContent!==k,$b=/<.*?>/g;m=function(a){this.$=function(a,b){return this.api(!0).$(a,b)};this._=function(a,b){return this.api(!0).rows(a,b).data()};this.api=function(a){return a?new t(za(this[u.iApiIndex])):new t(this)};this.fnAddData=function(a,b){var c=this.api(!0),e=h.isArray(a)&&(h.isArray(a[0])||h.isPlainObject(a[0]))?c.rows.add(a):c.row.add(a);(b===
k||b)&&c.draw();return e.flatten().toArray()};this.fnAdjustColumnSizing=function(a){var b=this.api(!0).columns.adjust(),c=b.settings()[0],e=c.oScroll;a===k||a?b.draw(!1):(""!==e.sX||""!==e.sY)&&Y(c)};this.fnClearTable=function(a){var b=this.api(!0).clear();(a===k||a)&&b.draw()};this.fnClose=function(a){this.api(!0).row(a).child.hide()};this.fnDeleteRow=function(a,b,c){var e=this.api(!0),a=e.rows(a),d=a.settings()[0],h=d.aoData[a[0][0]];a.remove();b&&b.call(this,d,h);(c===k||c)&&e.draw();return h};
this.fnDestroy=function(a){this.api(!0).destroy(a)};this.fnDraw=function(a){this.api(!0).draw(a)};this.fnFilter=function(a,b,c,e,d,h){d=this.api(!0);null===b||b===k?d.search(a,c,e,h):d.column(b).search(a,c,e,h);d.draw()};this.fnGetData=function(a,b){var c=this.api(!0);if(a!==k){var e=a.nodeName?a.nodeName.toLowerCase():"";return b!==k||"td"==e||"th"==e?c.cell(a,b).data():c.row(a).data()||null}return c.data().toArray()};this.fnGetNodes=function(a){var b=this.api(!0);return a!==k?b.row(a).node():b.rows().nodes().flatten().toArray()};
this.fnGetPosition=function(a){var b=this.api(!0),c=a.nodeName.toUpperCase();return"TR"==c?b.row(a).index():"TD"==c||"TH"==c?(a=b.cell(a).index(),[a.row,a.columnVisible,a.column]):null};this.fnIsOpen=function(a){return this.api(!0).row(a).child.isShown()};this.fnOpen=function(a,b,c){return this.api(!0).row(a).child(b,c).show().child()[0]};this.fnPageChange=function(a,b){var c=this.api(!0).page(a);(b===k||b)&&c.draw(!1)};this.fnSetColumnVis=function(a,b,c){a=this.api(!0).column(a).visible(b);(c===
k||c)&&a.columns.adjust().draw()};this.fnSettings=function(){return za(this[u.iApiIndex])};this.fnSort=function(a){this.api(!0).order(a).draw()};this.fnSortListener=function(a,b,c){this.api(!0).order.listener(a,b,c)};this.fnUpdate=function(a,b,c,e,d){var h=this.api(!0);c===k||null===c?h.row(b).data(a):h.cell(b,c).data(a);(d===k||d)&&h.columns.adjust();(e===k||e)&&h.draw();return 0};this.fnVersionCheck=u.fnVersionCheck;var b=this,c=a===k,e=this.length;c&&(a={});this.oApi=this.internal=u.internal;for(var d in m.ext.internal)d&&
(this[d]=Nb(d));this.each(function(){var d={},d=1<e?Lb(d,a,!0):a,g=0,j,i=this.getAttribute("id"),o=!1,l=m.defaults,q=h(this);if("table"!=this.nodeName.toLowerCase())I(null,0,"Non-table node initialisation ("+this.nodeName+")",2);else{eb(l);fb(l.column);H(l,l,!0);H(l.column,l.column,!0);H(l,h.extend(d,q.data()));var n=m.settings,g=0;for(j=n.length;g<j;g++){var r=n[g];if(r.nTable==this||r.nTHead.parentNode==this||r.nTFoot&&r.nTFoot.parentNode==this){g=d.bRetrieve!==k?d.bRetrieve:l.bRetrieve;if(c||g)return r.oInstance;
if(d.bDestroy!==k?d.bDestroy:l.bDestroy){r.oInstance.fnDestroy();break}else{I(r,0,"Cannot reinitialise DataTable",3);return}}if(r.sTableId==this.id){n.splice(g,1);break}}if(null===i||""===i)this.id=i="DataTables_Table_"+m.ext._unique++;var p=h.extend(!0,{},m.models.oSettings,{sDestroyWidth:q[0].style.width,sInstance:i,sTableId:i});p.nTable=this;p.oApi=b.internal;p.oInit=d;n.push(p);p.oInstance=1===b.length?b:q.dataTable();eb(d);d.oLanguage&&P(d.oLanguage);d.aLengthMenu&&!d.iDisplayLength&&(d.iDisplayLength=
h.isArray(d.aLengthMenu[0])?d.aLengthMenu[0][0]:d.aLengthMenu[0]);d=Lb(h.extend(!0,{},l),d);E(p.oFeatures,d,"bPaginate bLengthChange bFilter bSort bSortMulti bInfo bProcessing bAutoWidth bSortClasses bServerSide bDeferRender".split(" "));E(p,d,["asStripeClasses","ajax","fnServerData","fnFormatNumber","sServerMethod","aaSorting","aaSortingFixed","aLengthMenu","sPaginationType","sAjaxSource","sAjaxDataProp","iStateDuration","sDom","bSortCellsTop","iTabIndex","fnStateLoadCallback","fnStateSaveCallback",
"renderer","searchDelay",["iCookieDuration","iStateDuration"],["oSearch","oPreviousSearch"],["aoSearchCols","aoPreSearchCols"],["iDisplayLength","_iDisplayLength"],["bJQueryUI","bJUI"]]);E(p.oScroll,d,[["sScrollX","sX"],["sScrollXInner","sXInner"],["sScrollY","sY"],["bScrollCollapse","bCollapse"]]);E(p.oLanguage,d,"fnInfoCallback");z(p,"aoDrawCallback",d.fnDrawCallback,"user");z(p,"aoServerParams",d.fnServerParams,"user");z(p,"aoStateSaveParams",d.fnStateSaveParams,"user");z(p,"aoStateLoadParams",
d.fnStateLoadParams,"user");z(p,"aoStateLoaded",d.fnStateLoaded,"user");z(p,"aoRowCallback",d.fnRowCallback,"user");z(p,"aoRowCreatedCallback",d.fnCreatedRow,"user");z(p,"aoHeaderCallback",d.fnHeaderCallback,"user");z(p,"aoFooterCallback",d.fnFooterCallback,"user");z(p,"aoInitComplete",d.fnInitComplete,"user");z(p,"aoPreDrawCallback",d.fnPreDrawCallback,"user");i=p.oClasses;d.bJQueryUI?(h.extend(i,m.ext.oJUIClasses,d.oClasses),d.sDom===l.sDom&&"lfrtip"===l.sDom&&(p.sDom='<"H"lfr>t<"F"ip>'),p.renderer)?
h.isPlainObject(p.renderer)&&!p.renderer.header&&(p.renderer.header="jqueryui"):p.renderer="jqueryui":h.extend(i,m.ext.classes,d.oClasses);q.addClass(i.sTable);if(""!==p.oScroll.sX||""!==p.oScroll.sY)p.oScroll.iBarWidth=Hb();!0===p.oScroll.sX&&(p.oScroll.sX="100%");p.iInitDisplayStart===k&&(p.iInitDisplayStart=d.iDisplayStart,p._iDisplayStart=d.iDisplayStart);null!==d.iDeferLoading&&(p.bDeferLoading=!0,g=h.isArray(d.iDeferLoading),p._iRecordsDisplay=g?d.iDeferLoading[0]:d.iDeferLoading,p._iRecordsTotal=
g?d.iDeferLoading[1]:d.iDeferLoading);var t=p.oLanguage;h.extend(!0,t,d.oLanguage);""!==t.sUrl&&(h.ajax({dataType:"json",url:t.sUrl,success:function(a){P(a);H(l.oLanguage,a);h.extend(true,t,a);ga(p)},error:function(){ga(p)}}),o=!0);null===d.asStripeClasses&&(p.asStripeClasses=[i.sStripeOdd,i.sStripeEven]);var g=p.asStripeClasses,s=q.children("tbody").find("tr").eq(0);-1!==h.inArray(!0,h.map(g,function(a){return s.hasClass(a)}))&&(h("tbody tr",this).removeClass(g.join(" ")),p.asDestroyStripes=g.slice());
n=[];g=this.getElementsByTagName("thead");0!==g.length&&(da(p.aoHeader,g[0]),n=qa(p));if(null===d.aoColumns){r=[];g=0;for(j=n.length;g<j;g++)r.push(null)}else r=d.aoColumns;g=0;for(j=r.length;g<j;g++)Fa(p,n?n[g]:null);ib(p,d.aoColumnDefs,r,function(a,b){ka(p,a,b)});if(s.length){var u=function(a,b){return a.getAttribute("data-"+b)!==null?b:null};h.each(na(p,s[0]).cells,function(a,b){var c=p.aoColumns[a];if(c.mData===a){var d=u(b,"sort")||u(b,"order"),e=u(b,"filter")||u(b,"search");if(d!==null||e!==
null){c.mData={_:a+".display",sort:d!==null?a+".@data-"+d:k,type:d!==null?a+".@data-"+d:k,filter:e!==null?a+".@data-"+e:k};ka(p,a)}}})}var v=p.oFeatures;d.bStateSave&&(v.bStateSave=!0,Kb(p,d),z(p,"aoDrawCallback",ya,"state_save"));if(d.aaSorting===k){n=p.aaSorting;g=0;for(j=n.length;g<j;g++)n[g][1]=p.aoColumns[g].asSorting[0]}xa(p);v.bSort&&z(p,"aoDrawCallback",function(){if(p.bSorted){var a=U(p),b={};h.each(a,function(a,c){b[c.src]=c.dir});w(p,null,"order",[p,a,b]);Jb(p)}});z(p,"aoDrawCallback",
function(){(p.bSorted||B(p)==="ssp"||v.bDeferRender)&&xa(p)},"sc");gb(p);g=q.children("caption").each(function(){this._captionSide=q.css("caption-side")});j=q.children("thead");0===j.length&&(j=h("<thead/>").appendTo(this));p.nTHead=j[0];j=q.children("tbody");0===j.length&&(j=h("<tbody/>").appendTo(this));p.nTBody=j[0];j=q.children("tfoot");if(0===j.length&&0<g.length&&(""!==p.oScroll.sX||""!==p.oScroll.sY))j=h("<tfoot/>").appendTo(this);0===j.length||0===j.children().length?q.addClass(i.sNoFooter):
0<j.length&&(p.nTFoot=j[0],da(p.aoFooter,p.nTFoot));if(d.aaData)for(g=0;g<d.aaData.length;g++)K(p,d.aaData[g]);else(p.bDeferLoading||"dom"==B(p))&&ma(p,h(p.nTBody).children("tr"));p.aiDisplay=p.aiDisplayMaster.slice();p.bInitialised=!0;!1===o&&ga(p)}});b=null;return this};var Tb=[],y=Array.prototype,cc=function(a){var b,c,e=m.settings,d=h.map(e,function(a){return a.nTable});if(a){if(a.nTable&&a.oApi)return[a];if(a.nodeName&&"table"===a.nodeName.toLowerCase())return b=h.inArray(a,d),-1!==b?[e[b]]:
null;if(a&&"function"===typeof a.settings)return a.settings().toArray();"string"===typeof a?c=h(a):a instanceof h&&(c=a)}else return[];if(c)return c.map(function(){b=h.inArray(this,d);return-1!==b?e[b]:null}).toArray()};t=function(a,b){if(!(this instanceof t))return new t(a,b);var c=[],e=function(a){(a=cc(a))&&c.push.apply(c,a)};if(h.isArray(a))for(var d=0,f=a.length;d<f;d++)e(a[d]);else e(a);this.context=Na(c);b&&this.push.apply(this,b.toArray?b.toArray():b);this.selector={rows:null,cols:null,opts:null};
t.extend(this,this,Tb)};m.Api=t;t.prototype={any:function(){return 0!==this.flatten().length},concat:y.concat,context:[],each:function(a){for(var b=0,c=this.length;b<c;b++)a.call(this,this[b],b,this);return this},eq:function(a){var b=this.context;return b.length>a?new t(b[a],this[a]):null},filter:function(a){var b=[];if(y.filter)b=y.filter.call(this,a,this);else for(var c=0,e=this.length;c<e;c++)a.call(this,this[c],c,this)&&b.push(this[c]);return new t(this.context,b)},flatten:function(){var a=[];
return new t(this.context,a.concat.apply(a,this.toArray()))},join:y.join,indexOf:y.indexOf||function(a,b){for(var c=b||0,e=this.length;c<e;c++)if(this[c]===a)return c;return-1},iterator:function(a,b,c,e){var d=[],f,g,h,i,o,l=this.context,q,n,m=this.selector;"string"===typeof a&&(e=c,c=b,b=a,a=!1);g=0;for(h=l.length;g<h;g++){var p=new t(l[g]);if("table"===b)f=c.call(p,l[g],g),f!==k&&d.push(f);else if("columns"===b||"rows"===b)f=c.call(p,l[g],this[g],g),f!==k&&d.push(f);else if("column"===b||"column-rows"===
b||"row"===b||"cell"===b){n=this[g];"column-rows"===b&&(q=Ca(l[g],m.opts));i=0;for(o=n.length;i<o;i++)f=n[i],f="cell"===b?c.call(p,l[g],f.row,f.column,g,i):c.call(p,l[g],f,g,i,q),f!==k&&d.push(f)}}return d.length||e?(a=new t(l,a?d.concat.apply([],d):d),b=a.selector,b.rows=m.rows,b.cols=m.cols,b.opts=m.opts,a):this},lastIndexOf:y.lastIndexOf||function(a,b){return this.indexOf.apply(this.toArray.reverse(),arguments)},length:0,map:function(a){var b=[];if(y.map)b=y.map.call(this,a,this);else for(var c=
0,e=this.length;c<e;c++)b.push(a.call(this,this[c],c));return new t(this.context,b)},pluck:function(a){return this.map(function(b){return b[a]})},pop:y.pop,push:y.push,reduce:y.reduce||function(a,b){return hb(this,a,b,0,this.length,1)},reduceRight:y.reduceRight||function(a,b){return hb(this,a,b,this.length-1,-1,-1)},reverse:y.reverse,selector:null,shift:y.shift,sort:y.sort,splice:y.splice,toArray:function(){return y.slice.call(this)},to$:function(){return h(this)},toJQuery:function(){return h(this)},
unique:function(){return new t(this.context,Na(this))},unshift:y.unshift};t.extend=function(a,b,c){if(c.length&&b&&(b instanceof t||b.__dt_wrapper)){var e,d,f,g=function(a,b,c){return function(){var d=b.apply(a,arguments);t.extend(d,d,c.methodExt);return d}};e=0;for(d=c.length;e<d;e++)f=c[e],b[f.name]="function"===typeof f.val?g(a,f.val,f):h.isPlainObject(f.val)?{}:f.val,b[f.name].__dt_wrapper=!0,t.extend(a,b[f.name],f.propExt)}};t.register=r=function(a,b){if(h.isArray(a))for(var c=0,e=a.length;c<
e;c++)t.register(a[c],b);else for(var d=a.split("."),f=Tb,g,j,c=0,e=d.length;c<e;c++){g=(j=-1!==d[c].indexOf("()"))?d[c].replace("()",""):d[c];var i;a:{i=0;for(var o=f.length;i<o;i++)if(f[i].name===g){i=f[i];break a}i=null}i||(i={name:g,val:{},methodExt:[],propExt:[]},f.push(i));c===e-1?i.val=b:f=j?i.methodExt:i.propExt}};t.registerPlural=v=function(a,b,c){t.register(a,c);t.register(b,function(){var a=c.apply(this,arguments);return a===this?this:a instanceof t?a.length?h.isArray(a[0])?new t(a.context,
a[0]):a[0]:k:a})};r("tables()",function(a){var b;if(a){b=t;var c=this.context;if("number"===typeof a)a=[c[a]];else var e=h.map(c,function(a){return a.nTable}),a=h(e).filter(a).map(function(){var a=h.inArray(this,e);return c[a]}).toArray();b=new b(a)}else b=this;return b});r("table()",function(a){var a=this.tables(a),b=a.context;return b.length?new t(b[0]):a});v("tables().nodes()","table().node()",function(){return this.iterator("table",function(a){return a.nTable},1)});v("tables().body()","table().body()",
function(){return this.iterator("table",function(a){return a.nTBody},1)});v("tables().header()","table().header()",function(){return this.iterator("table",function(a){return a.nTHead},1)});v("tables().footer()","table().footer()",function(){return this.iterator("table",function(a){return a.nTFoot},1)});v("tables().containers()","table().container()",function(){return this.iterator("table",function(a){return a.nTableWrapper},1)});r("draw()",function(a){return this.iterator("table",function(b){N(b,
!1===a)})});r("page()",function(a){return a===k?this.page.info().page:this.iterator("table",function(b){Ta(b,a)})});r("page.info()",function(){if(0===this.context.length)return k;var a=this.context[0],b=a._iDisplayStart,c=a._iDisplayLength,e=a.fnRecordsDisplay(),d=-1===c;return{page:d?0:Math.floor(b/c),pages:d?1:Math.ceil(e/c),start:b,end:a.fnDisplayEnd(),length:c,recordsTotal:a.fnRecordsTotal(),recordsDisplay:e}});r("page.len()",function(a){return a===k?0!==this.context.length?this.context[0]._iDisplayLength:
k:this.iterator("table",function(b){Ra(b,a)})});var Ub=function(a,b,c){if(c){var e=new t(a);e.one("draw",function(){c(e.ajax.json())})}"ssp"==B(a)?N(a,b):(C(a,!0),ra(a,[],function(c){oa(a);for(var c=sa(a,c),e=0,g=c.length;e<g;e++)K(a,c[e]);N(a,b);C(a,!1)}))};r("ajax.json()",function(){var a=this.context;if(0<a.length)return a[0].json});r("ajax.params()",function(){var a=this.context;if(0<a.length)return a[0].oAjaxData});r("ajax.reload()",function(a,b){return this.iterator("table",function(c){Ub(c,
!1===b,a)})});r("ajax.url()",function(a){var b=this.context;if(a===k){if(0===b.length)return k;b=b[0];return b.ajax?h.isPlainObject(b.ajax)?b.ajax.url:b.ajax:b.sAjaxSource}return this.iterator("table",function(b){h.isPlainObject(b.ajax)?b.ajax.url=a:b.ajax=a})});r("ajax.url().load()",function(a,b){return this.iterator("table",function(c){Ub(c,!1===b,a)})});var $a=function(a,b,c,e,d){var f=[],g,j,i,o,l,q;i=typeof b;if(!b||"string"===i||"function"===i||b.length===k)b=[b];i=0;for(o=b.length;i<o;i++){j=
b[i]&&b[i].split?b[i].split(","):[b[i]];l=0;for(q=j.length;l<q;l++)(g=c("string"===typeof j[l]?h.trim(j[l]):j[l]))&&g.length&&f.push.apply(f,g)}a=u.selector[a];if(a.length){i=0;for(o=a.length;i<o;i++)f=a[i](e,d,f)}return f},ab=function(a){a||(a={});a.filter&&a.search===k&&(a.search=a.filter);return h.extend({search:"none",order:"current",page:"all"},a)},bb=function(a){for(var b=0,c=a.length;b<c;b++)if(0<a[b].length)return a[0]=a[b],a[0].length=1,a.length=1,a.context=[a.context[b]],a;a.length=0;return a},
Ca=function(a,b){var c,e,d,f=[],g=a.aiDisplay;c=a.aiDisplayMaster;var j=b.search;e=b.order;d=b.page;if("ssp"==B(a))return"removed"===j?[]:V(0,c.length);if("current"==d){c=a._iDisplayStart;for(e=a.fnDisplayEnd();c<e;c++)f.push(g[c])}else if("current"==e||"applied"==e)f="none"==j?c.slice():"applied"==j?g.slice():h.map(c,function(a){return-1===h.inArray(a,g)?a:null});else if("index"==e||"original"==e){c=0;for(e=a.aoData.length;c<e;c++)"none"==j?f.push(c):(d=h.inArray(c,g),(-1===d&&"removed"==j||0<=d&&
"applied"==j)&&f.push(c))}return f};r("rows()",function(a,b){a===k?a="":h.isPlainObject(a)&&(b=a,a="");var b=ab(b),c=this.iterator("table",function(c){var d=b;return $a("row",a,function(a){var b=Pb(a);if(b!==null&&!d)return[b];var j=Ca(c,d);if(b!==null&&h.inArray(b,j)!==-1)return[b];if(!a)return j;if(typeof a==="function")return h.map(j,function(b){var d=c.aoData[b];return a(b,d._aData,d.nTr)?b:null});b=Sb(ia(c.aoData,j,"nTr"));return a.nodeName&&h.inArray(a,b)!==-1?[a._DT_RowIndex]:h(b).filter(a).map(function(){return this._DT_RowIndex}).toArray()},
c,d)},1);c.selector.rows=a;c.selector.opts=b;return c});r("rows().nodes()",function(){return this.iterator("row",function(a,b){return a.aoData[b].nTr||k},1)});r("rows().data()",function(){return this.iterator(!0,"rows",function(a,b){return ia(a.aoData,b,"_aData")},1)});v("rows().cache()","row().cache()",function(a){return this.iterator("row",function(b,c){var e=b.aoData[c];return"search"===a?e._aFilterData:e._aSortData},1)});v("rows().invalidate()","row().invalidate()",function(a){return this.iterator("row",
function(b,c){ca(b,c,a)})});v("rows().indexes()","row().index()",function(){return this.iterator("row",function(a,b){return b},1)});v("rows().remove()","row().remove()",function(){var a=this;return this.iterator("row",function(b,c,e){var d=b.aoData;d.splice(c,1);for(var f=0,g=d.length;f<g;f++)null!==d[f].nTr&&(d[f].nTr._DT_RowIndex=f);h.inArray(c,b.aiDisplay);pa(b.aiDisplayMaster,c);pa(b.aiDisplay,c);pa(a[e],c,!1);Sa(b)})});r("rows.add()",function(a){var b=this.iterator("table",function(b){var c,
f,g,h=[];f=0;for(g=a.length;f<g;f++)c=a[f],c.nodeName&&"TR"===c.nodeName.toUpperCase()?h.push(ma(b,c)[0]):h.push(K(b,c));return h},1),c=this.rows(-1);c.pop();c.push.apply(c,b.toArray());return c});r("row()",function(a,b){return bb(this.rows(a,b))});r("row().data()",function(a){var b=this.context;if(a===k)return b.length&&this.length?b[0].aoData[this[0]]._aData:k;b[0].aoData[this[0]]._aData=a;ca(b[0],this[0],"data");return this});r("row().node()",function(){var a=this.context;return a.length&&this.length?
a[0].aoData[this[0]].nTr||null:null});r("row.add()",function(a){a instanceof h&&a.length&&(a=a[0]);var b=this.iterator("table",function(b){return a.nodeName&&"TR"===a.nodeName.toUpperCase()?ma(b,a)[0]:K(b,a)});return this.row(b[0])});var cb=function(a,b){var c=a.context;c.length&&(c=c[0].aoData[b!==k?b:a[0]],c._details&&(c._details.remove(),c._detailsShow=k,c._details=k))},Vb=function(a,b){var c=a.context;if(c.length&&a.length){var e=c[0].aoData[a[0]];if(e._details){(e._detailsShow=b)?e._details.insertAfter(e.nTr):
e._details.detach();var d=c[0],f=new t(d),g=d.aoData;f.off("draw.dt.DT_details column-visibility.dt.DT_details destroy.dt.DT_details");0<D(g,"_details").length&&(f.on("draw.dt.DT_details",function(a,b){d===b&&f.rows({page:"current"}).eq(0).each(function(a){a=g[a];a._detailsShow&&a._details.insertAfter(a.nTr)})}),f.on("column-visibility.dt.DT_details",function(a,b){if(d===b)for(var c,e=aa(b),f=0,h=g.length;f<h;f++)c=g[f],c._details&&c._details.children("td[colspan]").attr("colspan",e)}),f.on("destroy.dt.DT_details",
function(a,b){if(d===b)for(var c=0,e=g.length;c<e;c++)g[c]._details&&cb(f,c)}))}}};r("row().child()",function(a,b){var c=this.context;if(a===k)return c.length&&this.length?c[0].aoData[this[0]]._details:k;if(!0===a)this.child.show();else if(!1===a)cb(this);else if(c.length&&this.length){var e=c[0],c=c[0].aoData[this[0]],d=[],f=function(a,b){if(h.isArray(a)||a instanceof h)for(var c=0,k=a.length;c<k;c++)f(a[c],b);else a.nodeName&&"tr"===a.nodeName.toLowerCase()?d.push(a):(c=h("<tr><td/></tr>").addClass(b),
h("td",c).addClass(b).html(a)[0].colSpan=aa(e),d.push(c[0]))};f(a,b);c._details&&c._details.remove();c._details=h(d);c._detailsShow&&c._details.insertAfter(c.nTr)}return this});r(["row().child.show()","row().child().show()"],function(){Vb(this,!0);return this});r(["row().child.hide()","row().child().hide()"],function(){Vb(this,!1);return this});r(["row().child.remove()","row().child().remove()"],function(){cb(this);return this});r("row().child.isShown()",function(){var a=this.context;return a.length&&
this.length?a[0].aoData[this[0]]._detailsShow||!1:!1});var dc=/^(.+):(name|visIdx|visible)$/,Wb=function(a,b,c,e,d){for(var c=[],e=0,f=d.length;e<f;e++)c.push(x(a,d[e],b));return c};r("columns()",function(a,b){a===k?a="":h.isPlainObject(a)&&(b=a,a="");var b=ab(b),c=this.iterator("table",function(c){var d=a,f=b,g=c.aoColumns,j=D(g,"sName"),i=D(g,"nTh");return $a("column",d,function(a){var b=Pb(a);if(a==="")return V(g.length);if(b!==null)return[b>=0?b:g.length+b];if(typeof a==="function"){var d=Ca(c,
f);return h.map(g,function(b,f){return a(f,Wb(c,f,0,0,d),i[f])?f:null})}var k=typeof a==="string"?a.match(dc):"";if(k)switch(k[2]){case "visIdx":case "visible":b=parseInt(k[1],10);if(b<0){var m=h.map(g,function(a,b){return a.bVisible?b:null});return[m[m.length+b]]}return[la(c,b)];case "name":return h.map(j,function(a,b){return a===k[1]?b:null})}else return h(i).filter(a).map(function(){return h.inArray(this,i)}).toArray()},c,f)},1);c.selector.cols=a;c.selector.opts=b;return c});v("columns().header()",
"column().header()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTh},1)});v("columns().footer()","column().footer()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTf},1)});v("columns().data()","column().data()",function(){return this.iterator("column-rows",Wb,1)});v("columns().dataSrc()","column().dataSrc()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].mData},1)});v("columns().cache()","column().cache()",
function(a){return this.iterator("column-rows",function(b,c,e,d,f){return ia(b.aoData,f,"search"===a?"_aFilterData":"_aSortData",c)},1)});v("columns().nodes()","column().nodes()",function(){return this.iterator("column-rows",function(a,b,c,e,d){return ia(a.aoData,d,"anCells",b)},1)});v("columns().visible()","column().visible()",function(a,b){return this.iterator("column",function(c,e){if(a===k)return c.aoColumns[e].bVisible;var d=c.aoColumns,f=d[e],g=c.aoData,j,i,m;if(a!==k&&f.bVisible!==a){if(a){var l=
h.inArray(!0,D(d,"bVisible"),e+1);j=0;for(i=g.length;j<i;j++)m=g[j].nTr,d=g[j].anCells,m&&m.insertBefore(d[e],d[l]||null)}else h(D(c.aoData,"anCells",e)).detach();f.bVisible=a;ea(c,c.aoHeader);ea(c,c.aoFooter);if(b===k||b)X(c),(c.oScroll.sX||c.oScroll.sY)&&Y(c);w(c,null,"column-visibility",[c,e,a]);ya(c)}})});v("columns().indexes()","column().index()",function(a){return this.iterator("column",function(b,c){return"visible"===a?$(b,c):c},1)});r("columns.adjust()",function(){return this.iterator("table",
function(a){X(a)},1)});r("column.index()",function(a,b){if(0!==this.context.length){var c=this.context[0];if("fromVisible"===a||"toData"===a)return la(c,b);if("fromData"===a||"toVisible"===a)return $(c,b)}});r("column()",function(a,b){return bb(this.columns(a,b))});r("cells()",function(a,b,c){h.isPlainObject(a)&&(a.row===k?(c=a,a=null):(c=b,b=null));h.isPlainObject(b)&&(c=b,b=null);if(null===b||b===k)return this.iterator("table",function(b){var d=a,e=ab(c),f=b.aoData,g=Ca(b,e),i=Sb(ia(f,g,"anCells")),
j=h([].concat.apply([],i)),l,m=b.aoColumns.length,o,r,t,s,u,v;return $a("cell",d,function(a){var c=typeof a==="function";if(a===null||a===k||c){o=[];r=0;for(t=g.length;r<t;r++){l=g[r];for(s=0;s<m;s++){u={row:l,column:s};if(c){v=b.aoData[l];a(u,x(b,l,s),v.anCells?v.anCells[s]:null)&&o.push(u)}else o.push(u)}}return o}return h.isPlainObject(a)?[a]:j.filter(a).map(function(a,b){l=b.parentNode._DT_RowIndex;return{row:l,column:h.inArray(b,f[l].anCells)}}).toArray()},b,e)});var e=this.columns(b,c),d=this.rows(a,
c),f,g,j,i,m,l=this.iterator("table",function(a,b){f=[];g=0;for(j=d[b].length;g<j;g++){i=0;for(m=e[b].length;i<m;i++)f.push({row:d[b][g],column:e[b][i]})}return f},1);h.extend(l.selector,{cols:b,rows:a,opts:c});return l});v("cells().nodes()","cell().node()",function(){return this.iterator("cell",function(a,b,c){return(a=a.aoData[b].anCells)?a[c]:k},1)});r("cells().data()",function(){return this.iterator("cell",function(a,b,c){return x(a,b,c)},1)});v("cells().cache()","cell().cache()",function(a){a=
"search"===a?"_aFilterData":"_aSortData";return this.iterator("cell",function(b,c,e){return b.aoData[c][a][e]},1)});v("cells().render()","cell().render()",function(a){return this.iterator("cell",function(b,c,e){return x(b,c,e,a)},1)});v("cells().indexes()","cell().index()",function(){return this.iterator("cell",function(a,b,c){return{row:b,column:c,columnVisible:$(a,c)}},1)});v("cells().invalidate()","cell().invalidate()",function(a){return this.iterator("cell",function(b,c,e){ca(b,c,a,e)})});r("cell()",
function(a,b,c){return bb(this.cells(a,b,c))});r("cell().data()",function(a){var b=this.context,c=this[0];if(a===k)return b.length&&c.length?x(b[0],c[0].row,c[0].column):k;Ia(b[0],c[0].row,c[0].column,a);ca(b[0],c[0].row,"data",c[0].column);return this});r("order()",function(a,b){var c=this.context;if(a===k)return 0!==c.length?c[0].aaSorting:k;"number"===typeof a?a=[[a,b]]:h.isArray(a[0])||(a=Array.prototype.slice.call(arguments));return this.iterator("table",function(b){b.aaSorting=a.slice()})});
r("order.listener()",function(a,b,c){return this.iterator("table",function(e){Oa(e,a,b,c)})});r(["columns().order()","column().order()"],function(a){var b=this;return this.iterator("table",function(c,e){var d=[];h.each(b[e],function(b,c){d.push([c,a])});c.aaSorting=d})});r("search()",function(a,b,c,e){var d=this.context;return a===k?0!==d.length?d[0].oPreviousSearch.sSearch:k:this.iterator("table",function(d){d.oFeatures.bFilter&&fa(d,h.extend({},d.oPreviousSearch,{sSearch:a+"",bRegex:null===b?!1:
b,bSmart:null===c?!0:c,bCaseInsensitive:null===e?!0:e}),1)})});v("columns().search()","column().search()",function(a,b,c,e){return this.iterator("column",function(d,f){var g=d.aoPreSearchCols;if(a===k)return g[f].sSearch;d.oFeatures.bFilter&&(h.extend(g[f],{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:c,bCaseInsensitive:null===e?!0:e}),fa(d,d.oPreviousSearch,1))})});r("state()",function(){return this.context.length?this.context[0].oSavedState:null});r("state.clear()",function(){return this.iterator("table",
function(a){a.fnStateSaveCallback.call(a.oInstance,a,{})})});r("state.loaded()",function(){return this.context.length?this.context[0].oLoadedState:null});r("state.save()",function(){return this.iterator("table",function(a){ya(a)})});m.versionCheck=m.fnVersionCheck=function(a){for(var b=m.version.split("."),a=a.split("."),c,e,d=0,f=a.length;d<f;d++)if(c=parseInt(b[d],10)||0,e=parseInt(a[d],10)||0,c!==e)return c>e;return!0};m.isDataTable=m.fnIsDataTable=function(a){var b=h(a).get(0),c=!1;h.each(m.settings,
function(a,d){var f=d.nScrollHead?h("table",d.nScrollHead)[0]:null,g=d.nScrollFoot?h("table",d.nScrollFoot)[0]:null;if(d.nTable===b||f===b||g===b)c=!0});return c};m.tables=m.fnTables=function(a){return h.map(m.settings,function(b){if(!a||a&&h(b.nTable).is(":visible"))return b.nTable})};m.util={throttle:ua,escapeRegex:va};m.camelToHungarian=H;r("$()",function(a,b){var c=this.rows(b).nodes(),c=h(c);return h([].concat(c.filter(a).toArray(),c.find(a).toArray()))});h.each(["on","one","off"],function(a,
b){r(b+"()",function(){var a=Array.prototype.slice.call(arguments);a[0].match(/\.dt\b/)||(a[0]+=".dt");var e=h(this.tables().nodes());e[b].apply(e,a);return this})});r("clear()",function(){return this.iterator("table",function(a){oa(a)})});r("settings()",function(){return new t(this.context,this.context)});r("init()",function(){var a=this.context;return a.length?a[0].oInit:null});r("data()",function(){return this.iterator("table",function(a){return D(a.aoData,"_aData")}).flatten()});r("destroy()",
function(a){a=a||!1;return this.iterator("table",function(b){var c=b.nTableWrapper.parentNode,e=b.oClasses,d=b.nTable,f=b.nTBody,g=b.nTHead,j=b.nTFoot,i=h(d),f=h(f),k=h(b.nTableWrapper),l=h.map(b.aoData,function(a){return a.nTr}),q;b.bDestroying=!0;w(b,"aoDestroyCallback","destroy",[b]);a||(new t(b)).columns().visible(!0);k.unbind(".DT").find(":not(tbody *)").unbind(".DT");h(Ea).unbind(".DT-"+b.sInstance);d!=g.parentNode&&(i.children("thead").detach(),i.append(g));j&&d!=j.parentNode&&(i.children("tfoot").detach(),
i.append(j));i.detach();k.detach();b.aaSorting=[];b.aaSortingFixed=[];xa(b);h(l).removeClass(b.asStripeClasses.join(" "));h("th, td",g).removeClass(e.sSortable+" "+e.sSortableAsc+" "+e.sSortableDesc+" "+e.sSortableNone);b.bJUI&&(h("th span."+e.sSortIcon+", td span."+e.sSortIcon,g).detach(),h("th, td",g).each(function(){var a=h("div."+e.sSortJUIWrapper,this);h(this).append(a.contents());a.detach()}));!a&&c&&c.insertBefore(d,b.nTableReinsertBefore);f.children().detach();f.append(l);i.css("width",b.sDestroyWidth).removeClass(e.sTable);
(q=b.asDestroyStripes.length)&&f.children().each(function(a){h(this).addClass(b.asDestroyStripes[a%q])});c=h.inArray(b,m.settings);-1!==c&&m.settings.splice(c,1)})});h.each(["column","row","cell"],function(a,b){r(b+"s().every()",function(a){return this.iterator(b,function(e,d,f){a.call((new t(e))[b](d,f))})})});r("i18n()",function(a,b,c){var e=this.context[0],a=R(a)(e.oLanguage);a===k&&(a=b);c!==k&&h.isPlainObject(a)&&(a=a[c]!==k?a[c]:a._);return a.replace("%d",c)});m.version="1.10.7";m.settings=
[];m.models={};m.models.oSearch={bCaseInsensitive:!0,sSearch:"",bRegex:!1,bSmart:!0};m.models.oRow={nTr:null,anCells:null,_aData:[],_aSortData:null,_aFilterData:null,_sFilterRow:null,_sRowStripe:"",src:null};m.models.oColumn={idx:null,aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bVisible:null,_sManualType:null,_bAttrSrc:!1,fnCreatedCell:null,fnGetData:null,fnSetData:null,mData:null,mRender:null,nTh:null,nTf:null,sClass:null,sContentPadding:null,sDefaultContent:null,sName:null,sSortDataType:"std",
sSortingClass:null,sSortingClassJUI:null,sTitle:null,sType:null,sWidth:null,sWidthOrig:null};m.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:[],ajax:null,aLengthMenu:[10,25,50,100],aoColumns:null,aoColumnDefs:null,aoSearchCols:[],asStripeClasses:null,bAutoWidth:!0,bDeferRender:!1,bDestroy:!1,bFilter:!0,bInfo:!0,bJQueryUI:!1,bLengthChange:!0,bPaginate:!0,bProcessing:!1,bRetrieve:!1,bScrollCollapse:!1,bServerSide:!1,bSort:!0,bSortMulti:!0,bSortCellsTop:!1,bSortClasses:!0,bStateSave:!1,
fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(a){return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g,this.oLanguage.sThousands)},fnHeaderCallback:null,fnInfoCallback:null,fnInitComplete:null,fnPreDrawCallback:null,fnRowCallback:null,fnServerData:null,fnServerParams:null,fnStateLoadCallback:function(a){try{return JSON.parse((-1===a.iStateDuration?sessionStorage:localStorage).getItem("DataTables_"+a.sInstance+"_"+location.pathname))}catch(b){}},fnStateLoadParams:null,
fnStateLoaded:null,fnStateSaveCallback:function(a,b){try{(-1===a.iStateDuration?sessionStorage:localStorage).setItem("DataTables_"+a.sInstance+"_"+location.pathname,JSON.stringify(b))}catch(c){}},fnStateSaveParams:null,iStateDuration:7200,iDeferLoading:null,iDisplayLength:10,iDisplayStart:0,iTabIndex:0,oClasses:{},oLanguage:{oAria:{sSortAscending:": activate to sort column ascending",sSortDescending:": activate to sort column descending"},oPaginate:{sFirst:"First",sLast:"Last",sNext:"Next",sPrevious:"Previous"},
sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sDecimal:"",sThousands:",",sLengthMenu:"Show _MENU_ entries",sLoadingRecords:"Loading...",sProcessing:"Processing...",sSearch:"Search:",sSearchPlaceholder:"",sUrl:"",sZeroRecords:"No matching records found"},oSearch:h.extend({},m.models.oSearch),sAjaxDataProp:"data",sAjaxSource:null,sDom:"lfrtip",searchDelay:null,
sPaginationType:"simple_numbers",sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET",renderer:null};W(m.defaults);m.defaults.column={aDataSort:null,iDataSort:-1,asSorting:["asc","desc"],bSearchable:!0,bSortable:!0,bVisible:!0,fnCreatedCell:null,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null};W(m.defaults.column);m.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender:null,bFilter:null,
bInfo:null,bLengthChange:null,bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortMulti:null,bSortClasses:null,bStateSave:null},oScroll:{bCollapse:null,iBarWidth:0,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1,bScrollbarLeft:!1},ajax:null,aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aoColumns:[],aoHeader:[],aoFooter:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],asStripeClasses:null,asDestroyStripes:[],
sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,searchDelay:null,sPaginationType:"two_button",iStateDuration:0,aoStateSave:[],aoStateLoad:[],oSavedState:null,oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,
bAjaxDataGet:!0,jqXHR:null,json:k,oAjaxData:k,fnServerData:null,aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,bJUI:null,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return"ssp"==B(this)?1*this._iRecordsTotal:this.aiDisplayMaster.length},fnRecordsDisplay:function(){return"ssp"==B(this)?1*this._iRecordsDisplay:
this.aiDisplay.length},fnDisplayEnd:function(){var a=this._iDisplayLength,b=this._iDisplayStart,c=b+a,e=this.aiDisplay.length,d=this.oFeatures,f=d.bPaginate;return d.bServerSide?!1===f||-1===a?b+e:Math.min(b+a,this._iRecordsDisplay):!f||c>e||-1===a?e:c},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null,aLastSort:[],oPlugins:{}};m.ext=u={buttons:{},classes:{},errMode:"alert",feature:[],search:[],selector:{cell:[],column:[],row:[]},internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},
header:{}},order:{},type:{detect:[],search:{},order:{}},_unique:0,fnVersionCheck:m.fnVersionCheck,iApiIndex:0,oJUIClasses:{},sVersion:m.version};h.extend(u,{afnFiltering:u.search,aTypes:u.type.detect,ofnSearch:u.type.search,oSort:u.type.order,afnSortData:u.order,aoFeatures:u.feature,oApi:u.internal,oStdClasses:u.classes,oPagination:u.pager});h.extend(m.ext.classes,{sTable:"dataTable",sNoFooter:"no-footer",sPageButton:"paginate_button",sPageButtonActive:"current",sPageButtonDisabled:"disabled",sStripeOdd:"odd",
sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sFilterInput:"",sLengthSelect:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",
sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sHeaderTH:"",sFooterTH:"",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sJUIHeader:"",sJUIFooter:""});var Da="",Da="",F=Da+"ui-state-default",ja=Da+"css_right ui-icon ui-icon-",Xb=Da+"fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix";h.extend(m.ext.oJUIClasses,
m.ext.classes,{sPageButton:"fg-button ui-button "+F,sPageButtonActive:"ui-state-disabled",sPageButtonDisabled:"ui-state-disabled",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sSortAsc:F+" sorting_asc",sSortDesc:F+" sorting_desc",sSortable:F+" sorting",sSortableAsc:F+" sorting_asc_disabled",sSortableDesc:F+" sorting_desc_disabled",sSortableNone:F+" sorting_disabled",sSortJUIAsc:ja+"triangle-1-n",sSortJUIDesc:ja+"triangle-1-s",sSortJUI:ja+"carat-2-n-s",
sSortJUIAscAllowed:ja+"carat-1-n",sSortJUIDescAllowed:ja+"carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",sSortIcon:"DataTables_sort_icon",sScrollHead:"dataTables_scrollHead "+F,sScrollFoot:"dataTables_scrollFoot "+F,sHeaderTH:F,sFooterTH:F,sJUIHeader:Xb+" ui-corner-tl ui-corner-tr",sJUIFooter:Xb+" ui-corner-bl ui-corner-br"});var Mb=m.ext.pager;h.extend(Mb,{simple:function(){return["previous","next"]},full:function(){return["first","previous","next","last"]},simple_numbers:function(a,b){return["previous",
Wa(a,b),"next"]},full_numbers:function(a,b){return["first","previous",Wa(a,b),"next","last"]},_numbers:Wa,numbers_length:7});h.extend(!0,m.ext.renderer,{pageButton:{_:function(a,b,c,e,d,f){var g=a.oClasses,j=a.oLanguage.oPaginate,i,k,l=0,m=function(b,e){var n,r,t,s,u=function(b){Ta(a,b.data.action,true)};n=0;for(r=e.length;n<r;n++){s=e[n];if(h.isArray(s)){t=h("<"+(s.DT_el||"div")+"/>").appendTo(b);m(t,s)}else{k=i="";switch(s){case "ellipsis":b.append('<span class="ellipsis">&#x2026;</span>');break;
case "first":i=j.sFirst;k=s+(d>0?"":" "+g.sPageButtonDisabled);break;case "previous":i=j.sPrevious;k=s+(d>0?"":" "+g.sPageButtonDisabled);break;case "next":i=j.sNext;k=s+(d<f-1?"":" "+g.sPageButtonDisabled);break;case "last":i=j.sLast;k=s+(d<f-1?"":" "+g.sPageButtonDisabled);break;default:i=s+1;k=d===s?g.sPageButtonActive:""}if(i){t=h("<a>",{"class":g.sPageButton+" "+k,"aria-controls":a.sTableId,"data-dt-idx":l,tabindex:a.iTabIndex,id:c===0&&typeof s==="string"?a.sTableId+"_"+s:null}).html(i).appendTo(b);
Va(t,{action:s},u);l++}}}},n;try{n=h(Q.activeElement).data("dt-idx")}catch(r){}m(h(b).empty(),e);n&&h(b).find("[data-dt-idx="+n+"]").focus()}}});h.extend(m.ext.type.detect,[function(a,b){var c=b.oLanguage.sDecimal;return Za(a,c)?"num"+c:null},function(a){if(a&&!(a instanceof Date)&&(!ac.test(a)||!bc.test(a)))return null;var b=Date.parse(a);return null!==b&&!isNaN(b)||J(a)?"date":null},function(a,b){var c=b.oLanguage.sDecimal;return Za(a,c,!0)?"num-fmt"+c:null},function(a,b){var c=b.oLanguage.sDecimal;
return Rb(a,c)?"html-num"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Rb(a,c,!0)?"html-num-fmt"+c:null},function(a){return J(a)||"string"===typeof a&&-1!==a.indexOf("<")?"html":null}]);h.extend(m.ext.type.search,{html:function(a){return J(a)?a:"string"===typeof a?a.replace(Ob," ").replace(Ba,""):""},string:function(a){return J(a)?a:"string"===typeof a?a.replace(Ob," "):a}});var Aa=function(a,b,c,e){if(0!==a&&(!a||"-"===a))return-Infinity;b&&(a=Qb(a,b));a.replace&&(c&&(a=a.replace(c,"")),
e&&(a=a.replace(e,"")));return 1*a};h.extend(u.type.order,{"date-pre":function(a){return Date.parse(a)||0},"html-pre":function(a){return J(a)?"":a.replace?a.replace(/<.*?>/g,"").toLowerCase():a+""},"string-pre":function(a){return J(a)?"":"string"===typeof a?a.toLowerCase():!a.toString?"":a.toString()},"string-asc":function(a,b){return a<b?-1:a>b?1:0},"string-desc":function(a,b){return a<b?1:a>b?-1:0}});db("");h.extend(!0,m.ext.renderer,{header:{_:function(a,b,c,e){h(a.nTable).on("order.dt.DT",function(d,
f,g,h){if(a===f){d=c.idx;b.removeClass(c.sSortingClass+" "+e.sSortAsc+" "+e.sSortDesc).addClass(h[d]=="asc"?e.sSortAsc:h[d]=="desc"?e.sSortDesc:c.sSortingClass)}})},jqueryui:function(a,b,c,e){h("<div/>").addClass(e.sSortJUIWrapper).append(b.contents()).append(h("<span/>").addClass(e.sSortIcon+" "+c.sSortingClassJUI)).appendTo(b);h(a.nTable).on("order.dt.DT",function(d,f,g,h){if(a===f){d=c.idx;b.removeClass(e.sSortAsc+" "+e.sSortDesc).addClass(h[d]=="asc"?e.sSortAsc:h[d]=="desc"?e.sSortDesc:c.sSortingClass);
b.find("span."+e.sSortIcon).removeClass(e.sSortJUIAsc+" "+e.sSortJUIDesc+" "+e.sSortJUI+" "+e.sSortJUIAscAllowed+" "+e.sSortJUIDescAllowed).addClass(h[d]=="asc"?e.sSortJUIAsc:h[d]=="desc"?e.sSortJUIDesc:c.sSortingClassJUI)}})}}});m.render={number:function(a,b,c,e){return{display:function(d){if("number"!==typeof d&&"string"!==typeof d)return d;var f=0>d?"-":"",d=Math.abs(parseFloat(d)),g=parseInt(d,10),d=c?b+(d-g).toFixed(c).substring(2):"";return f+(e||"")+g.toString().replace(/\B(?=(\d{3})+(?!\d))/g,
a)+d}}}};h.extend(m.ext.internal,{_fnExternApiFunc:Nb,_fnBuildAjax:ra,_fnAjaxUpdate:kb,_fnAjaxParameters:tb,_fnAjaxUpdateDraw:ub,_fnAjaxDataSrc:sa,_fnAddColumn:Fa,_fnColumnOptions:ka,_fnAdjustColumnSizing:X,_fnVisibleToColumnIndex:la,_fnColumnIndexToVisible:$,_fnVisbleColumns:aa,_fnGetColumns:Z,_fnColumnTypes:Ha,_fnApplyColumnDefs:ib,_fnHungarianMap:W,_fnCamelToHungarian:H,_fnLanguageCompat:P,_fnBrowserDetect:gb,_fnAddData:K,_fnAddTr:ma,_fnNodeToDataIndex:function(a,b){return b._DT_RowIndex!==k?b._DT_RowIndex:
null},_fnNodeToColumnIndex:function(a,b,c){return h.inArray(c,a.aoData[b].anCells)},_fnGetCellData:x,_fnSetCellData:Ia,_fnSplitObjNotation:Ka,_fnGetObjectDataFn:R,_fnSetObjectDataFn:S,_fnGetDataMaster:La,_fnClearTable:oa,_fnDeleteIndex:pa,_fnInvalidate:ca,_fnGetRowElements:na,_fnCreateTr:Ja,_fnBuildHead:jb,_fnDrawHead:ea,_fnDraw:M,_fnReDraw:N,_fnAddOptionsHtml:mb,_fnDetectHeader:da,_fnGetUniqueThs:qa,_fnFeatureHtmlFilter:ob,_fnFilterComplete:fa,_fnFilterCustom:xb,_fnFilterColumn:wb,_fnFilter:vb,_fnFilterCreateSearch:Qa,
_fnEscapeRegex:va,_fnFilterData:yb,_fnFeatureHtmlInfo:rb,_fnUpdateInfo:Bb,_fnInfoMacros:Cb,_fnInitialise:ga,_fnInitComplete:ta,_fnLengthChange:Ra,_fnFeatureHtmlLength:nb,_fnFeatureHtmlPaginate:sb,_fnPageChange:Ta,_fnFeatureHtmlProcessing:pb,_fnProcessingDisplay:C,_fnFeatureHtmlTable:qb,_fnScrollDraw:Y,_fnApplyToChildren:G,_fnCalculateColumnWidths:Ga,_fnThrottle:ua,_fnConvertToWidth:Db,_fnScrollingWidthAdjust:Fb,_fnGetWidestNode:Eb,_fnGetMaxLenString:Gb,_fnStringToCss:s,_fnScrollBarWidth:Hb,_fnSortFlatten:U,
_fnSort:lb,_fnSortAria:Jb,_fnSortListener:Ua,_fnSortAttachListener:Oa,_fnSortingClasses:xa,_fnSortData:Ib,_fnSaveState:ya,_fnLoadState:Kb,_fnSettingsFromNode:za,_fnLog:I,_fnMap:E,_fnBindAction:Va,_fnCallbackReg:z,_fnCallbackFire:w,_fnLengthOverflow:Sa,_fnRenderer:Pa,_fnDataSource:B,_fnRowAttributes:Ma,_fnCalculateEnd:function(){}});h.fn.dataTable=m;h.fn.dataTableSettings=m.settings;h.fn.dataTableExt=m.ext;h.fn.DataTable=function(a){return h(this).dataTable(a).api()};h.each(m,function(a,b){h.fn.DataTable[a]=
b});return h.fn.dataTable};"function"===typeof define&&define.amd?define("datatables",["jquery"],P):"object"===typeof exports?module.exports=P(require("jquery")):jQuery&&!jQuery.fn.dataTable&&P(jQuery)})(window,document);

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More