Initial commit
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
48
README.md
Executable file
@@ -0,0 +1,48 @@
|
||||
# THEME - SEPTEMBER 2022 - VSPHIM CMS
|
||||
|
||||
## Demo
|
||||
### Trang Chủ
|
||||

|
||||
|
||||
### Trang Danh Sách Phim
|
||||

|
||||
|
||||
### Trang Thông Tin Phim
|
||||

|
||||
|
||||
### Trang Xem Phim
|
||||

|
||||
|
||||
## Requirements
|
||||
https://github.com/vsphim/vsmov-core
|
||||
|
||||
## Install
|
||||
1. Tại thư mục của Project: `composer require vsmov/vsmov-september`
|
||||
2. Kích hoạt giao diện trong Admin Panel
|
||||
|
||||
## Update
|
||||
1. Tại thư mục của Project: `composer update vsmov/vsmov-september`
|
||||
2. Re-Activate giao diện trong Admin Panel
|
||||
|
||||
## Note
|
||||
- Một vài lưu ý quan trọng của các nút chức năng:
|
||||
+ `Activate` và `Re-Activate` sẽ publish toàn bộ file js,css trong themes ra ngoài public của laravel.
|
||||
+ `Reset` reset lại toàn bộ cấu hình của themes
|
||||
|
||||
## Document
|
||||
### List
|
||||
- Trang chủ: `display_label|relation|find_by_field|value|limit|show_more_url|show_template (slider_poster|slider_thumb|section_poster|section_thumb)`
|
||||
+ Ví dụ theo định dạng: `Phim bộ mới||type|series|12|/danh-sach/phim-bo|section_poster`
|
||||
+ Ví dụ theo định dạng: `Phim lẻ mới||type|single|12|/danh-sach/phim-bo|section_thumb`
|
||||
+ Ví dụ theo thể loại: `Phim hành động|categories|slug|hanh-dong|12|/the-loai/hanh-dong|slider_thumb`
|
||||
+ Ví dụ theo quốc gia: `Phim hàn quốc|regions|slug|han-quoc|12|/quoc-gia/han-quoc|section_poster`
|
||||
+ Ví dụ với các field khác: `Phim chiếu rạp||is_shown_in_theater|1|12||section_poster`
|
||||
|
||||
- Danh sách hot: `Label|relation|find_by_field|value|sort_by_field|sort_algo|limit|show_template (top_text|top_thumb)`
|
||||
+ `Phim sắp chiếu||status|trailer|publish_year|desc|9|top_text`
|
||||
+ `Top phim bộ||type|series|view_total|desc|9|top_thumb`
|
||||
+ `Top phim lẻ||type|single|view_total|desc|9|top_thumb`
|
||||
|
||||
### Custom View Blade
|
||||
- File blade gốc trong Package: `/vendor/vsmov/vsmov-september/resources/views/september`
|
||||
- Copy file cần custom đến: `/resources/views/vendor/themes/september`
|
||||
32
composer.json
Executable file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "vsmov/vsmov-september",
|
||||
"description": "VsMov's september theme",
|
||||
"type": "library",
|
||||
"authors": [
|
||||
{
|
||||
"name": "vsmov",
|
||||
"email": "vsmov@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"laravel/framework": "^6|^7|^8",
|
||||
"ckfinder/ckfinder-laravel-package": "v3.5.2.1",
|
||||
"vsmov/vsmov-core": "^1.0.1"
|
||||
},
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"VsMov\\September\\": "src/",
|
||||
"VsMov\\September\\Database\\Factories\\": "database/factories/",
|
||||
"VsMov\\September\\Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Vsmov\\September\\SeptemberServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"minimum-stability": "stable"
|
||||
}
|
||||
1916
resources/assets/css/styles.css
Normal file
1
resources/assets/css/styles.min.css
vendored
Normal file
2
resources/assets/js/jquery-3.6.0.min.js
vendored
Normal file
BIN
resources/assets/js/plugins/jquery-raty/fonts/raty.eot
Normal file
15
resources/assets/js/plugins/jquery-raty/fonts/raty.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Generated by IcoMoon</metadata>
|
||||
<defs>
|
||||
<font id="raty" horiz-adv-x="512">
|
||||
<font-face units-per-em="512" ascent="480" descent="-32" />
|
||||
<missing-glyph horiz-adv-x="512" />
|
||||
<glyph unicode=" " d="" horiz-adv-x="256" />
|
||||
<glyph unicode="" d="M256 16c-114.88 0-208 93.12-208 208s93.12 208 208 208 208-93.12 208-208-93.12-208-208-208zM351.376 284.656c3.904 3.904 3.904 10.256 0 14.16l-21.248 21.232c-3.904 3.904-10.256 3.904-14.16 0l-60.176-60.176-60.176 60.176c-3.904 3.904-10.256 3.904-14.16 0l-21.248-21.232c-3.904-3.904-3.904-10.256 0-14.16l60.192-60.192-60.192-60.16c-3.904-3.904-3.904-10.256 0-14.16l21.248-21.248c3.904-3.904 10.256-3.904 14.16 0l60.176 60.192 60.176-60.192c3.904-3.904 10.256-3.904 14.16 0l21.248 21.248c3.904 3.904 3.904 10.256 0 14.16l-60.192 60.16 60.192 60.192z" />
|
||||
<glyph unicode="" d="M256 16c-114.88 0-208 93.12-208 208s93.12 208 208 208 208-93.12 208-208-93.12-208-208-208zM256 384c-88.352 0-160-71.648-160-160s71.648-160 160-160c88.368 0 160 71.648 160 160s-71.632 160-160 160zM328.592 167.44l-16.224-16.224c-2.976-2.976-7.808-2.976-10.8 0l-45.92 45.92-45.92-45.92c-2.992-2.976-7.808-2.976-10.8 0l-16.224 16.224c-2.976 2.976-2.976 7.808 0 10.8l45.936 45.904-45.936 45.92c-2.976 2.992-2.976 7.824 0 10.816l16.224 16.208c2.992 2.992 7.808 2.992 10.8 0l45.92-45.92 45.92 45.92c2.992 2.992 7.824 2.992 10.8 0l16.224-16.208c2.976-2.992 2.976-7.824 0-10.816l-45.936-45.92 45.936-45.904c2.976-2.992 2.976-7.84 0-10.8z" />
|
||||
<glyph unicode="" d="M475.428 290.572q0-6.286-7.428-13.714l-103.714-101.143 24.572-142.857q0.286-2 0.286-5.714 0-6-3-10.143t-8.714-4.143q-5.428 0-11.428 3.428l-128.286 67.428-128.286-67.428q-6.285-3.428-11.428-3.428-6 0-9 4.143t-3 10.143q0 1.714 0.572 5.714l24.572 142.857-104 101.143q-7.143 7.714-7.143 13.714 0 10.572 16 13.143l143.428 20.857 64.286 130q5.428 11.714 14 11.714t14-11.714l64.286-130 143.429-20.857q16-2.572 16-13.143z" horiz-adv-x="476" />
|
||||
<glyph unicode="" d="M324.857 188.572l87.428 84.857-120.572 17.715-54 109.143-54-109.143-120.572-17.714 87.428-84.857-20.857-120.286 108 56.857 107.714-56.857zM475.428 290.572q0-6.286-7.428-13.714l-103.714-101.143 24.572-142.857q0.286-2 0.286-5.714 0-14.286-11.714-14.286-5.428 0-11.428 3.428l-128.286 67.428-128.286-67.428q-6.285-3.428-11.428-3.428-6 0-9 4.143t-3 10.143q0 1.714 0.572 5.714l24.572 142.857-104 101.143q-7.143 7.714-7.143 13.714 0 10.572 16 13.143l143.428 20.857 64.286 130q5.428 11.714 14 11.714t14-11.714l64.286-130 143.429-20.857q16-2.572 16-13.143z" horiz-adv-x="476" />
|
||||
<glyph unicode="" d="M338.857 202l73.428 71.428-120.572 17.714-8.572 17.143-45.428 92v-275.143l16.857-8.857 90.857-48-17.143 101.428-3.428 18.857zM468 276.857l-103.714-101.143 24.572-142.857q1.428-9.428-1.714-14.714t-9.714-5.286q-4.857 0-11.428 3.428l-128.286 67.428-128.286-67.428q-6.572-3.428-11.428-3.428-6.572 0-9.715 5.286t-1.715 14.714l24.572 142.857-104 101.143q-9.143 9.143-6.572 17t15.428 9.857l143.429 20.857 64.286 130q5.714 11.714 14 11.714 8 0 14-11.714l64.286-130 143.429-20.857q12.857-2 15.428-9.857t-6.857-17z" horiz-adv-x="476" />
|
||||
</font></defs></svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
BIN
resources/assets/js/plugins/jquery-raty/fonts/raty.ttf
Normal file
BIN
resources/assets/js/plugins/jquery-raty/fonts/raty.woff
Normal file
BIN
resources/assets/js/plugins/jquery-raty/images/cancel-off.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
resources/assets/js/plugins/jquery-raty/images/cancel-on.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
resources/assets/js/plugins/jquery-raty/images/star-half.png
Normal file
|
After Width: | Height: | Size: 958 B |
BIN
resources/assets/js/plugins/jquery-raty/images/star-off.png
Normal file
|
After Width: | Height: | Size: 930 B |
BIN
resources/assets/js/plugins/jquery-raty/images/star-on.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
46
resources/assets/js/plugins/jquery-raty/jquery.raty.css
Normal file
@@ -0,0 +1,46 @@
|
||||
.cancel-on-png, .cancel-off-png, .star-on-png, .star-off-png, .star-half-png {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "raty";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
src: url("./fonts/raty.eot");
|
||||
src: url("./fonts/raty.eot?#iefix") format("embedded-opentype");
|
||||
src: url("./fonts/raty.svg#raty") format("svg");
|
||||
src: url("./fonts/raty.ttf") format("truetype");
|
||||
src: url("./fonts/raty.woff") format("woff");
|
||||
}
|
||||
|
||||
.cancel-on-png, .cancel-off-png, .star-on-png, .star-off-png, .star-half-png {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: "raty";
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
speak: none;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.cancel-on-png:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.cancel-off-png:before {
|
||||
content: "\e601";
|
||||
}
|
||||
|
||||
.star-on-png:before {
|
||||
content: "\f005";
|
||||
}
|
||||
|
||||
.star-off-png:before {
|
||||
content: "\f006";
|
||||
}
|
||||
|
||||
.star-half-png:before {
|
||||
content: "\f123";
|
||||
}
|
||||
777
resources/assets/js/plugins/jquery-raty/jquery.raty.js
Normal file
@@ -0,0 +1,777 @@
|
||||
/*!
|
||||
* Raty - A Star Rating Plugin
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* author: Washington Botelho
|
||||
* github: wbotelhos/raty
|
||||
* version: 3.1.0
|
||||
*
|
||||
*/
|
||||
|
||||
(function(root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory(require('jquery'));
|
||||
} else {
|
||||
factory(root.jQuery);
|
||||
}
|
||||
}(this, function($) {
|
||||
'use strict';
|
||||
|
||||
$.raty = {
|
||||
cancelButton: false,
|
||||
cancelClass: 'raty-cancel',
|
||||
cancelHint: 'Cancel this rating!',
|
||||
cancelOff: 'cancel-off.png',
|
||||
cancelOn: 'cancel-on.png',
|
||||
cancelPlace: 'left',
|
||||
click: undefined,
|
||||
half: false,
|
||||
halfShow: true,
|
||||
hints: ['bad', 'poor', 'regular', 'good', 'gorgeous'],
|
||||
iconRange: undefined,
|
||||
iconRangeSame: false,
|
||||
mouseout: undefined,
|
||||
mouseover: undefined,
|
||||
noRatedMsg: 'Not rated yet!',
|
||||
number: 5,
|
||||
numberMax: 20,
|
||||
path: undefined,
|
||||
precision: false,
|
||||
readOnly: false,
|
||||
round: { down: 0.25, full: 0.6, up: 0.76 },
|
||||
score: undefined,
|
||||
scoreName: 'score',
|
||||
single: false,
|
||||
space: true,
|
||||
starHalf: 'star-half.png',
|
||||
starOff: 'star-off.png',
|
||||
starOn: 'star-on.png',
|
||||
starType: 'img',
|
||||
target: undefined,
|
||||
targetFormat: '{score}',
|
||||
targetKeep: false,
|
||||
targetScore: undefined,
|
||||
targetText: '',
|
||||
targetType: 'hint'
|
||||
};
|
||||
|
||||
$.fn.raty = function(options) {
|
||||
return this.each(function() {
|
||||
var instance = new $.raty.Raty(this, options);
|
||||
|
||||
return instance._create();
|
||||
});
|
||||
};
|
||||
|
||||
$.raty.Raty = (function() {
|
||||
var Raty = function(element, options) {
|
||||
this.element = element;
|
||||
this.self = $(element);
|
||||
this.opt = $.extend(true, {}, $.raty, options, this.self.data());
|
||||
};
|
||||
|
||||
Raty.prototype = {
|
||||
_create: function() {
|
||||
this._executeCallbacks();
|
||||
this._adjustNumber();
|
||||
this._adjustHints();
|
||||
|
||||
this.opt.score = this._adjustedScore(this.opt.score);
|
||||
|
||||
if (this.opt.starType !== 'img') {
|
||||
this._adjustStarName();
|
||||
}
|
||||
|
||||
this._setPath();
|
||||
this._createStars();
|
||||
|
||||
if (this.opt.cancelButton) {
|
||||
this._createCancel();
|
||||
}
|
||||
|
||||
if (this.opt.precision) {
|
||||
this._adjustPrecision();
|
||||
}
|
||||
|
||||
this._createScore();
|
||||
|
||||
this._apply(this.opt.score);
|
||||
this._setTitle(this.opt.score);
|
||||
this._target(this.opt.score);
|
||||
|
||||
if (this.opt.readOnly) {
|
||||
this._lock();
|
||||
} else {
|
||||
this.element.style.cursor = 'pointer';
|
||||
|
||||
this._binds();
|
||||
}
|
||||
|
||||
this.self.data('raty', this);
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_adjustedScore: function(score) {
|
||||
if (score || score === 0) {
|
||||
return this._between(score, 0, this.opt.number);
|
||||
}
|
||||
},
|
||||
|
||||
_adjustHints: function() {
|
||||
if (!this.opt.hints) {
|
||||
this.opt.hints = [];
|
||||
}
|
||||
|
||||
if (!this.opt.halfShow && !this.opt.half) {
|
||||
return;
|
||||
}
|
||||
|
||||
var steps = this.opt.precision ? 10 : 2;
|
||||
|
||||
for (var i = 0; i < this.opt.number; i++) {
|
||||
var group = this.opt.hints[i];
|
||||
|
||||
if (Object.prototype.toString.call(group) !== '[object Array]') {
|
||||
group = [group];
|
||||
}
|
||||
|
||||
this.opt.hints[i] = [];
|
||||
|
||||
for (var j = 0; j < steps; j++) {
|
||||
var hint = group[j];
|
||||
var last = group[group.length - 1];
|
||||
|
||||
if (last === undefined) {
|
||||
last = null;
|
||||
}
|
||||
|
||||
this.opt.hints[i][j] = hint === undefined ? last : hint;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_adjustNumber: function() {
|
||||
this.opt.number = this._between(this.opt.number, 1, this.opt.numberMax);
|
||||
},
|
||||
|
||||
_adjustPrecision: function() {
|
||||
this.opt.half = true;
|
||||
},
|
||||
|
||||
_adjustStarName: function() {
|
||||
var replaces = ['cancelOff', 'cancelOn', 'starHalf', 'starOff', 'starOn'];
|
||||
|
||||
this.opt.path = '';
|
||||
|
||||
for (var i = 0; i < replaces.length; i++) {
|
||||
this.opt[replaces[i]] = this.opt[replaces[i]].replace('.', '-');
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_apply: function(score) {
|
||||
this._fill(score);
|
||||
|
||||
if (score) {
|
||||
if (score > 0) {
|
||||
this.scoreField.val(score);
|
||||
}
|
||||
|
||||
this._roundStars(score);
|
||||
}
|
||||
},
|
||||
|
||||
_attributesForIndex: function(i) {
|
||||
var name = this._nameForIndex(i);
|
||||
var attributes = { alt: i, src: this.opt.path + this.opt[name] };
|
||||
|
||||
if (this.opt.starType !== 'img') {
|
||||
attributes = { 'data-alt': i, 'class': this.opt[name] };
|
||||
}
|
||||
|
||||
attributes.title = this._getHint(i);
|
||||
|
||||
return attributes;
|
||||
},
|
||||
|
||||
_between: function(value, min, max) {
|
||||
return Math.min(Math.max(parseFloat(value), min), max);
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_binds: function() {
|
||||
if (this.cancelButton) {
|
||||
this._bindOverCancel();
|
||||
this._bindClickCancel();
|
||||
this._bindOutCancel();
|
||||
}
|
||||
|
||||
this._bindOver();
|
||||
this._bindClick();
|
||||
this._bindOut();
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_bindClick: function() {
|
||||
var that = this;
|
||||
|
||||
this.stars.on('click.raty', function(evt) {
|
||||
if (that.self.data('readonly')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var execute = true;
|
||||
var score = (that.opt.half || that.opt.precision) ? that.self.data('score') : (this.alt || $(this).data('alt'));
|
||||
|
||||
if (that.opt.half && !that.opt.precision) {
|
||||
score = that._roundHalfScore(score);
|
||||
}
|
||||
|
||||
if (that.opt.click) {
|
||||
execute = that.opt.click.call(that.element, +score, evt);
|
||||
}
|
||||
|
||||
if (execute || execute === undefined) {
|
||||
that._apply(+score);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_bindClickCancel: function() {
|
||||
this.cancelButton.on('click.raty', function(evt) {
|
||||
this.scoreField.removeAttr('value');
|
||||
|
||||
if (this.opt.click) {
|
||||
this.opt.click.call(this.element, null, evt);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_bindOut: function() {
|
||||
this.self.on('mouseleave.raty', function(evt) {
|
||||
var score = +this.scoreField.val() || undefined;
|
||||
|
||||
this._apply(score);
|
||||
this._target(score, evt);
|
||||
this._resetTitle();
|
||||
|
||||
if (this.opt.mouseout) {
|
||||
this.opt.mouseout.call(this.element, score, evt);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_bindOutCancel: function() {
|
||||
var that = this;
|
||||
|
||||
this.cancelButton.on('mouseleave.raty', function(evt) {
|
||||
var icon = that.opt.cancelOff;
|
||||
|
||||
if (that.opt.starType !== 'img') {
|
||||
icon = that.opt.cancelClass + ' ' + icon;
|
||||
}
|
||||
|
||||
that._setIcon(this, icon);
|
||||
|
||||
if (that.opt.mouseout) {
|
||||
var score = +that.scoreField.val() || undefined;
|
||||
|
||||
that.opt.mouseout.call(that.element, score, evt);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_bindOver: function() {
|
||||
var that = this;
|
||||
var action = that.opt.half ? 'mousemove.raty' : 'mouseover.raty';
|
||||
|
||||
this.stars.on(action, function(evt) {
|
||||
var score = that._getScoreByPosition(evt, this);
|
||||
|
||||
that._fill(score);
|
||||
|
||||
if (that.opt.half) {
|
||||
that._roundStars(score, evt);
|
||||
that._setTitle(score, evt);
|
||||
|
||||
that.self.data('score', score);
|
||||
}
|
||||
|
||||
that._target(score, evt);
|
||||
|
||||
if (that.opt.mouseover) {
|
||||
that.opt.mouseover.call(that.element, score, evt);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_bindOverCancel: function() {
|
||||
var that = this;
|
||||
|
||||
this.cancelButton.on('mouseover.raty', function(evt) {
|
||||
if (that.self.data('readonly')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var starOff = that.opt.path + that.opt.starOff;
|
||||
var icon = that.opt.cancelOn;
|
||||
|
||||
if (that.opt.starType === 'img') {
|
||||
that.stars.attr('src', starOff);
|
||||
} else {
|
||||
icon = that.opt.cancelClass + ' ' + icon;
|
||||
|
||||
that.stars.attr('class', starOff);
|
||||
}
|
||||
|
||||
that._setIcon(this, icon);
|
||||
that._target(null, evt);
|
||||
|
||||
if (that.opt.mouseover) {
|
||||
that.opt.mouseover.call(that.element, null);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_buildScoreField: function() {
|
||||
return $('<input />', { name: this.opt.scoreName, type: 'hidden' }).appendTo(this.self);
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_createCancel: function() {
|
||||
var icon = this.opt.path + this.opt.cancelOff;
|
||||
var button = $('<' + this.opt.starType + ' />', { title: this.opt.cancelHint, 'class': this.opt.cancelClass });
|
||||
|
||||
if (this.opt.starType === 'img') {
|
||||
button.attr({ src: icon, alt: 'x' });
|
||||
} else {
|
||||
// TODO: use $.data
|
||||
button.attr('data-alt', 'x').addClass(icon);
|
||||
}
|
||||
|
||||
if (this.opt.cancelPlace === 'left') {
|
||||
this.self.prepend(' ').prepend(button);
|
||||
} else {
|
||||
this.self.append(' ').append(button);
|
||||
}
|
||||
|
||||
this.cancelButton = button;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_createScore: function() {
|
||||
var score = $(this.opt.targetScore);
|
||||
|
||||
this.scoreField = score.length ? score : this._buildScoreField();
|
||||
},
|
||||
|
||||
_createStars: function() {
|
||||
for (var i = 1; i <= this.opt.number; i++) {
|
||||
var attributes = this._attributesForIndex(i);
|
||||
|
||||
$('<' + this.opt.starType + ' />', attributes).appendTo(this.element);
|
||||
|
||||
if (this.opt.space && i < this.opt.number) {
|
||||
this.self.append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
this.stars = this.self.children(this.opt.starType);
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_error: function(message) {
|
||||
$(this).text(message);
|
||||
|
||||
$.error(message);
|
||||
},
|
||||
|
||||
_executeCallbacks: function() {
|
||||
var options = ['number', 'readOnly', 'score', 'scoreName', 'target', 'path'];
|
||||
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
if (typeof this.opt[options[i]] === 'function') {
|
||||
var value = this.opt[options[i]].call(this.element);
|
||||
|
||||
if (value) {
|
||||
this.opt[options[i]] = value;
|
||||
} else {
|
||||
delete this.opt[options[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_fill: function(score) {
|
||||
var hash = 0;
|
||||
|
||||
if (this.opt.iconRangeSame && this.opt.iconRange) {
|
||||
while (hash < this.opt.iconRange.length && this.opt.iconRange[hash].range < score) {
|
||||
hash++;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 1; i <= this.stars.length; i++) {
|
||||
var icon;
|
||||
var star = this.stars[i - 1];
|
||||
var turnOn = this._turnOn(i, score);
|
||||
|
||||
if (this.opt.iconRange && this.opt.iconRange.length > hash) {
|
||||
var irange = this.opt.iconRange[hash];
|
||||
|
||||
icon = this._getRangeIcon(irange, turnOn);
|
||||
|
||||
if (i <= irange.range) {
|
||||
this._setIcon(star, icon);
|
||||
}
|
||||
|
||||
if (i === irange.range) {
|
||||
hash++;
|
||||
}
|
||||
} else {
|
||||
icon = this.opt[turnOn ? 'starOn' : 'starOff'];
|
||||
|
||||
this._setIcon(star, icon);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_getDecimal: function(number, fractions) {
|
||||
var decimal = number.toString().split('.')[1];
|
||||
var result = 0;
|
||||
|
||||
if (decimal) {
|
||||
result = parseInt(decimal.slice(0, fractions), 10);
|
||||
|
||||
if (decimal.slice(1, 5) === '9999') {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_getRangeIcon: function(irange, turnOn) {
|
||||
return turnOn ? irange.on || this.opt.starOn : irange.off || this.opt.starOff;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_getScoreByPosition: function(evt, icon) {
|
||||
var score = parseInt(icon.alt || icon.getAttribute('data-alt'), 10);
|
||||
|
||||
if (this.opt.half) {
|
||||
var size = this._getWidth();
|
||||
var percent = parseFloat((evt.pageX - $(icon).offset().left) / size);
|
||||
|
||||
score = score - 1 + percent;
|
||||
}
|
||||
|
||||
return score;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_getHint: function(score, evt) {
|
||||
if (score !== 0 && !score) {
|
||||
return this.opt.noRatedMsg;
|
||||
}
|
||||
|
||||
var decimal = this._getDecimal(score, 1);
|
||||
var integer = Math.ceil(score);
|
||||
var group = this.opt.hints[(integer || 1) - 1];
|
||||
var hint = group;
|
||||
var set = !evt || this.isMove;
|
||||
|
||||
if (this.opt.precision) {
|
||||
if (set) {
|
||||
decimal = decimal === 0 ? 9 : decimal - 1;
|
||||
}
|
||||
|
||||
hint = group[decimal];
|
||||
} else if (this.opt.halfShow || this.opt.half) {
|
||||
decimal = set && decimal === 0 ? 1 : decimal > 5 ? 1 : 0;
|
||||
|
||||
hint = group[decimal];
|
||||
}
|
||||
|
||||
return hint === '' ? '' : hint || score;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_getWidth: function() {
|
||||
var width = this.stars[0].width || parseFloat(this.stars.eq(0).css('font-size'));
|
||||
|
||||
if (!width) {
|
||||
this._error('Could not get the icon width!');
|
||||
}
|
||||
|
||||
return width;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_lock: function() {
|
||||
var hint = this._getHint(this.scoreField.val());
|
||||
|
||||
this.element.style.cursor = '';
|
||||
this.element.title = hint;
|
||||
|
||||
this.scoreField.prop('readonly', true);
|
||||
this.stars.prop('title', hint);
|
||||
|
||||
if (this.cancelButton) {
|
||||
this.cancelButton.hide();
|
||||
}
|
||||
|
||||
this.self.data('readonly', true);
|
||||
},
|
||||
|
||||
_nameForIndex: function(i) {
|
||||
return this.opt.score && this.opt.score >= i ? 'starOn' : 'starOff';
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_resetTitle: function() {
|
||||
for (var i = 0; i < this.opt.number; i++) {
|
||||
this.stars[i].title = this._getHint(i + 1);
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_roundHalfScore: function(score) {
|
||||
var integer = parseInt(score, 10);
|
||||
var decimal = this._getDecimal(score, 1);
|
||||
|
||||
if (decimal !== 0) {
|
||||
decimal = decimal > 5 ? 1 : 0.5;
|
||||
}
|
||||
|
||||
return integer + decimal;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_roundStars: function(score, evt) {
|
||||
var name = this._starName(score, evt);
|
||||
|
||||
if (name) {
|
||||
var icon = this.opt[name];
|
||||
var star = this.stars[Math.ceil(score) - 1];
|
||||
|
||||
this._setIcon(star, icon);
|
||||
} // Full down: [x.00 .. x.25]
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_setIcon: function(star, icon) {
|
||||
star[this.opt.starType === 'img' ? 'src' : 'className'] = this.opt.path + icon;
|
||||
},
|
||||
|
||||
_setPath: function() {
|
||||
this.opt.path = this.opt.path || '';
|
||||
|
||||
if (this.opt.path && this.opt.path.slice(-1)[0] !== '/') {
|
||||
this.opt.path += '/';
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_setTarget: function(target, score) {
|
||||
if (score) {
|
||||
score = this.opt.targetFormat.toString().replace('{score}', score);
|
||||
}
|
||||
|
||||
if (target.is(':input')) {
|
||||
target.val(score);
|
||||
} else {
|
||||
target.html(score);
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_setTitle: function(score, evt) {
|
||||
if (score) {
|
||||
var integer = parseInt(Math.ceil(score), 10);
|
||||
var star = this.stars[integer - 1];
|
||||
|
||||
star.title = this._getHint(score, evt);
|
||||
}
|
||||
},
|
||||
|
||||
_starName: function(score, evt) {
|
||||
var decimal = +(score % 1).toFixed(2);
|
||||
|
||||
if (evt || this.isMove) {
|
||||
return decimal > 0.5 ? 'starOn' : 'starHalf';
|
||||
}
|
||||
|
||||
if (decimal <= this.opt.round.down) { // Down: [x.00 ... x.25]
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.opt.halfShow && decimal < this.opt.round.up) { // Half: [x.26 ... x.75]
|
||||
return 'starHalf';
|
||||
}
|
||||
|
||||
if (decimal < this.opt.round.full) { // Off: [x.26 .. x.6]
|
||||
return 'starOff';
|
||||
}
|
||||
|
||||
return 'starOn'; // Up: [x.26 ...] || [x.6 ...]
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_target: function(score, evt) {
|
||||
if (this.opt.target) {
|
||||
var target = $(this.opt.target);
|
||||
|
||||
if (!target.length) {
|
||||
this._error('Target selector invalid or missing!');
|
||||
}
|
||||
|
||||
var mouseover = evt && evt.type === 'mouseover';
|
||||
|
||||
if (score === undefined) {
|
||||
score = this.opt.targetText;
|
||||
} else if (score === null) {
|
||||
score = mouseover ? this.opt.cancelHint : this.opt.targetText;
|
||||
} else {
|
||||
if (this.opt.targetType === 'hint') {
|
||||
score = this._getHint(score, evt);
|
||||
} else if (this.opt.precision) {
|
||||
score = parseFloat(score).toFixed(1);
|
||||
}
|
||||
|
||||
var mousemove = evt && evt.type === 'mousemove';
|
||||
|
||||
if (!mouseover && !mousemove && !this.opt.targetKeep) {
|
||||
score = this.opt.targetText;
|
||||
}
|
||||
}
|
||||
|
||||
this._setTarget(target, score);
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_turnOn: function(i, score) {
|
||||
return this.opt.single ? (i === score) : (i <= score);
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_unlock: function() {
|
||||
this.element.style.cursor = 'pointer';
|
||||
this.element.removeAttribute('title');
|
||||
|
||||
this.scoreField.removeAttr('readonly');
|
||||
|
||||
this.self.data('readonly', false);
|
||||
|
||||
this._resetTitle();
|
||||
|
||||
if (this.cancelButton) {
|
||||
this.cancelButton.css('display', '');
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
cancel: function(click) {
|
||||
if (this.self.data('readonly') !== true) {
|
||||
this[click ? 'click' : 'score'].call(this, null);
|
||||
|
||||
this.scoreField.removeAttr('value');
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
click: function(score) {
|
||||
if (this.self.data('readonly') !== true) {
|
||||
score = this._adjustedScore(score);
|
||||
|
||||
this._apply(score);
|
||||
|
||||
if (this.opt.click) {
|
||||
this.opt.click.call(this.element, score, $.Event('click'));
|
||||
}
|
||||
|
||||
this._target(score);
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
getScore: function() {
|
||||
var score = [];
|
||||
var value ;
|
||||
|
||||
value = this.scoreField.val();
|
||||
|
||||
score.push(value ? +value : undefined);
|
||||
|
||||
return (score.length > 1) ? score : score[0];
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
move: function(score) {
|
||||
var integer = parseInt(score, 10);
|
||||
var decimal = this._getDecimal(score, 1);
|
||||
|
||||
if (integer >= this.opt.number) {
|
||||
integer = this.opt.number - 1;
|
||||
decimal = 10;
|
||||
}
|
||||
|
||||
var width = this._getWidth();
|
||||
var steps = width / 10;
|
||||
var star = $(this.stars[integer]);
|
||||
var percent = star.offset().left + steps * decimal;
|
||||
var evt = $.Event('mousemove', { pageX: percent });
|
||||
|
||||
this.isMove = true;
|
||||
|
||||
star.trigger(evt);
|
||||
|
||||
this.isMove = false;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
readOnly: function(readonly) {
|
||||
if (this.self.data('readonly') !== readonly) {
|
||||
if (readonly) {
|
||||
this.self.off('.raty').children(this.opt.starType).off('.raty');
|
||||
|
||||
this._lock();
|
||||
} else {
|
||||
this._binds();
|
||||
this._unlock();
|
||||
}
|
||||
|
||||
this.self.data('readonly', readonly);
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
score: function() {
|
||||
return arguments.length ? this.setScore.apply(this, arguments) : this.getScore();
|
||||
},
|
||||
|
||||
setScore: function(score) {
|
||||
if (this.self.data('readonly') !== true) {
|
||||
score = this._adjustedScore(score);
|
||||
|
||||
this._apply(score);
|
||||
this._target(score);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Raty;
|
||||
})();
|
||||
}));
|
||||
BIN
resources/assets/player/1s_blank.mp4
Normal file
1
resources/assets/player/js/p2p-media-loader-core.min.js
vendored
Normal file
28
resources/assets/player/js/p2p-media-loader-hlsjs.min.js
vendored
Normal file
BIN
resources/assets/plugins/jquery-raty/fonts/raty.eot
Normal file
15
resources/assets/plugins/jquery-raty/fonts/raty.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Generated by IcoMoon</metadata>
|
||||
<defs>
|
||||
<font id="raty" horiz-adv-x="512">
|
||||
<font-face units-per-em="512" ascent="480" descent="-32" />
|
||||
<missing-glyph horiz-adv-x="512" />
|
||||
<glyph unicode=" " d="" horiz-adv-x="256" />
|
||||
<glyph unicode="" d="M256 16c-114.88 0-208 93.12-208 208s93.12 208 208 208 208-93.12 208-208-93.12-208-208-208zM351.376 284.656c3.904 3.904 3.904 10.256 0 14.16l-21.248 21.232c-3.904 3.904-10.256 3.904-14.16 0l-60.176-60.176-60.176 60.176c-3.904 3.904-10.256 3.904-14.16 0l-21.248-21.232c-3.904-3.904-3.904-10.256 0-14.16l60.192-60.192-60.192-60.16c-3.904-3.904-3.904-10.256 0-14.16l21.248-21.248c3.904-3.904 10.256-3.904 14.16 0l60.176 60.192 60.176-60.192c3.904-3.904 10.256-3.904 14.16 0l21.248 21.248c3.904 3.904 3.904 10.256 0 14.16l-60.192 60.16 60.192 60.192z" />
|
||||
<glyph unicode="" d="M256 16c-114.88 0-208 93.12-208 208s93.12 208 208 208 208-93.12 208-208-93.12-208-208-208zM256 384c-88.352 0-160-71.648-160-160s71.648-160 160-160c88.368 0 160 71.648 160 160s-71.632 160-160 160zM328.592 167.44l-16.224-16.224c-2.976-2.976-7.808-2.976-10.8 0l-45.92 45.92-45.92-45.92c-2.992-2.976-7.808-2.976-10.8 0l-16.224 16.224c-2.976 2.976-2.976 7.808 0 10.8l45.936 45.904-45.936 45.92c-2.976 2.992-2.976 7.824 0 10.816l16.224 16.208c2.992 2.992 7.808 2.992 10.8 0l45.92-45.92 45.92 45.92c2.992 2.992 7.824 2.992 10.8 0l16.224-16.208c2.976-2.992 2.976-7.824 0-10.816l-45.936-45.92 45.936-45.904c2.976-2.992 2.976-7.84 0-10.8z" />
|
||||
<glyph unicode="" d="M475.428 290.572q0-6.286-7.428-13.714l-103.714-101.143 24.572-142.857q0.286-2 0.286-5.714 0-6-3-10.143t-8.714-4.143q-5.428 0-11.428 3.428l-128.286 67.428-128.286-67.428q-6.285-3.428-11.428-3.428-6 0-9 4.143t-3 10.143q0 1.714 0.572 5.714l24.572 142.857-104 101.143q-7.143 7.714-7.143 13.714 0 10.572 16 13.143l143.428 20.857 64.286 130q5.428 11.714 14 11.714t14-11.714l64.286-130 143.429-20.857q16-2.572 16-13.143z" horiz-adv-x="476" />
|
||||
<glyph unicode="" d="M324.857 188.572l87.428 84.857-120.572 17.715-54 109.143-54-109.143-120.572-17.714 87.428-84.857-20.857-120.286 108 56.857 107.714-56.857zM475.428 290.572q0-6.286-7.428-13.714l-103.714-101.143 24.572-142.857q0.286-2 0.286-5.714 0-14.286-11.714-14.286-5.428 0-11.428 3.428l-128.286 67.428-128.286-67.428q-6.285-3.428-11.428-3.428-6 0-9 4.143t-3 10.143q0 1.714 0.572 5.714l24.572 142.857-104 101.143q-7.143 7.714-7.143 13.714 0 10.572 16 13.143l143.428 20.857 64.286 130q5.428 11.714 14 11.714t14-11.714l64.286-130 143.429-20.857q16-2.572 16-13.143z" horiz-adv-x="476" />
|
||||
<glyph unicode="" d="M338.857 202l73.428 71.428-120.572 17.714-8.572 17.143-45.428 92v-275.143l16.857-8.857 90.857-48-17.143 101.428-3.428 18.857zM468 276.857l-103.714-101.143 24.572-142.857q1.428-9.428-1.714-14.714t-9.714-5.286q-4.857 0-11.428 3.428l-128.286 67.428-128.286-67.428q-6.572-3.428-11.428-3.428-6.572 0-9.715 5.286t-1.715 14.714l24.572 142.857-104 101.143q-9.143 9.143-6.572 17t15.428 9.857l143.429 20.857 64.286 130q5.714 11.714 14 11.714 8 0 14-11.714l64.286-130 143.429-20.857q12.857-2 15.428-9.857t-6.857-17z" horiz-adv-x="476" />
|
||||
</font></defs></svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
BIN
resources/assets/plugins/jquery-raty/fonts/raty.ttf
Normal file
BIN
resources/assets/plugins/jquery-raty/fonts/raty.woff
Normal file
BIN
resources/assets/plugins/jquery-raty/images/cancel-off.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
resources/assets/plugins/jquery-raty/images/cancel-on.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
resources/assets/plugins/jquery-raty/images/star-half.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/assets/plugins/jquery-raty/images/star-off.png
Normal file
|
After Width: | Height: | Size: 781 B |
BIN
resources/assets/plugins/jquery-raty/images/star-on.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
46
resources/assets/plugins/jquery-raty/jquery.raty.css
Normal file
@@ -0,0 +1,46 @@
|
||||
.cancel-on-png, .cancel-off-png, .star-on-png, .star-off-png, .star-half-png {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "raty";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
src: url("./fonts/raty.eot");
|
||||
src: url("./fonts/raty.eot?#iefix") format("embedded-opentype");
|
||||
src: url("./fonts/raty.svg#raty") format("svg");
|
||||
src: url("./fonts/raty.ttf") format("truetype");
|
||||
src: url("./fonts/raty.woff") format("woff");
|
||||
}
|
||||
|
||||
.cancel-on-png, .cancel-off-png, .star-on-png, .star-off-png, .star-half-png {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: "raty";
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
speak: none;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.cancel-on-png:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.cancel-off-png:before {
|
||||
content: "\e601";
|
||||
}
|
||||
|
||||
.star-on-png:before {
|
||||
content: "\f005";
|
||||
}
|
||||
|
||||
.star-off-png:before {
|
||||
content: "\f006";
|
||||
}
|
||||
|
||||
.star-half-png:before {
|
||||
content: "\f123";
|
||||
}
|
||||
777
resources/assets/plugins/jquery-raty/jquery.raty.js
Normal file
@@ -0,0 +1,777 @@
|
||||
/*!
|
||||
* Raty - A Star Rating Plugin
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* author: Washington Botelho
|
||||
* github: wbotelhos/raty
|
||||
* version: 3.1.0
|
||||
*
|
||||
*/
|
||||
|
||||
(function(root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory(require('jquery'));
|
||||
} else {
|
||||
factory(root.jQuery);
|
||||
}
|
||||
}(this, function($) {
|
||||
'use strict';
|
||||
|
||||
$.raty = {
|
||||
cancelButton: false,
|
||||
cancelClass: 'raty-cancel',
|
||||
cancelHint: 'Cancel this rating!',
|
||||
cancelOff: 'cancel-off.png',
|
||||
cancelOn: 'cancel-on.png',
|
||||
cancelPlace: 'left',
|
||||
click: undefined,
|
||||
half: false,
|
||||
halfShow: true,
|
||||
hints: ['bad', 'poor', 'regular', 'good', 'gorgeous'],
|
||||
iconRange: undefined,
|
||||
iconRangeSame: false,
|
||||
mouseout: undefined,
|
||||
mouseover: undefined,
|
||||
noRatedMsg: 'Not rated yet!',
|
||||
number: 5,
|
||||
numberMax: 20,
|
||||
path: undefined,
|
||||
precision: false,
|
||||
readOnly: false,
|
||||
round: { down: 0.25, full: 0.6, up: 0.76 },
|
||||
score: undefined,
|
||||
scoreName: 'score',
|
||||
single: false,
|
||||
space: true,
|
||||
starHalf: 'star-half.png',
|
||||
starOff: 'star-off.png',
|
||||
starOn: 'star-on.png',
|
||||
starType: 'img',
|
||||
target: undefined,
|
||||
targetFormat: '{score}',
|
||||
targetKeep: false,
|
||||
targetScore: undefined,
|
||||
targetText: '',
|
||||
targetType: 'hint'
|
||||
};
|
||||
|
||||
$.fn.raty = function(options) {
|
||||
return this.each(function() {
|
||||
var instance = new $.raty.Raty(this, options);
|
||||
|
||||
return instance._create();
|
||||
});
|
||||
};
|
||||
|
||||
$.raty.Raty = (function() {
|
||||
var Raty = function(element, options) {
|
||||
this.element = element;
|
||||
this.self = $(element);
|
||||
this.opt = $.extend(true, {}, $.raty, options, this.self.data());
|
||||
};
|
||||
|
||||
Raty.prototype = {
|
||||
_create: function() {
|
||||
this._executeCallbacks();
|
||||
this._adjustNumber();
|
||||
this._adjustHints();
|
||||
|
||||
this.opt.score = this._adjustedScore(this.opt.score);
|
||||
|
||||
if (this.opt.starType !== 'img') {
|
||||
this._adjustStarName();
|
||||
}
|
||||
|
||||
this._setPath();
|
||||
this._createStars();
|
||||
|
||||
if (this.opt.cancelButton) {
|
||||
this._createCancel();
|
||||
}
|
||||
|
||||
if (this.opt.precision) {
|
||||
this._adjustPrecision();
|
||||
}
|
||||
|
||||
this._createScore();
|
||||
|
||||
this._apply(this.opt.score);
|
||||
this._setTitle(this.opt.score);
|
||||
this._target(this.opt.score);
|
||||
|
||||
if (this.opt.readOnly) {
|
||||
this._lock();
|
||||
} else {
|
||||
this.element.style.cursor = 'pointer';
|
||||
|
||||
this._binds();
|
||||
}
|
||||
|
||||
this.self.data('raty', this);
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_adjustedScore: function(score) {
|
||||
if (score || score === 0) {
|
||||
return this._between(score, 0, this.opt.number);
|
||||
}
|
||||
},
|
||||
|
||||
_adjustHints: function() {
|
||||
if (!this.opt.hints) {
|
||||
this.opt.hints = [];
|
||||
}
|
||||
|
||||
if (!this.opt.halfShow && !this.opt.half) {
|
||||
return;
|
||||
}
|
||||
|
||||
var steps = this.opt.precision ? 10 : 2;
|
||||
|
||||
for (var i = 0; i < this.opt.number; i++) {
|
||||
var group = this.opt.hints[i];
|
||||
|
||||
if (Object.prototype.toString.call(group) !== '[object Array]') {
|
||||
group = [group];
|
||||
}
|
||||
|
||||
this.opt.hints[i] = [];
|
||||
|
||||
for (var j = 0; j < steps; j++) {
|
||||
var hint = group[j];
|
||||
var last = group[group.length - 1];
|
||||
|
||||
if (last === undefined) {
|
||||
last = null;
|
||||
}
|
||||
|
||||
this.opt.hints[i][j] = hint === undefined ? last : hint;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_adjustNumber: function() {
|
||||
this.opt.number = this._between(this.opt.number, 1, this.opt.numberMax);
|
||||
},
|
||||
|
||||
_adjustPrecision: function() {
|
||||
this.opt.half = true;
|
||||
},
|
||||
|
||||
_adjustStarName: function() {
|
||||
var replaces = ['cancelOff', 'cancelOn', 'starHalf', 'starOff', 'starOn'];
|
||||
|
||||
this.opt.path = '';
|
||||
|
||||
for (var i = 0; i < replaces.length; i++) {
|
||||
this.opt[replaces[i]] = this.opt[replaces[i]].replace('.', '-');
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_apply: function(score) {
|
||||
this._fill(score);
|
||||
|
||||
if (score) {
|
||||
if (score > 0) {
|
||||
this.scoreField.val(score);
|
||||
}
|
||||
|
||||
this._roundStars(score);
|
||||
}
|
||||
},
|
||||
|
||||
_attributesForIndex: function(i) {
|
||||
var name = this._nameForIndex(i);
|
||||
var attributes = { alt: i, src: this.opt.path + this.opt[name] };
|
||||
|
||||
if (this.opt.starType !== 'img') {
|
||||
attributes = { 'data-alt': i, 'class': this.opt[name] };
|
||||
}
|
||||
|
||||
attributes.title = this._getHint(i);
|
||||
|
||||
return attributes;
|
||||
},
|
||||
|
||||
_between: function(value, min, max) {
|
||||
return Math.min(Math.max(parseFloat(value), min), max);
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_binds: function() {
|
||||
if (this.cancelButton) {
|
||||
this._bindOverCancel();
|
||||
this._bindClickCancel();
|
||||
this._bindOutCancel();
|
||||
}
|
||||
|
||||
this._bindOver();
|
||||
this._bindClick();
|
||||
this._bindOut();
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_bindClick: function() {
|
||||
var that = this;
|
||||
|
||||
this.stars.on('click.raty', function(evt) {
|
||||
if (that.self.data('readonly')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var execute = true;
|
||||
var score = (that.opt.half || that.opt.precision) ? that.self.data('score') : (this.alt || $(this).data('alt'));
|
||||
|
||||
if (that.opt.half && !that.opt.precision) {
|
||||
score = that._roundHalfScore(score);
|
||||
}
|
||||
|
||||
if (that.opt.click) {
|
||||
execute = that.opt.click.call(that.element, +score, evt);
|
||||
}
|
||||
|
||||
if (execute || execute === undefined) {
|
||||
that._apply(+score);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_bindClickCancel: function() {
|
||||
this.cancelButton.on('click.raty', function(evt) {
|
||||
this.scoreField.removeAttr('value');
|
||||
|
||||
if (this.opt.click) {
|
||||
this.opt.click.call(this.element, null, evt);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_bindOut: function() {
|
||||
this.self.on('mouseleave.raty', function(evt) {
|
||||
var score = +this.scoreField.val() || undefined;
|
||||
|
||||
this._apply(score);
|
||||
this._target(score, evt);
|
||||
this._resetTitle();
|
||||
|
||||
if (this.opt.mouseout) {
|
||||
this.opt.mouseout.call(this.element, score, evt);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_bindOutCancel: function() {
|
||||
var that = this;
|
||||
|
||||
this.cancelButton.on('mouseleave.raty', function(evt) {
|
||||
var icon = that.opt.cancelOff;
|
||||
|
||||
if (that.opt.starType !== 'img') {
|
||||
icon = that.opt.cancelClass + ' ' + icon;
|
||||
}
|
||||
|
||||
that._setIcon(this, icon);
|
||||
|
||||
if (that.opt.mouseout) {
|
||||
var score = +that.scoreField.val() || undefined;
|
||||
|
||||
that.opt.mouseout.call(that.element, score, evt);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_bindOver: function() {
|
||||
var that = this;
|
||||
var action = that.opt.half ? 'mousemove.raty' : 'mouseover.raty';
|
||||
|
||||
this.stars.on(action, function(evt) {
|
||||
var score = that._getScoreByPosition(evt, this);
|
||||
|
||||
that._fill(score);
|
||||
|
||||
if (that.opt.half) {
|
||||
that._roundStars(score, evt);
|
||||
that._setTitle(score, evt);
|
||||
|
||||
that.self.data('score', score);
|
||||
}
|
||||
|
||||
that._target(score, evt);
|
||||
|
||||
if (that.opt.mouseover) {
|
||||
that.opt.mouseover.call(that.element, score, evt);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_bindOverCancel: function() {
|
||||
var that = this;
|
||||
|
||||
this.cancelButton.on('mouseover.raty', function(evt) {
|
||||
if (that.self.data('readonly')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var starOff = that.opt.path + that.opt.starOff;
|
||||
var icon = that.opt.cancelOn;
|
||||
|
||||
if (that.opt.starType === 'img') {
|
||||
that.stars.attr('src', starOff);
|
||||
} else {
|
||||
icon = that.opt.cancelClass + ' ' + icon;
|
||||
|
||||
that.stars.attr('class', starOff);
|
||||
}
|
||||
|
||||
that._setIcon(this, icon);
|
||||
that._target(null, evt);
|
||||
|
||||
if (that.opt.mouseover) {
|
||||
that.opt.mouseover.call(that.element, null);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_buildScoreField: function() {
|
||||
return $('<input />', { name: this.opt.scoreName, type: 'hidden' }).appendTo(this.self);
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_createCancel: function() {
|
||||
var icon = this.opt.path + this.opt.cancelOff;
|
||||
var button = $('<' + this.opt.starType + ' />', { title: this.opt.cancelHint, 'class': this.opt.cancelClass });
|
||||
|
||||
if (this.opt.starType === 'img') {
|
||||
button.attr({ src: icon, alt: 'x' });
|
||||
} else {
|
||||
// TODO: use $.data
|
||||
button.attr('data-alt', 'x').addClass(icon);
|
||||
}
|
||||
|
||||
if (this.opt.cancelPlace === 'left') {
|
||||
this.self.prepend(' ').prepend(button);
|
||||
} else {
|
||||
this.self.append(' ').append(button);
|
||||
}
|
||||
|
||||
this.cancelButton = button;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_createScore: function() {
|
||||
var score = $(this.opt.targetScore);
|
||||
|
||||
this.scoreField = score.length ? score : this._buildScoreField();
|
||||
},
|
||||
|
||||
_createStars: function() {
|
||||
for (var i = 1; i <= this.opt.number; i++) {
|
||||
var attributes = this._attributesForIndex(i);
|
||||
|
||||
$('<' + this.opt.starType + ' />', attributes).appendTo(this.element);
|
||||
|
||||
if (this.opt.space && i < this.opt.number) {
|
||||
this.self.append(' ');
|
||||
}
|
||||
}
|
||||
|
||||
this.stars = this.self.children(this.opt.starType);
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_error: function(message) {
|
||||
$(this).text(message);
|
||||
|
||||
$.error(message);
|
||||
},
|
||||
|
||||
_executeCallbacks: function() {
|
||||
var options = ['number', 'readOnly', 'score', 'scoreName', 'target', 'path'];
|
||||
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
if (typeof this.opt[options[i]] === 'function') {
|
||||
var value = this.opt[options[i]].call(this.element);
|
||||
|
||||
if (value) {
|
||||
this.opt[options[i]] = value;
|
||||
} else {
|
||||
delete this.opt[options[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_fill: function(score) {
|
||||
var hash = 0;
|
||||
|
||||
if (this.opt.iconRangeSame && this.opt.iconRange) {
|
||||
while (hash < this.opt.iconRange.length && this.opt.iconRange[hash].range < score) {
|
||||
hash++;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 1; i <= this.stars.length; i++) {
|
||||
var icon;
|
||||
var star = this.stars[i - 1];
|
||||
var turnOn = this._turnOn(i, score);
|
||||
|
||||
if (this.opt.iconRange && this.opt.iconRange.length > hash) {
|
||||
var irange = this.opt.iconRange[hash];
|
||||
|
||||
icon = this._getRangeIcon(irange, turnOn);
|
||||
|
||||
if (i <= irange.range) {
|
||||
this._setIcon(star, icon);
|
||||
}
|
||||
|
||||
if (i === irange.range) {
|
||||
hash++;
|
||||
}
|
||||
} else {
|
||||
icon = this.opt[turnOn ? 'starOn' : 'starOff'];
|
||||
|
||||
this._setIcon(star, icon);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_getDecimal: function(number, fractions) {
|
||||
var decimal = number.toString().split('.')[1];
|
||||
var result = 0;
|
||||
|
||||
if (decimal) {
|
||||
result = parseInt(decimal.slice(0, fractions), 10);
|
||||
|
||||
if (decimal.slice(1, 5) === '9999') {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_getRangeIcon: function(irange, turnOn) {
|
||||
return turnOn ? irange.on || this.opt.starOn : irange.off || this.opt.starOff;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_getScoreByPosition: function(evt, icon) {
|
||||
var score = parseInt(icon.alt || icon.getAttribute('data-alt'), 10);
|
||||
|
||||
if (this.opt.half) {
|
||||
var size = this._getWidth();
|
||||
var percent = parseFloat((evt.pageX - $(icon).offset().left) / size);
|
||||
|
||||
score = score - 1 + percent;
|
||||
}
|
||||
|
||||
return score;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_getHint: function(score, evt) {
|
||||
if (score !== 0 && !score) {
|
||||
return this.opt.noRatedMsg;
|
||||
}
|
||||
|
||||
var decimal = this._getDecimal(score, 1);
|
||||
var integer = Math.ceil(score);
|
||||
var group = this.opt.hints[(integer || 1) - 1];
|
||||
var hint = group;
|
||||
var set = !evt || this.isMove;
|
||||
|
||||
if (this.opt.precision) {
|
||||
if (set) {
|
||||
decimal = decimal === 0 ? 9 : decimal - 1;
|
||||
}
|
||||
|
||||
hint = group[decimal];
|
||||
} else if (this.opt.halfShow || this.opt.half) {
|
||||
decimal = set && decimal === 0 ? 1 : decimal > 5 ? 1 : 0;
|
||||
|
||||
hint = group[decimal];
|
||||
}
|
||||
|
||||
return hint === '' ? '' : hint || score;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_getWidth: function() {
|
||||
var width = this.stars[0].width || parseFloat(this.stars.eq(0).css('font-size'));
|
||||
|
||||
if (!width) {
|
||||
this._error('Could not get the icon width!');
|
||||
}
|
||||
|
||||
return width;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_lock: function() {
|
||||
var hint = this._getHint(this.scoreField.val());
|
||||
|
||||
this.element.style.cursor = '';
|
||||
this.element.title = hint;
|
||||
|
||||
this.scoreField.prop('readonly', true);
|
||||
this.stars.prop('title', hint);
|
||||
|
||||
if (this.cancelButton) {
|
||||
this.cancelButton.hide();
|
||||
}
|
||||
|
||||
this.self.data('readonly', true);
|
||||
},
|
||||
|
||||
_nameForIndex: function(i) {
|
||||
return this.opt.score && this.opt.score >= i ? 'starOn' : 'starOff';
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_resetTitle: function() {
|
||||
for (var i = 0; i < this.opt.number; i++) {
|
||||
this.stars[i].title = this._getHint(i + 1);
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_roundHalfScore: function(score) {
|
||||
var integer = parseInt(score, 10);
|
||||
var decimal = this._getDecimal(score, 1);
|
||||
|
||||
if (decimal !== 0) {
|
||||
decimal = decimal > 5 ? 1 : 0.5;
|
||||
}
|
||||
|
||||
return integer + decimal;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_roundStars: function(score, evt) {
|
||||
var name = this._starName(score, evt);
|
||||
|
||||
if (name) {
|
||||
var icon = this.opt[name];
|
||||
var star = this.stars[Math.ceil(score) - 1];
|
||||
|
||||
this._setIcon(star, icon);
|
||||
} // Full down: [x.00 .. x.25]
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_setIcon: function(star, icon) {
|
||||
star[this.opt.starType === 'img' ? 'src' : 'className'] = this.opt.path + icon;
|
||||
},
|
||||
|
||||
_setPath: function() {
|
||||
this.opt.path = this.opt.path || '';
|
||||
|
||||
if (this.opt.path && this.opt.path.slice(-1)[0] !== '/') {
|
||||
this.opt.path += '/';
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_setTarget: function(target, score) {
|
||||
if (score) {
|
||||
score = this.opt.targetFormat.toString().replace('{score}', score);
|
||||
}
|
||||
|
||||
if (target.is(':input')) {
|
||||
target.val(score);
|
||||
} else {
|
||||
target.html(score);
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_setTitle: function(score, evt) {
|
||||
if (score) {
|
||||
var integer = parseInt(Math.ceil(score), 10);
|
||||
var star = this.stars[integer - 1];
|
||||
|
||||
star.title = this._getHint(score, evt);
|
||||
}
|
||||
},
|
||||
|
||||
_starName: function(score, evt) {
|
||||
var decimal = +(score % 1).toFixed(2);
|
||||
|
||||
if (evt || this.isMove) {
|
||||
return decimal > 0.5 ? 'starOn' : 'starHalf';
|
||||
}
|
||||
|
||||
if (decimal <= this.opt.round.down) { // Down: [x.00 ... x.25]
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.opt.halfShow && decimal < this.opt.round.up) { // Half: [x.26 ... x.75]
|
||||
return 'starHalf';
|
||||
}
|
||||
|
||||
if (decimal < this.opt.round.full) { // Off: [x.26 .. x.6]
|
||||
return 'starOff';
|
||||
}
|
||||
|
||||
return 'starOn'; // Up: [x.26 ...] || [x.6 ...]
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_target: function(score, evt) {
|
||||
if (this.opt.target) {
|
||||
var target = $(this.opt.target);
|
||||
|
||||
if (!target.length) {
|
||||
this._error('Target selector invalid or missing!');
|
||||
}
|
||||
|
||||
var mouseover = evt && evt.type === 'mouseover';
|
||||
|
||||
if (score === undefined) {
|
||||
score = this.opt.targetText;
|
||||
} else if (score === null) {
|
||||
score = mouseover ? this.opt.cancelHint : this.opt.targetText;
|
||||
} else {
|
||||
if (this.opt.targetType === 'hint') {
|
||||
score = this._getHint(score, evt);
|
||||
} else if (this.opt.precision) {
|
||||
score = parseFloat(score).toFixed(1);
|
||||
}
|
||||
|
||||
var mousemove = evt && evt.type === 'mousemove';
|
||||
|
||||
if (!mouseover && !mousemove && !this.opt.targetKeep) {
|
||||
score = this.opt.targetText;
|
||||
}
|
||||
}
|
||||
|
||||
this._setTarget(target, score);
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_turnOn: function(i, score) {
|
||||
return this.opt.single ? (i === score) : (i <= score);
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
_unlock: function() {
|
||||
this.element.style.cursor = 'pointer';
|
||||
this.element.removeAttribute('title');
|
||||
|
||||
this.scoreField.removeAttr('readonly');
|
||||
|
||||
this.self.data('readonly', false);
|
||||
|
||||
this._resetTitle();
|
||||
|
||||
if (this.cancelButton) {
|
||||
this.cancelButton.css('display', '');
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
cancel: function(click) {
|
||||
if (this.self.data('readonly') !== true) {
|
||||
this[click ? 'click' : 'score'].call(this, null);
|
||||
|
||||
this.scoreField.removeAttr('value');
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
click: function(score) {
|
||||
if (this.self.data('readonly') !== true) {
|
||||
score = this._adjustedScore(score);
|
||||
|
||||
this._apply(score);
|
||||
|
||||
if (this.opt.click) {
|
||||
this.opt.click.call(this.element, score, $.Event('click'));
|
||||
}
|
||||
|
||||
this._target(score);
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
getScore: function() {
|
||||
var score = [];
|
||||
var value ;
|
||||
|
||||
value = this.scoreField.val();
|
||||
|
||||
score.push(value ? +value : undefined);
|
||||
|
||||
return (score.length > 1) ? score : score[0];
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
move: function(score) {
|
||||
var integer = parseInt(score, 10);
|
||||
var decimal = this._getDecimal(score, 1);
|
||||
|
||||
if (integer >= this.opt.number) {
|
||||
integer = this.opt.number - 1;
|
||||
decimal = 10;
|
||||
}
|
||||
|
||||
var width = this._getWidth();
|
||||
var steps = width / 10;
|
||||
var star = $(this.stars[integer]);
|
||||
var percent = star.offset().left + steps * decimal;
|
||||
var evt = $.Event('mousemove', { pageX: percent });
|
||||
|
||||
this.isMove = true;
|
||||
|
||||
star.trigger(evt);
|
||||
|
||||
this.isMove = false;
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
readOnly: function(readonly) {
|
||||
if (this.self.data('readonly') !== readonly) {
|
||||
if (readonly) {
|
||||
this.self.off('.raty').children(this.opt.starType).off('.raty');
|
||||
|
||||
this._lock();
|
||||
} else {
|
||||
this._binds();
|
||||
this._unlock();
|
||||
}
|
||||
|
||||
this.self.data('readonly', readonly);
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: model spec
|
||||
score: function() {
|
||||
return arguments.length ? this.setScore.apply(this, arguments) : this.getScore();
|
||||
},
|
||||
|
||||
setScore: function(score) {
|
||||
if (this.self.data('readonly') !== true) {
|
||||
score = this._adjustedScore(score);
|
||||
|
||||
this._apply(score);
|
||||
this._target(score);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Raty;
|
||||
})();
|
||||
}));
|
||||
6
resources/assets/plugins/owlcarousel/owl.carousel.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Owl Carousel v2.3.4
|
||||
* Copyright 2013-2018 David Deutsch
|
||||
* Licensed under: SEE LICENSE IN https://github.com/OwlCarousel2/OwlCarousel2/blob/master/LICENSE
|
||||
*/
|
||||
.owl-carousel,.owl-carousel .owl-item{-webkit-tap-highlight-color:transparent;position:relative}.owl-carousel{display:none;width:100%;z-index:1}.owl-carousel .owl-stage{position:relative;-ms-touch-action:pan-Y;touch-action:manipulation;-moz-backface-visibility:hidden}.owl-carousel .owl-stage:after{content:".";display:block;clear:both;visibility:hidden;line-height:0;height:0}.owl-carousel .owl-stage-outer{position:relative;overflow:hidden;-webkit-transform:translate3d(0,0,0)}.owl-carousel .owl-item,.owl-carousel .owl-wrapper{-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0)}.owl-carousel .owl-item{min-height:1px;float:left;-webkit-backface-visibility:hidden;-webkit-touch-callout:none}.owl-carousel .owl-item img{display:block;width:100%}.owl-carousel .owl-dots.disabled,.owl-carousel .owl-nav.disabled{display:none}.no-js .owl-carousel,.owl-carousel.owl-loaded{display:block}.owl-carousel .owl-dot,.owl-carousel .owl-nav .owl-next,.owl-carousel .owl-nav .owl-prev{cursor:pointer;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.owl-carousel .owl-nav button.owl-next,.owl-carousel .owl-nav button.owl-prev,.owl-carousel button.owl-dot{background:0 0;color:inherit;border:none;padding:0!important;font:inherit}.owl-carousel.owl-loading{opacity:0;display:block}.owl-carousel.owl-hidden{opacity:0}.owl-carousel.owl-refresh .owl-item{visibility:hidden}.owl-carousel.owl-drag .owl-item{-ms-touch-action:pan-y;touch-action:pan-y;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.owl-carousel.owl-grab{cursor:move;cursor:grab}.owl-carousel.owl-rtl{direction:rtl}.owl-carousel.owl-rtl .owl-item{float:right}.owl-carousel .animated{animation-duration:1s;animation-fill-mode:both}.owl-carousel .owl-animated-in{z-index:0}.owl-carousel .owl-animated-out{z-index:1}.owl-carousel .fadeOut{animation-name:fadeOut}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.owl-height{transition:height .5s ease-in-out}.owl-carousel .owl-item .owl-lazy{opacity:0;transition:opacity .4s ease}.owl-carousel .owl-item .owl-lazy:not([src]),.owl-carousel .owl-item .owl-lazy[src^=""]{max-height:0}.owl-carousel .owl-item img.owl-lazy{transform-style:preserve-3d}.owl-carousel .owl-video-wrapper{position:relative;height:100%;background:#000}.owl-carousel .owl-video-play-icon{position:absolute;height:80px;width:80px;left:50%;top:50%;margin-left:-40px;margin-top:-40px;background:url(owl.video.play.png) no-repeat;cursor:pointer;z-index:1;-webkit-backface-visibility:hidden;transition:transform .1s ease}.owl-carousel .owl-video-play-icon:hover{-ms-transform:scale(1.3,1.3);transform:scale(1.3,1.3)}.owl-carousel .owl-video-playing .owl-video-play-icon,.owl-carousel .owl-video-playing .owl-video-tn{display:none}.owl-carousel .owl-video-tn{opacity:0;height:100%;background-position:center center;background-repeat:no-repeat;background-size:contain;transition:opacity .4s ease}.owl-carousel .owl-video-frame{position:relative;z-index:1;height:100%;width:100%}
|
||||
7
resources/assets/plugins/owlcarousel/owl.carousel.min.js
vendored
Normal file
6
resources/assets/plugins/owlcarousel/owl.theme.default.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Owl Carousel v2.3.4
|
||||
* Copyright 2013-2018 David Deutsch
|
||||
* Licensed under: SEE LICENSE IN https://github.com/OwlCarousel2/OwlCarousel2/blob/master/LICENSE
|
||||
*/
|
||||
.owl-theme .owl-dots,.owl-theme .owl-nav{text-align:center;-webkit-tap-highlight-color:transparent}.owl-theme .owl-nav{margin-top:10px}.owl-theme .owl-nav [class*=owl-]{color:#FFF;font-size:14px;margin:5px;padding:4px 7px;background:#D6D6D6;display:inline-block;cursor:pointer;border-radius:3px}.owl-theme .owl-nav [class*=owl-]:hover{background:#869791;color:#FFF;text-decoration:none}.owl-theme .owl-nav .disabled{opacity:.5;cursor:default}.owl-theme .owl-nav.disabled+.owl-dots{margin-top:10px}.owl-theme .owl-dots .owl-dot{display:inline-block;zoom:1}.owl-theme .owl-dots .owl-dot span{width:10px;height:10px;margin:5px 7px;background:#D6D6D6;display:block;-webkit-backface-visibility:visible;transition:opacity .2s ease;border-radius:30px}.owl-theme .owl-dots .owl-dot.active span,.owl-theme .owl-dots .owl-dot:hover span{background:#869791}
|
||||
97
resources/views/september/catalog.blade.php
Normal file
@@ -0,0 +1,97 @@
|
||||
@extends('themes::september.layout')
|
||||
|
||||
@php
|
||||
$years = Cache::remember('all_years', \Backpack\Settings\app\Models\Setting::get('site_cache_ttl', 5 * 60), function () {
|
||||
return \VsMov\Core\Models\Movie::select('publish_year')
|
||||
->distinct()
|
||||
->pluck('publish_year')
|
||||
->sortDesc();
|
||||
});
|
||||
@endphp
|
||||
|
||||
@section('breadcrumb')
|
||||
<div class="breadcrumb block md:flex items-center container mx-auto mt-2 w-full line-clamp-1">
|
||||
<div class="ml-2 md:ml-0">
|
||||
<ol class="flex flex-wrap items-center gap-1" itemScope itemType="https://schema.org/BreadcrumbList">
|
||||
<li itemProp="itemListElement" itemScope itemType="http://schema.org/ListItem">
|
||||
<a class="flex items-center gap-x-1" itemProp="item" title="Xem phim" href="/">
|
||||
<span class="flex items-center gap-x-1 text-main-primary hover:text-main-warning" itemProp="name">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M8.25 21v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21m0 0h4.5V3.545M12.75 21h7.5V10.75M2.25 21h1.5m18 0h-18M2.25 9l4.5-1.636M18.75 3l-1.5.545m0 6.205l3 1m1.5.5l-1.5-.5M6.75 7.364V3h-3v18m3-13.636l10.5-3.819" />
|
||||
</svg>
|
||||
Xem phim
|
||||
</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
<meta itemProp="position" content="1" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="inline text-gray-400" itemprop="itemListElement" itemscope=""
|
||||
itemtype="http://schema.org/ListItem">
|
||||
<a itemprop="item" href="{{ url()->current() }}" title="{{ $section_name ?? 'Danh Sách Phim' }}">
|
||||
<span itemprop="name">
|
||||
{{ $section_name ?? 'Danh Sách Phim' }}
|
||||
</span>
|
||||
</a>
|
||||
<meta itemprop="position" content="2">
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
@include('themes::september.inc.catalog_filter')
|
||||
<section class="w-full mt-2">
|
||||
<div class="flex justify-between">
|
||||
<div class="py-2 w-max">
|
||||
<h3
|
||||
class="text-base md:text-2xl uppercase font-semibold text-transparent bg-clip-text bg-gradient-to-r from-[#7367F0] to-[#8e84fc]">
|
||||
{{ $section_name }}</h3>
|
||||
<div class="w-full h-0.5 bg-main-indigo bg-gradient-to-r from-[#7367F0] to-[#8e84fc]"></div>
|
||||
</div>
|
||||
</div>
|
||||
@if (count($data))
|
||||
<div
|
||||
class="grid grid-flow-row grid-cols-2 sm:grid-cols-3 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6 gap-2">
|
||||
@foreach ($data ?? [] as $movie)
|
||||
<a href="{{ $movie->getUrl() }}" class="overflow-hidden relative group"
|
||||
title="{{ $movie->name ?? '' }}">
|
||||
<img style="aspect-ratio: 256/340" data-src="{{ $movie->getThumbUrl() }}"
|
||||
alt="{{ $movie->name ?? '' }}"
|
||||
class="group-hover:opacity-60 transition-all duration-500 transform group-hover:scale-110 lazyload" />
|
||||
<span
|
||||
class="absolute bottom-0 px-2 pb-2 pt-16 bg-gradient-to-t from-main-900 w-full text-main-warning">
|
||||
<div class="font-bold overflow-hidden overflow-ellipsis whitespace-nowrap">{{ $movie->name }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-300 italic overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{{ $movie->origin_name }} ({{ $movie->publish_year }})
|
||||
</div>
|
||||
</span>
|
||||
<span class="absolute top-0 left-0 p-0.5 bg-main-primary text-sm">
|
||||
{{ $movie->episode_current }}
|
||||
</span>
|
||||
<div class="absolute hidden top-1/3 left-1/3 animate-pulse group-hover:block">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-16 h-16 text-main-warning">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M15.91 11.672a.375.375 0 010 .656l-5.603 3.113a.375.375 0 01-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112z" />
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-row flex-wrap flex-grow h-50 mt-10">
|
||||
<p class="w-full text-center text-white">Rất tiếc, không có nội dung nào trùng khớp yêu cầu.</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{ $data->appends(request()->all())->links('themes::september.inc.pagination') }}
|
||||
</section>
|
||||
@endsection
|
||||
608
resources/views/september/episode.blade.php
Normal file
@@ -0,0 +1,608 @@
|
||||
@extends('themes::september.layout')
|
||||
|
||||
@section('breadcrumb')
|
||||
<div class="breadcrumb block md:flex items-center container mx-auto mt-2 w-full line-clamp-1">
|
||||
<div class="ml-2 md:ml-0">
|
||||
<ol class="flex flex-wrap items-center gap-1" itemScope itemType="https://schema.org/BreadcrumbList">
|
||||
<li itemProp="itemListElement" itemScope itemType="http://schema.org/ListItem">
|
||||
<a class="flex items-center gap-x-1" itemProp="item" title="Xem phim" href="/">
|
||||
<span class="flex items-center gap-x-1 text-main-primary hover:text-main-warning" itemProp="name">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M8.25 21v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21m0 0h4.5V3.545M12.75 21h7.5V10.75M2.25 21h1.5m18 0h-18M2.25 9l4.5-1.636M18.75 3l-1.5.545m0 6.205l3 1m1.5.5l-1.5-.5M6.75 7.364V3h-3v18m3-13.636l10.5-3.819" />
|
||||
</svg>
|
||||
Xem phim
|
||||
</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 inline">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
<meta itemProp="position" content="1" />
|
||||
</a>
|
||||
</li>
|
||||
@foreach ($currentMovie->regions as $region)
|
||||
<li itemprop="itemListElement" itemscope="" itemtype="http://schema.org/ListItem">
|
||||
<a class="text-main-primary hover:text-main-warning" itemprop="item" href="{{ $region->getUrl() }}"
|
||||
title="{{ $region->name }}">
|
||||
<span itemprop="name">
|
||||
{{ $region->name }}
|
||||
</span>
|
||||
</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 inline">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
<meta itemprop="position" content="3">
|
||||
</li>
|
||||
@endforeach
|
||||
@foreach ($currentMovie->categories as $category)
|
||||
<li itemprop="itemListElement" itemscope="" itemtype="http://schema.org/ListItem">
|
||||
<a class="text-main-primary hover:text-main-warning" itemprop="item"
|
||||
href="{{ $category->getUrl() }}" title="{{ $category->name }}">
|
||||
<span itemprop="name">
|
||||
{{ $category->name }}
|
||||
</span>
|
||||
</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 inline">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
<meta itemprop="position" content="3">
|
||||
</li>
|
||||
@endforeach
|
||||
<li itemprop="itemListElement" itemscope="" itemtype="http://schema.org/ListItem">
|
||||
<a class="text-main-primary hover:text-main-warning" itemprop="item"
|
||||
href="{{ $currentMovie->getUrl() }}" title="{{ $currentMovie->name }}">
|
||||
<span itemprop="name">
|
||||
{{ $currentMovie->name }}
|
||||
</span>
|
||||
</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 inline">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
<meta itemprop="position" content="4">
|
||||
</li>
|
||||
<li class="inline text-gray-400" itemprop="itemListElement" itemscope=""
|
||||
itemtype="http://schema.org/ListItem">
|
||||
<a itemprop="item" href="{{ url()->current() }}" title="Tập {{ $episode->name }}">
|
||||
<span itemprop="name">
|
||||
Tập {{ $episode->name }}
|
||||
</span>
|
||||
</a>
|
||||
<meta itemprop="position" content="5">
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('player_wraper')
|
||||
<div class="w-full table relative container mx-auto my-2 px-2 lg:px-0">
|
||||
<div class="relative table-cell bg-main-800/40">
|
||||
<div class="relative iframe w-full" style="aspect-ratio: 16 / 9;" id="player-wrapper">
|
||||
|
||||
</div>
|
||||
<div class="flex justify-between justify-items-center items-center w-full h-[44px] p-1">
|
||||
<div class="flex items-center">
|
||||
<div
|
||||
class="text-sm md:text-base py-2.5 flex flex-wrap md:grid grid-flow-row grid-cols-5 gap-1 md:gap-2">
|
||||
@foreach ($currentMovie->episodes->where('slug', $episode->slug)->where('server', $episode->server) as $server)
|
||||
<a class="streaming-server relative w-content text-center bg-main-labelbgSecondary text-white hover:shadow-menu hover:bg-main-primary duration-150 px-2 md:px-3 py-1 cursor-pointer"
|
||||
onclick="chooseStreamingServer(this)" data-type="{{ $server->type }}"
|
||||
data-id="{{ $server->id }}" data-link="{{ $server->link }}">
|
||||
<span>VIP #{{ $loop->index + 1 }}</span>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="block lg:flex items-center">
|
||||
<span id="toggleModal-report"
|
||||
class="w-content text-center bg-main-danger text-white hover:shadow-menu hover:bg-main-primary duration-150 px-2 md:px-3 py-1 cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 inline">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 12.75c1.148 0 2.278.08 3.383.237 1.037.146 1.866.966 1.866 2.013 0 3.728-2.35 6.75-5.25 6.75S6.75 18.728 6.75 15c0-1.046.83-1.867 1.866-2.013A24.204 24.204 0 0112 12.75zm0 0c2.883 0 5.647.508 8.207 1.44a23.91 23.91 0 01-1.152 6.06M12 12.75c-2.883 0-5.647.508-8.208 1.44.125 2.104.52 4.136 1.153 6.06M12 12.75a2.25 2.25 0 002.248-2.354M12 12.75a2.25 2.25 0 01-2.248-2.354M12 8.25c.995 0 1.971-.08 2.922-.236.403-.066.74-.358.795-.762a3.778 3.778 0 00-.399-2.25M12 8.25c-.995 0-1.97-.08-2.922-.236-.402-.066-.74-.358-.795-.762a3.734 3.734 0 01.4-2.253M12 8.25a2.25 2.25 0 00-2.248 2.146M12 8.25a2.25 2.25 0 012.248 2.146M8.683 5a6.032 6.032 0 01-1.155-1.002c.07-.63.27-1.222.574-1.747m.581 2.749A3.75 3.75 0 0115.318 5m0 0c.427-.283.815-.62 1.155-.999a4.471 4.471 0 00-.575-1.752M4.921 6a24.048 24.048 0 00-.392 3.314c1.668.546 3.416.914 5.223 1.082M19.08 6c.205 1.08.337 2.187.392 3.314a23.882 23.882 0 01-5.223 1.082" />
|
||||
</svg>
|
||||
Báo lỗi
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="episode relative table-row lg:table-cell overflow-hidden lg:w-1/4 bg-main-800/40">
|
||||
<div class="lg:absolute inset-0">
|
||||
<div class="hidden lg:block text-xl font-bold m-2">{{ $currentMovie->name }}</div>
|
||||
<div class="flex flex-wrap" id="tabs-id">
|
||||
<div class="w-full">
|
||||
<ul class="flex mb-0 list-none flex-wrap pt-3 flex-row p-1 lg:p-0">
|
||||
@foreach ($currentMovie->episodes->sortBy([['server', 'asc']])->groupBy('server') as $server => $data)
|
||||
<li class="-mb-px mr-2 last:mr-0 flex-auto text-center cursor-pointer">
|
||||
<a class="text-xs font-bold uppercase px-1 py-2 shadow-lg block leading-normal text-white bg-main-labelbgSecondary @if ($episode->server == $server) bg-main-primary @endif hover:shadow-menu hover:bg-main-primary"
|
||||
onclick="changeAtiveTab(event,'tab-server-{{ $loop->index }}')">
|
||||
@if ($episode->server == $server)
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 inline">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z" />
|
||||
</svg>
|
||||
@endif {{ $server }}
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
<div class="relative tab-content tab-space mt-1 p-2 scroll overflow-auto max-h-32 md:max-h-[345px] lg:max-h-[370px] xl:max-h-[475px] 2xl:max-h-[602px]">
|
||||
@foreach ($currentMovie->episodes->sortBy([['server', 'asc']])->groupBy('server') as $server => $data)
|
||||
<div class="@if ($episode->server != $server) hidden @endif grid grid-flow-row grid-cols-3 md:grid-cols-4 lg:grid-cols-4 xl:grid-cols-5 gap-2"
|
||||
id="tab-server-{{ $loop->index }}">
|
||||
@foreach ($data->sortByDesc('name', SORT_NATURAL)->groupBy('name') as $name => $item)
|
||||
<a class="relative w-content text-center bg-main-labelbgSecondary @if ($item->contains($episode)) bg-main-primary @endif text-white hover:shadow-menu hover:bg-main-primary duration-150 py-1 overflow-hidden overflow-ellipsis whitespace-nowrap"
|
||||
title="{{ $name }}" href="{{ $item->sortByDesc('type')->first()->getUrl() }}">
|
||||
{{ $name }}
|
||||
@if ($item->contains($episode))
|
||||
<span class="flex absolute h-3 w-3 top-0 right-0 -mt-1 -mr-1">
|
||||
<span
|
||||
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-orange-400 opacity-75"></span>
|
||||
<span
|
||||
class="relative inline-flex rounded-full h-3 w-3 bg-orange-500"></span>
|
||||
</span>
|
||||
@endif
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="bg-main-800/40 p-2 mt-2 md:flex flex-wrap">
|
||||
<div class="relative w-full h-full pt-0 md:w-1/3 lg:w-1/3 xl:w-1/6">
|
||||
<img style="aspect-ratio: 266/400" src="{{ $currentMovie->getThumbUrl() }}" alt="{{ $currentMovie->name }}"
|
||||
class="w-full h-auto max-h-96" />
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-2/3 lg:w-2/3 xl:w-5/6 md:pl-2 pt-0 md:mt-0 text-sm">
|
||||
<div class="bg-main-800/40 text-center py-1">
|
||||
<h1 class="uppercase text-xl font-extrabold text-main-primary">Xem phim {{ $currentMovie->name }} Tập
|
||||
{{ $episode->name }}</h1>
|
||||
<h2 class="italic text-main-orange">{{ $currentMovie->origin_name }}</h2>
|
||||
<div class="items-center text-center gap-x-2">
|
||||
<div id="movies-rating-star" class="flex justify-center" style="height: 18px;"></div>
|
||||
<div class="text-white align-middle">
|
||||
({{$currentMovie->getRatingStar()}}
|
||||
sao
|
||||
/
|
||||
{{$currentMovie->getRatingCount()}} đánh giá)
|
||||
</div>
|
||||
<div id="movies-rating-msg" class="text-[#FDB813] mb-2 font-bold text-sm mt-2"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<article class="block bg-main-800/40 p-2 mt-2">
|
||||
<div class="border-b-[1px] border-main-secondary border-opacity-25 py-1 scroll overflow-auto max-h-32">
|
||||
@if ($currentMovie->content)
|
||||
{!! $currentMovie->content !!}
|
||||
@else
|
||||
<p>Đang cập nhật ...</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ul class="flex flex-wrap gap-1 py-1">
|
||||
@foreach ($currentMovie->tags as $tag)
|
||||
<li class="bg-main-labelbgPrimary text-white hover:text-main-warning px-2">
|
||||
<a href="{{ $tag->getUrl() }}" title="{{ $tag->name }}">{{ $tag->name }}</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white mt-2 w-full" style="background-color: #ffffff">
|
||||
<div class="fb-comments w-full" data-href="{{ $currentMovie->getUrl() }}" data-width="100%"
|
||||
data-numposts="5" data-colorscheme="light" data-lazy="true">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="w-full mt-2">
|
||||
<div class="flex justify-between">
|
||||
<div class="py-2 w-max">
|
||||
<h3
|
||||
class="text-base md:text-2xl uppercase font-semibold text-transparent bg-clip-text bg-gradient-to-r from-[#7367F0] to-[#8e84fc]">
|
||||
Có thể bạn muốn xem</h3>
|
||||
</div>
|
||||
</div>
|
||||
@php
|
||||
$item['data'] = $movie_related;
|
||||
@endphp
|
||||
@include('themes::september.inc.section.section_thumb')
|
||||
</section>
|
||||
|
||||
|
||||
<div id="modal-report" class="relative z-10 hidden" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
||||
<div class="fixed inset-0 bg-black bg-opacity-75 transition-opacity"></div>
|
||||
<div class="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<div id="modal-report-form"
|
||||
class="relative transform overflow-hidden text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg">
|
||||
<div class=" bg-main-800/60 p-2">
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div
|
||||
class="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-red-600">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="w-full mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 class="text-lg font-medium leading-6 text-gray-100" id="modal-title">Báo lỗi phim</h3>
|
||||
<div class="mt-2">
|
||||
<textarea id="report_message" class="w-full p-3 bg-main-900 text-white focus:outline-none" rows="5"
|
||||
placeholder="Hãy nhập nội dung lỗi để chúng mình sửa nhanh hơn...">Không tải được tập phim</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-main-900/60 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
|
||||
<button type="button" id="report_episode_btn"
|
||||
class="inline-flex w-full justify-center border border-transparent bg-main-primary px-2 py-1 text-base font-medium text-white shadow-sm hover:bg-main-primary/80 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 sm:ml-3 sm:w-auto sm:text-sm">Gửi</button>
|
||||
<button type="button" id="close-report-modal"
|
||||
class="mt-3 inline-flex w-full justify-center border border-gray-300 bg-white px-2 py-1 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">Đóng</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
|
||||
<script src="/themes/september/player/js/p2p-media-loader-core.min.js"></script>
|
||||
<script src="/themes/september/player/js/p2p-media-loader-hlsjs.min.js"></script>
|
||||
|
||||
<script src="/js/jwplayer-8.9.3.js"></script>
|
||||
<script src="/js/hls.min.js"></script>
|
||||
<script src="/js/jwplayer.hlsjs.min.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("#toggleModal-report").click(() => {
|
||||
$("#modal-report").toggle("hidden");
|
||||
$("#modal-report-backdrop").toggle("hidden");
|
||||
})
|
||||
$('body').click(function(event) {
|
||||
if (($(event.target).closest("#modal-report").length && !$(event.target).closest(
|
||||
"#modal-report-form")
|
||||
.length) || $(event.target).closest("#close-report-modal").length) {
|
||||
$("#modal-report").toggle("hidden");
|
||||
$("#modal-report-backdrop").toggle("hidden");
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('html, body').animate({
|
||||
scrollTop: $('#player-wrapper').offset().top - 40
|
||||
}, 'slow');
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
function changeAtiveTab(event, tabID) {
|
||||
let element = event.target;
|
||||
while (element.nodeName !== "A") {
|
||||
element = element.parentNode;
|
||||
}
|
||||
ulElement = element.parentNode.parentNode;
|
||||
aElements = ulElement.querySelectorAll("li > a");
|
||||
tabContents = document.getElementById("tabs-id").querySelectorAll(".tab-content > div");
|
||||
for (let i = 0; i < aElements.length; i++) {
|
||||
aElements[i].classList.remove("text-white");
|
||||
aElements[i].classList.remove("bg-main-primary");
|
||||
aElements[i].classList.add("text-white");
|
||||
aElements[i].classList.add("bg-main-labelbgSecondary");
|
||||
tabContents[i].classList.add("hidden");
|
||||
tabContents[i].classList.remove("block");
|
||||
}
|
||||
element.classList.remove("text-white");
|
||||
element.classList.remove("bg-main-labelbgSecondary");
|
||||
element.classList.add("text-white");
|
||||
element.classList.add("bg-main-primary");
|
||||
document.getElementById(tabID).classList.remove("hidden");
|
||||
document.getElementById(tabID).classList.add("block");
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
var episode_id = {{$episode->id}};
|
||||
const wrapper = document.getElementById('player-wrapper');
|
||||
const vastAds = "{{ Setting::get('jwplayer_advertising_file') }}";
|
||||
|
||||
function chooseStreamingServer(el) {
|
||||
const type = el.dataset.type;
|
||||
const link = el.dataset.link.replace(/^http:\/\//i, 'https://');
|
||||
const id = el.dataset.id;
|
||||
|
||||
const newUrl =
|
||||
location.protocol +
|
||||
"//" +
|
||||
location.host +
|
||||
location.pathname.replace(`-${episode_id}`, `-${id}`);
|
||||
|
||||
history.pushState({
|
||||
path: newUrl
|
||||
}, "", newUrl);
|
||||
episode_id = id;
|
||||
|
||||
|
||||
Array.from(document.getElementsByClassName('streaming-server')).forEach(server => {
|
||||
server.classList.remove('bg-main-primary');
|
||||
})
|
||||
el.classList.add('bg-main-primary');
|
||||
document.getElementById('stream-server-active')?.remove();
|
||||
|
||||
el.innerHTML +=
|
||||
`<span id="stream-server-active" class="flex absolute h-3 w-3 top-0 right-0 -mt-1 -mr-1"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-purple-400 opacity-75"></span><span class="relative inline-flex rounded-full h-3 w-3 bg-purple-500"></span></span>`;
|
||||
|
||||
renderPlayer(type, link, id);
|
||||
}
|
||||
|
||||
function renderPlayer(type, link, id) {
|
||||
if (type == 'embed') {
|
||||
if (vastAds) {
|
||||
wrapper.innerHTML = `<div id="fake_jwplayer"></div>`;
|
||||
const fake_player = jwplayer("fake_jwplayer");
|
||||
const objSetupFake = {
|
||||
key: "{{ Setting::get('jwplayer_license') }}",
|
||||
aspectratio: "16:9",
|
||||
width: "100%",
|
||||
file: "/themes/september/player/1s_blank.mp4",
|
||||
volume: 100,
|
||||
mute: false,
|
||||
autostart: true,
|
||||
advertising: {
|
||||
tag: "{{ Setting::get('jwplayer_advertising_file') }}",
|
||||
client: "vast",
|
||||
vpaidmode: "insecure",
|
||||
skipoffset: {{ (int) Setting::get('jwplayer_advertising_skipoffset') ?: 5 }}, // Bỏ qua quảng cáo trong vòng 5 giây
|
||||
skipmessage: "Bỏ qua sau xx giây",
|
||||
skiptext: "Bỏ qua"
|
||||
}
|
||||
};
|
||||
fake_player.setup(objSetupFake);
|
||||
fake_player.on('complete', function(event) {
|
||||
$("#fake_jwplayer").remove();
|
||||
wrapper.innerHTML = `<iframe width="100%" height="100%" src="${link}" frameborder="0" scrolling="no"
|
||||
allowfullscreen="" allow='autoplay'></iframe>`
|
||||
fake_player.remove();
|
||||
});
|
||||
|
||||
fake_player.on('adSkipped', function(event) {
|
||||
$("#fake_jwplayer").remove();
|
||||
wrapper.innerHTML = `<iframe width="100%" height="100%" src="${link}" frameborder="0" scrolling="no"
|
||||
allowfullscreen="" allow='autoplay'></iframe>`
|
||||
fake_player.remove();
|
||||
});
|
||||
|
||||
fake_player.on('adComplete', function(event) {
|
||||
$("#fake_jwplayer").remove();
|
||||
wrapper.innerHTML = `<iframe width="100%" height="100%" src="${link}" frameborder="0" scrolling="no"
|
||||
allowfullscreen="" allow='autoplay'></iframe>`
|
||||
fake_player.remove();
|
||||
});
|
||||
} else {
|
||||
if (wrapper) {
|
||||
wrapper.innerHTML = `<iframe width="100%" height="100%" src="${link}" frameborder="0" scrolling="no"
|
||||
allowfullscreen="" allow='autoplay'></iframe>`
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 'm3u8' || type == 'mp4') {
|
||||
wrapper.innerHTML = `<div id="jwplayer"></div>`;
|
||||
const player = jwplayer("jwplayer");
|
||||
const objSetup = {
|
||||
key: "{{ Setting::get('jwplayer_license') }}",
|
||||
aspectratio: "16:9",
|
||||
width: "100%",
|
||||
image: "{{ $currentMovie->getPosterUrl() }}",
|
||||
file: link,
|
||||
playbackRateControls: true,
|
||||
playbackRates: [0.25, 0.75, 1, 1.25],
|
||||
sharing: {
|
||||
sites: [
|
||||
"reddit",
|
||||
"facebook",
|
||||
"twitter",
|
||||
"googleplus",
|
||||
"email",
|
||||
"linkedin",
|
||||
],
|
||||
},
|
||||
volume: 100,
|
||||
mute: false,
|
||||
autostart: true,
|
||||
logo: {
|
||||
file: "{{ Setting::get('jwplayer_logo_file') }}",
|
||||
link: "{{ Setting::get('jwplayer_logo_link') }}",
|
||||
position: "{{ Setting::get('jwplayer_logo_position') }}",
|
||||
},
|
||||
advertising: {
|
||||
tag: "{{ Setting::get('jwplayer_advertising_file') }}",
|
||||
client: "vast",
|
||||
vpaidmode: "insecure",
|
||||
skipoffset: {{ (int) Setting::get('jwplayer_advertising_skipoffset') ?: 5 }}, // Bỏ qua quảng cáo trong vòng 5 giây
|
||||
skipmessage: "Bỏ qua sau xx giây",
|
||||
skiptext: "Bỏ qua"
|
||||
}
|
||||
};
|
||||
|
||||
if (type == 'm3u8') {
|
||||
const segments_in_queue = 50;
|
||||
|
||||
var engine_config = {
|
||||
debug: !1,
|
||||
segments: {
|
||||
forwardSegmentCount: 50,
|
||||
},
|
||||
loader: {
|
||||
cachedSegmentExpiration: 864e5,
|
||||
cachedSegmentsCount: 1e3,
|
||||
requiredSegmentsPriority: segments_in_queue,
|
||||
httpDownloadMaxPriority: 9,
|
||||
httpDownloadProbability: 0.06,
|
||||
httpDownloadProbabilityInterval: 1e3,
|
||||
httpDownloadProbabilitySkipIfNoPeers: !0,
|
||||
p2pDownloadMaxPriority: 50,
|
||||
httpFailedSegmentTimeout: 500,
|
||||
simultaneousP2PDownloads: 20,
|
||||
simultaneousHttpDownloads: 2,
|
||||
// httpDownloadInitialTimeout: 12e4,
|
||||
// httpDownloadInitialTimeoutPerSegment: 17e3,
|
||||
httpDownloadInitialTimeout: 0,
|
||||
httpDownloadInitialTimeoutPerSegment: 17e3,
|
||||
httpUseRanges: !0,
|
||||
maxBufferLength: 300,
|
||||
// useP2P: false,
|
||||
},
|
||||
};
|
||||
if (Hls.isSupported() && p2pml.hlsjs.Engine.isSupported()) {
|
||||
var engine = new p2pml.hlsjs.Engine(engine_config);
|
||||
player.setup(objSetup);
|
||||
jwplayer_hls_provider.attach();
|
||||
p2pml.hlsjs.initJwPlayer(player, {
|
||||
liveSyncDurationCount: segments_in_queue, // To have at least 7 segments in queue
|
||||
maxBufferLength: 300,
|
||||
loader: engine.createLoaderClass(),
|
||||
});
|
||||
} else {
|
||||
player.setup(objSetup);
|
||||
}
|
||||
} else {
|
||||
player.setup(objSetup);
|
||||
}
|
||||
|
||||
const resumeData = 'OPCMS-PlayerPosition-' + id;
|
||||
|
||||
player.on('ready', function() {
|
||||
if (typeof(Storage) !== 'undefined') {
|
||||
if (localStorage[resumeData] == '' || localStorage[resumeData] == 'undefined') {
|
||||
console.log("No cookie for position found");
|
||||
var currentPosition = 0;
|
||||
} else {
|
||||
if (localStorage[resumeData] == "null") {
|
||||
localStorage[resumeData] = 0;
|
||||
} else {
|
||||
var currentPosition = localStorage[resumeData];
|
||||
}
|
||||
console.log("Position cookie found: " + localStorage[resumeData]);
|
||||
}
|
||||
player.once('play', function() {
|
||||
console.log('Checking position cookie!');
|
||||
console.log(Math.abs(player.getDuration() - currentPosition));
|
||||
if (currentPosition > 180 && Math.abs(player.getDuration() - currentPosition) >
|
||||
5) {
|
||||
player.seek(currentPosition);
|
||||
}
|
||||
});
|
||||
window.onunload = function() {
|
||||
localStorage[resumeData] = player.getPosition();
|
||||
}
|
||||
} else {
|
||||
console.log('Your browser is too old!');
|
||||
}
|
||||
});
|
||||
|
||||
player.on('complete', function() {
|
||||
if (typeof(Storage) !== 'undefined') {
|
||||
localStorage.removeItem(resumeData);
|
||||
} else {
|
||||
console.log('Your browser is too old!');
|
||||
}
|
||||
})
|
||||
|
||||
function formatSeconds(seconds) {
|
||||
var date = new Date(1970, 0, 1);
|
||||
date.setSeconds(seconds);
|
||||
return date.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1");
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const episode = '{{$episode->id}}';
|
||||
let playing = document.querySelector(`[data-id="${episode}"]`);
|
||||
if (playing) {
|
||||
playing.click();
|
||||
return;
|
||||
}
|
||||
|
||||
const servers = document.getElementsByClassName('streaming-server');
|
||||
if (servers[0]) {
|
||||
servers[0].click();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
$("#report_episode_btn").click(() => {
|
||||
fetch("{{ route('episodes.report', ['movie' => $currentMovie->slug, 'episode' => $episode->slug, 'id' => $episode->id]) }}", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute(
|
||||
'content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
message: document.getElementById('report_message')
|
||||
.innerHTML ??
|
||||
''
|
||||
})
|
||||
});
|
||||
$("#modal-report").toggle("hidden");
|
||||
$("#modal-report-backdrop").toggle("hidden");
|
||||
$("#toggleModal-report").remove();
|
||||
})
|
||||
</script>
|
||||
|
||||
<script src="/themes/september/plugins/jquery-raty/jquery.raty.js"></script>
|
||||
<link href="/themes/september/plugins/jquery-raty/jquery.raty.css" rel="stylesheet" type="text/css" />
|
||||
<script>
|
||||
var rated = false;
|
||||
$('#movies-rating-star').raty({
|
||||
score: {{$currentMovie->getRatingStar()}},
|
||||
number: 10,
|
||||
numberMax: 10,
|
||||
hints: ['quá tệ', 'tệ', 'không hay', 'không hay lắm', 'bình thường', 'xem được', 'có vẻ hay', 'hay',
|
||||
'rất hay', 'siêu phẩm'
|
||||
],
|
||||
starOff: '/themes/september/plugins/jquery-raty/images/star-off.png',
|
||||
starOn: '/themes/september/plugins/jquery-raty/images/star-on.png',
|
||||
starHalf: '/themes/september/plugins/jquery-raty/images/star-half.png',
|
||||
click: function(score, evt) {
|
||||
if (rated) return
|
||||
fetch("{{ route('movie.rating', ['movie' => $currentMovie->slug]) }}", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'X-CSRF-TOKEN': document.querySelector(
|
||||
'meta[name="csrf-token"]')
|
||||
.getAttribute(
|
||||
'content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
rating: score
|
||||
})
|
||||
});
|
||||
rated = true;
|
||||
$('#movies-rating-star').data('raty').readOnly(true);
|
||||
$('#movies-rating-msg').html(`Bạn đã đánh giá ${score} sao cho phim này!`);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{!! setting('site_scripts_facebook_sdk') !!}
|
||||
@endpush
|
||||
10
resources/views/september/inc/aside.blade.php
Normal file
@@ -0,0 +1,10 @@
|
||||
@foreach ($tops as $top)
|
||||
<div class="bg-main-800/40 p-2 mt-2">
|
||||
<div class="text-center">
|
||||
<h3 class="font-semibold uppercase">{{ $top['label'] }}</h3>
|
||||
</div>
|
||||
<div class="space-y-1 block">
|
||||
@include('themes::september.inc.aside.' . $top['template'])
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
54
resources/views/september/inc/aside/top_text.blade.php
Normal file
@@ -0,0 +1,54 @@
|
||||
@foreach ($top['data'] ?? [] as $movie)
|
||||
@if ($loop->first)
|
||||
<a href="{{ $movie->getUrl() }}" title="{{ $movie->name }}" class="block overflow-hidden relative group">
|
||||
<img src="{{ $movie->getPosterUrl() }}"
|
||||
class="group-hover:opacity-60 transition-all duration-500 w-full h-[200px]" alt="{{ $movie->name }}" />
|
||||
<span class="absolute bottom-0 px-2 pb-2 pt-16 bg-gradient-to-t from-main-900 w-full text-main-warning">
|
||||
<div class="font-bold overflow-hidden overflow-ellipsis whitespace-nowrap">{{ $movie->name }}</div>
|
||||
<div class="text-sm text-gray-300 italic overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{{ $movie->origin_name }} ({{ $movie->publish_year }})
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<div class="absolute top-0 right-0 bg-main-800/40 p-2 flex gap-x-1 items-center text-main-success">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
|
||||
<span class="text-xs">
|
||||
{{ $movie->view_week ?? 0 }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ $movie->getUrl() }}" title="{{ $movie->name }}" class="group">
|
||||
<div
|
||||
class="flex justify-start items-center space-x-1 py-1 border-b-[1px] border-main-secondary border-dashed border-opacity-25">
|
||||
<div
|
||||
class="w-6 text-center bg-main-primary group-hover:text-main-warning bg-opacity-10 text-main-primary font-bold">
|
||||
{{ $loop->index + 1 }}</div>
|
||||
<div class="basis-4/6 flex-grow px-2 space-y-2 overflow-hidden">
|
||||
<div
|
||||
class="font-bold text-main-primary text-sm group-hover:text-main-warning transition duration-300 ease-in-out overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{{ $movie->name }}</div>
|
||||
</div>
|
||||
<div class="flex gap-x-0.5 items-center text-gray-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
|
||||
<span class="text-xs">
|
||||
{{ $movie->view_week ?? 0 }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@endif
|
||||
@endforeach
|
||||
32
resources/views/september/inc/aside/top_thumb.blade.php
Normal file
@@ -0,0 +1,32 @@
|
||||
@foreach ($top['data'] ?? [] as $movie)
|
||||
<a href="{{ $movie->getUrl() }}" title="{{ $movie->name }}" class="group">
|
||||
<div
|
||||
class="flex justify-start items-center space-x-1 py-1 border-b-[1px] border-main-secondary border-dashed border-opacity-25">
|
||||
<div
|
||||
class="w-6 text-center bg-main-primary group-hover:text-main-warning bg-opacity-10 text-main-primary font-bold">
|
||||
{{ $loop->index + 1 }}</div>
|
||||
<div class="w-[45px] h-[57px]">
|
||||
<img data-src="{{ $movie->getThumbUrl() }}" alt="{{ $movie->origin_name }} ({{ $movie->publish_year }})"
|
||||
class="group-hover:opacity-60 w-full h-full transition duration-300 ease-in-out lazyload" />
|
||||
</div>
|
||||
<div class="basis-4/6 flex-grow px-2 space-y-2 overflow-hidden">
|
||||
<div
|
||||
class="font-bold text-main-primary text-sm group-hover:text-main-warning transition duration-300 ease-in-out overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{{ $movie->name }}</div>
|
||||
<div class="italic text-xs text-gray-400 overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{{ $movie->origin_name }} ({{ $movie->publish_year }})</div>
|
||||
</div>
|
||||
<div class="flex gap-x-0.5 items-center text-gray-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
<span class="text-xs">
|
||||
{{ $movie->view_week ?? 0 }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
60
resources/views/september/inc/catalog_filter.blade.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<div class="w-full container mx-auto mt-2 bg-main-800/40">
|
||||
<div class="text-gray-50">
|
||||
<form id="form-filter" class="lg:flex flex-wrap gap-1 items-center">
|
||||
<div class="p-2 flex justify-between">
|
||||
Lọc Phim
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<select name="filter[sort]" form="form-search" class="scroll bg-main-700 p-2 outline-none">
|
||||
<option value="">Sắp xếp</option>
|
||||
<option value="update" @if (isset(request('filter')['sort']) && request('filter')['sort'] == 'update') selected @endif>Thời gian cập nhật</option>
|
||||
<option value="create" @if (isset(request('filter')['sort']) && request('filter')['sort'] == 'create') selected @endif>Thời gian đăng</option>
|
||||
<option value="year" @if (isset(request('filter')['sort']) && request('filter')['sort'] == 'year') selected @endif>Năm sản xuất</option>
|
||||
<option value="view" @if (isset(request('filter')['sort']) && request('filter')['sort'] == 'view') selected @endif>Lượt xem</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<select name="filter[type]" form="form-search" class="scroll bg-main-700 p-2 outline-none">
|
||||
<option value="">Mọi định dạng</option>
|
||||
<option value="series" @if (isset(request('filter')['type']) && request('filter')['type'] == 'series') selected @endif>Phim bộ</option>
|
||||
<option value="single" @if (isset(request('filter')['type']) && request('filter')['type'] == 'single') selected @endif>Phim lẻ</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<select name="filter[category]" form="form-search" class="scroll bg-main-700 p-2 outline-none">
|
||||
<option value="">Tất cả thể loại</option>
|
||||
@foreach (\VsMov\Core\Models\Category::fromCache()->all() as $item)
|
||||
<option value="{{ $item->id }}" @if ((isset(request('filter')['category']) && request('filter')['category'] == $item->id) ||
|
||||
(isset($category) && $category->id == $item->id)) selected @endif>
|
||||
{{ $item->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<select name="filter[region]" form="form-search" class="scroll bg-main-700 p-2 outline-none">
|
||||
<option value="">Tất cả quốc gia</option>
|
||||
@foreach (\VsMov\Core\Models\Region::fromCache()->all() as $item)
|
||||
<option value="{{ $item->id }}" @if ((isset(request('filter')['region']) && request('filter')['region'] == $item->id) ||
|
||||
(isset($region) && $region->id == $item->id)) selected @endif>
|
||||
{{ $item->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<select name="filter[year]" form="form-search" class="scroll bg-main-700 p-2 outline-none">
|
||||
<option value="">Tất cả năm</option>
|
||||
@foreach ($years as $year)
|
||||
<option value="{{ $year }}" @if (isset(request('filter')['year']) && request('filter')['year'] == $year) selected @endif>
|
||||
{{ $year }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<button type="submit" form="form-search"
|
||||
class="w-content text-center bg-main-labelbgSecondary text-white hover:shadow-menu hover:bg-main-primary duration-150 p-1.5 overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
<span>Duyệt phim</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
41
resources/views/september/inc/header.blade.php
Executable file
@@ -0,0 +1,41 @@
|
||||
@php
|
||||
$logo = setting('site_logo', '');
|
||||
$brand = setting('site_brand', '');
|
||||
$title = isset($title) ? $title : setting('site_homepage_title', '');
|
||||
@endphp
|
||||
|
||||
<!-- BEGIN HEADER -->
|
||||
<header class="w-full bg-main-800 px-1 xl:px-7 py-3 shadow-md">
|
||||
<div class="container mx-auto flex justify-between items-center">
|
||||
<div id="header-search-form" class="relative w-96 hidden xl:block">
|
||||
<form id="form-search" method="GET" action="/">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 absolute ml-3 mt-2 text-xl">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 15.75l-2.489-2.489m0 0a3.375 3.375 0 10-4.773-4.773 3.375 3.375 0 004.774 4.774zM21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<input
|
||||
class="appearance-none border border-input-border rounded-3xl w-full py-2 px-3 bg-main-700 text-gray-300 leading-tight focus: focus:outline-none focus:shadow-outline focus:border-input-focus pl-10"
|
||||
type="text" name="search" placeholder="Tìm kiếm phim..." value="{{ request('search') }}" />
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
<a href="/" class="navbar-brand">
|
||||
@if ($logo)
|
||||
{!! $logo !!}
|
||||
@else
|
||||
{!! $brand !!}
|
||||
@endif
|
||||
</a>
|
||||
</div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div class="flex xl:hidden gap-x-4 px-2">
|
||||
<svg id="header-search-button" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-8 h-8 block xl:hidden cursor-pointer">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
|
||||
</svg>
|
||||
<svg id="header-menu-button" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-8 h-8 block xl:hidden cursor-pointer">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 5.25h16.5m-16.5 4.5h16.5m-16.5 4.5h16.5m-16.5 4.5h16.5" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<!-- END HEADER -->
|
||||
37
resources/views/september/inc/nav.blade.php
Executable file
@@ -0,0 +1,37 @@
|
||||
<!-- BEGIN NAVBAR -->
|
||||
<nav id="nav-menu"
|
||||
class="hidden xl:block container mx-auto py-0 px-2 bg-main-700 xl:bg-main-800 xl:mt-5 shadow-navbar rounded-md rounded-t-none xl:rounded-t">
|
||||
<ul class="w-full xl:flex items-center bg-main-700 xl:bg-main-800 px-1 py-1 xl:space-x-4 text-[#D0D2D6]">
|
||||
@foreach ($menu as $item)
|
||||
<li
|
||||
class="group leading-6 cursor-pointer hover:bg-[#161D31] transition duration-150 ease-in-out py-2 my-1 rounded-md px-4 xl:px-2 tracking-wide relative block xl:flex xl:justify-between items-center space-x-1">
|
||||
@if (count($item['children']))
|
||||
<span>{{ $item->name }}</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 inline" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
<div class="group-hover:block hidden top-full left-0 xl:absolute h-auto pt-4 z-20">
|
||||
<ul
|
||||
class="w-full xl:w-max grid grid-flow-rows grid-cols-2 md:grid-cols-4 gap-1 bg-main-700 xl:bg-main-800 rounded-md xl:shadow max-h-48 md:max-h-full overflow-auto">
|
||||
@foreach ($item['children'] as $children)
|
||||
<li class="py-1 px-2 xl:py-2 xl:px-4">
|
||||
<a href="{{ $children['link'] }}"
|
||||
class="block text-gray-300 text-base cursor-pointer hover:translate-x-1 transition duration-200">
|
||||
{{ $children['name'] }}
|
||||
</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@else
|
||||
<a href="{{ $item['link'] }}"
|
||||
class="block text-gray-300 text-base cursor-pointer transition duration-200">
|
||||
{{ $item['name'] }}
|
||||
</a>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</nav>
|
||||
<!-- END NAVBAR -->
|
||||
121
resources/views/september/inc/pagination.blade.php
Normal file
@@ -0,0 +1,121 @@
|
||||
@if ($paginator->hasPages())
|
||||
<nav role="navigation" aria-label="{{ __('Pagination Navigation') }}" class="flex items-center justify-between my-2">
|
||||
<div class="flex justify-between flex-1 md:hidden">
|
||||
@if ($paginator->onFirstPage())
|
||||
<div></div>
|
||||
{{-- <span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-main-800/40 hover:bg-main-primary hover:shadow-menu shadow-md cursor-default leading-5 rounded-md">
|
||||
{!! __('pagination.previous') !!}
|
||||
</span> --}}
|
||||
@else
|
||||
<a href="{{ $paginator->previousPageUrl() }}"
|
||||
class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-main-800/40 hover:bg-main-primary hover:shadow-menu shadow-md leading-5 rounded-md">
|
||||
{!! __('pagination.previous') !!}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if ($paginator->hasMorePages())
|
||||
<a href="{{ $paginator->nextPageUrl() }}"
|
||||
class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-white bg-main-800/40 hover:bg-main-primary hover:shadow-menu shadow-md leading-5 rounded-md">
|
||||
{!! __('pagination.next') !!}
|
||||
</a>
|
||||
@else
|
||||
{{-- <span class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-500 bg-main-800/40 hover:bg-main-primary hover:shadow-menu shadow-md cursor-default leading-5 rounded-md">
|
||||
{!! __('pagination.next') !!}
|
||||
</span> --}}
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="hidden md:flex-1 md:flex md:items-center md:justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-white leading-5">
|
||||
{!! __('Showing') !!}
|
||||
@if ($paginator->firstItem())
|
||||
<span class="font-medium">{{ $paginator->firstItem() }}</span>
|
||||
{!! __('to') !!}
|
||||
<span class="font-medium">{{ $paginator->lastItem() }}</span>
|
||||
@else
|
||||
{{ $paginator->count() }}
|
||||
@endif
|
||||
{!! __('of') !!}
|
||||
<span class="font-medium">{{ $paginator->total() }}</span>
|
||||
{!! __('results') !!}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span class="relative z-0 inline-flex shadow-sm rounded-md">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
{{-- <span aria-disabled="true" aria-label="{{ __('pagination.previous') }}">
|
||||
<span class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-main-800/40 hover:bg-main-primary hover:shadow-menu shadow-md cursor-default rounded-l-md leading-5" aria-hidden="true">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
</span> --}}
|
||||
@else
|
||||
<a href="{{ $paginator->previousPageUrl() }}" rel="prev"
|
||||
class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-white bg-main-800/40 hover:bg-main-primary hover:shadow-menu shadow-md rounded-l-md leading-5focus:z-10 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150"
|
||||
aria-label="{{ __('pagination.previous') }}">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
@endif
|
||||
|
||||
{{-- Pagination Elements --}}
|
||||
@foreach ($elements as $element)
|
||||
{{-- "Three Dots" Separator --}}
|
||||
@if (is_string($element))
|
||||
<span aria-disabled="true">
|
||||
<span
|
||||
class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-white bg-main-800/40 hover:bg-main-primary hover:shadow-menu shadow-md cursor-default leading-5">{{ $element }}</span>
|
||||
</span>
|
||||
@endif
|
||||
|
||||
{{-- Array Of Links --}}
|
||||
@if (is_array($element))
|
||||
@foreach ($element as $page => $url)
|
||||
@if ($page == $paginator->currentPage())
|
||||
<span aria-current="page">
|
||||
<span
|
||||
class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-white bg-main-primary hover:bg-opacity-80 shadow-md cursor-default leading-5">{{ $page }}</span>
|
||||
</span>
|
||||
@else
|
||||
<a href="{{ $url }}"
|
||||
class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-white bg-main-800/40 hover:bg-main-primary hover:shadow-menu shadow-md leading-5 focus:z-10 active:bg-gray-100 active:text-white transition ease-in-out duration-150"
|
||||
aria-label="{{ __('Go to page :page', ['page' => $page]) }}">
|
||||
{{ $page }}
|
||||
</a>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<a href="{{ $paginator->nextPageUrl() }}" rel="next"
|
||||
class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-white bg-main-800/40 hover:bg-main-primary hover:shadow-menu shadow-md rounded-r-md leading-5 focus:z-10 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150"
|
||||
aria-label="{{ __('pagination.next') }}">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
@else
|
||||
{{-- <span aria-disabled="true" aria-label="{{ __('pagination.next') }}">
|
||||
<span class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-main-800/40 hover:bg-main-primary hover:shadow-menu shadow-md cursor-default rounded-r-md leading-5" aria-hidden="true">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
</span> --}}
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@endif
|
||||
@@ -0,0 +1,31 @@
|
||||
<div class="grid grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-5 grid-flow-row gap-2">
|
||||
@foreach ($item['data'] ?? [] as $movie)
|
||||
@if ($loop->first)
|
||||
<a href="{{ $movie->getUrl() }}" title="{{ $movie->name ?? '' }}"
|
||||
class="row-span-1 col-span-2 md:row-span-2 md:col-span-2 overflow-hidden relative group">
|
||||
<img
|
||||
src="{{ $movie->getPosterUrl() }}" alt="{{ $movie->name ?? '' }}"
|
||||
class="group-hover:opacity-60 transition-all duration-500 transform group-hover:scale-110 w-full h-full" />
|
||||
<span class="absolute bottom-0 px-2 pb-2 pt-16 bg-gradient-to-t from-main-900 w-full text-main-warning">
|
||||
<div class="font-bold overflow-hidden overflow-ellipsis whitespace-nowrap">{{ $movie->name }}</div>
|
||||
<div class="text-sm text-gray-300 italic overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{{ $movie->origin_name }} ({{ $movie->publish_year }})
|
||||
</div>
|
||||
</span>
|
||||
<span class="absolute top-0 left-0 p-0.5 bg-main-primary text-sm">{{ $movie->episode_current }}</span>
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ $movie->getUrl() }}" title="{{ $movie->name ?? '' }}" class="@if ($loop->last) hidden lg:block @endif overflow-hidden relative group">
|
||||
<img data-src="{{ $movie->getPosterUrl() }}" alt="{{ $movie->name ?? '' }}"
|
||||
class="group-hover:opacity-60 transition-all duration-500 transform group-hover:scale-110 w-full h-full lazyload" />
|
||||
<span class="absolute bottom-0 px-2 pb-2 pt-16 bg-gradient-to-t from-main-900 w-full text-main-warning">
|
||||
<div class="font-bold overflow-hidden overflow-ellipsis whitespace-nowrap">{{ $movie->name }}</div>
|
||||
<div class="text-sm text-gray-300 italic overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{{ $movie->origin_name }} ({{ $movie->publish_year }})
|
||||
</div>
|
||||
</span>
|
||||
<span class="absolute top-0 left-0 p-0.5 bg-main-primary text-sm">{{ $movie->episode_current }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@@ -0,0 +1,27 @@
|
||||
<div
|
||||
class="grid grid-flow-row grid-cols-2 sm:grid-cols-3 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6 gap-2">
|
||||
@foreach ($item['data'] ?? [] as $movie)
|
||||
<a href="{{ $movie->getUrl() }}" class="overflow-hidden relative group" title="{{ $movie->name ?? '' }}">
|
||||
<img style="aspect-ratio: 256/340" data-src="{{ $movie->getThumbUrl() }}" alt="{{ $movie->name ?? '' }}"
|
||||
class="w-full group-hover:opacity-60 transition-all duration-500 transform group-hover:scale-110 lazyload" />
|
||||
<span class="absolute bottom-0 px-2 pb-2 pt-16 bg-gradient-to-t from-main-900 w-full text-main-warning">
|
||||
<div class="font-bold overflow-hidden overflow-ellipsis whitespace-nowrap">{{ $movie->name }}</div>
|
||||
<div class="text-sm text-gray-300 italic overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{{ $movie->origin_name }} ({{ $movie->publish_year }})
|
||||
</div>
|
||||
</span>
|
||||
<span class="absolute top-0 left-0 p-0.5 bg-main-primary text-sm">
|
||||
{{ $movie->episode_current }}
|
||||
</span>
|
||||
<div class="absolute hidden top-1/3 left-1/3 animate-pulse group-hover:block">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-16 h-16 text-main-warning">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M15.91 11.672a.375.375 0 010 .656l-5.603 3.113a.375.375 0 01-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112z" />
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@@ -0,0 +1,67 @@
|
||||
<div id="slider_poster_{{ $loop->index }}" class="owl-carousel owl-theme absolute">
|
||||
@foreach ($item['data'] ?? [] as $movie)
|
||||
<a href="{{ $movie->getUrl() }}" title="{{ $movie->name ?? '' }}"
|
||||
class="block row-span-1 col-span-1 md:row-span-2 md:col-span-2 overflow-hidden relative group">
|
||||
<img style="aspect-ratio: 16/9" src="{{ $movie->getPosterUrl() }}" alt="{{ $movie->name ?? '' }}"
|
||||
class="group-hover:opacity-60 transition-all duration-500 transform group-hover:bg-opacity-60" />
|
||||
<span class="absolute bottom-0 px-2 pb-2 pt-16 bg-gradient-to-t from-main-900 w-full text-main-warning">
|
||||
<div class="font-bold overflow-hidden overflow-ellipsis whitespace-nowrap">{{ $movie->name }}</div>
|
||||
<div class="text-sm text-gray-300 italic overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{{ $movie->origin_name }} ({{ $movie->publish_year }})
|
||||
</div>
|
||||
</span>
|
||||
<span class="absolute top-0 left-0 p-0.5 bg-main-primary text-sm">{{ $movie->episode_current }}</span>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#slider_poster_{{ $loop->index }}").owlCarousel({
|
||||
items: 1,
|
||||
center: false,
|
||||
loop: true,
|
||||
dots: false,
|
||||
nav: true,
|
||||
margin: 10,
|
||||
stagePadding: 0,
|
||||
stageOuterClass: 'owl-stage-outer',
|
||||
responsive: {
|
||||
1400: {
|
||||
items: 4
|
||||
},
|
||||
1280: {
|
||||
items: 3
|
||||
},
|
||||
1024: {
|
||||
items: 2
|
||||
},
|
||||
768: {
|
||||
items: 2
|
||||
},
|
||||
},
|
||||
scrollPerPage: true,
|
||||
lazyLoad: true,
|
||||
slideSpeed: 800,
|
||||
paginationSpeed: 400,
|
||||
stopOnHover: true,
|
||||
autoplay: true,
|
||||
navText: [
|
||||
`<button class="block absolute top-0 left-0 text-white bg-main-900/60 h-[90%]">
|
||||
<span style="display: none" aria-label="Previous">‹</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-8 h-8 px-1 top-0 text-2xl">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18.75 19.5l-7.5-7.5 7.5-7.5m-6 15L5.25 12l7.5-7.5" />
|
||||
</svg>
|
||||
</button>`,
|
||||
`<button class="block absolute top-0 right-0 text-white bg-main-900/60 h-[90%]">
|
||||
<span style="display: none" aria-label="Next">›</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-8 h-8 px-1 top-0 text-2xl">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 4.5l7.5 7.5-7.5 7.5m-6-15l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</button>`
|
||||
],
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
67
resources/views/september/inc/section/slider_thumb.blade.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<div id="slider_thumb_{{ $loop->index }}" class="owl-carousel owl-theme absolute">
|
||||
@foreach ($item['data'] ?? [] as $movie)
|
||||
<a href="{{ $movie->getUrl() }}" title="{{ $movie->name ?? '' }}"
|
||||
class="block row-span-1 col-span-1 md:row-span-2 md:col-span-2 overflow-hidden relative group">
|
||||
<img style="aspect-ratio: 256/340" src="{{ $movie->getThumbUrl() }}" alt="{{ $movie->name ?? '' }}"
|
||||
class="group-hover:opacity-60 transition-all duration-500 transform group-hover:bg-opacity-60" />
|
||||
<span class="absolute bottom-0 px-2 pb-2 pt-16 bg-gradient-to-t from-main-900 w-full text-main-warning">
|
||||
<div class="font-bold overflow-hidden overflow-ellipsis whitespace-nowrap">{{ $movie->name }}</div>
|
||||
<div class="text-sm text-gray-300 italic overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{{ $movie->origin_name }} ({{ $movie->publish_year }})
|
||||
</div>
|
||||
</span>
|
||||
<span class="absolute top-0 left-0 p-0.5 bg-main-primary text-sm">{{ $movie->episode_current }}</span>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#slider_thumb_{{ $loop->index }}").owlCarousel({
|
||||
items: 2,
|
||||
center: false,
|
||||
loop: true,
|
||||
dots: false,
|
||||
nav: true,
|
||||
margin: 10,
|
||||
stagePadding: 0,
|
||||
stageOuterClass: 'owl-stage-outer',
|
||||
responsive: {
|
||||
1400: {
|
||||
items: 6
|
||||
},
|
||||
1280: {
|
||||
items: 4
|
||||
},
|
||||
1024: {
|
||||
items: 3
|
||||
},
|
||||
768: {
|
||||
items: 2
|
||||
},
|
||||
},
|
||||
scrollPerPage: true,
|
||||
lazyLoad: true,
|
||||
slideSpeed: 800,
|
||||
paginationSpeed: 400,
|
||||
stopOnHover: true,
|
||||
autoplay: true,
|
||||
navText: [
|
||||
`<button class="block absolute top-0 left-0 text-white bg-main-900/60 h-[90%]">
|
||||
<span style="display: none" aria-label="Previous">‹</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-8 h-8 px-1 top-0 text-2xl">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18.75 19.5l-7.5-7.5 7.5-7.5m-6 15L5.25 12l7.5-7.5" />
|
||||
</svg>
|
||||
</button>`,
|
||||
`<button class="block absolute top-0 right-0 text-white bg-main-900/60 h-[90%]">
|
||||
<span style="display: none" aria-label="Next">›</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-8 h-8 px-1 top-0 text-2xl">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 4.5l7.5 7.5-7.5 7.5m-6-15l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</button>`
|
||||
],
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
165
resources/views/september/index.blade.php
Normal file
@@ -0,0 +1,165 @@
|
||||
@extends('themes::september.layout')
|
||||
|
||||
@php
|
||||
use Ophim\Core\Models\Movie;
|
||||
|
||||
$recommendations = Cache::remember('site.movies.recommendations', setting('site_cache_ttl', 5 * 60), function () {
|
||||
return Movie::where('is_recommended', true)
|
||||
->limit(get_theme_option('recommendations_limit', 10))
|
||||
->orderBy('updated_at', 'desc')
|
||||
->get();
|
||||
});
|
||||
|
||||
$data = Cache::remember('site.movies.latest', setting('site_cache_ttl', 5 * 60), function () {
|
||||
$lists = preg_split('/[\n\r]+/', get_theme_option('latest'));
|
||||
$data = [];
|
||||
foreach ($lists as $list) {
|
||||
if (trim($list)) {
|
||||
$list = explode('|', $list);
|
||||
[$label, $relation, $field, $val, $limit, $link, $template] = array_merge($list, ['Phim mới cập nhật', '', 'type', 'series', 8, '/', 'section_thumb']);
|
||||
try {
|
||||
$data[] = [
|
||||
'label' => $label,
|
||||
'template' => $template,
|
||||
'data' => Movie::when($relation, function ($query) use ($relation, $field, $val) {
|
||||
$query->whereHas($relation, function ($rel) use ($field, $val) {
|
||||
$rel->where($field, $val);
|
||||
});
|
||||
})
|
||||
->when(!$relation, function ($query) use ($field, $val) {
|
||||
$query->where($field, $val);
|
||||
})
|
||||
->limit($limit)
|
||||
->orderBy('updated_at', 'desc')
|
||||
->get(),
|
||||
'link' => $link ?: '#',
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
});
|
||||
|
||||
@endphp
|
||||
|
||||
@section('slider_recommended')
|
||||
@if (count($recommendations))
|
||||
<div class="container mx-auto px-2 md:px-0">
|
||||
<section class="w-full mt-2">
|
||||
<div class="flex justify-between">
|
||||
<div class="py-2 w-max">
|
||||
<h3
|
||||
class="text-base md:text-2xl uppercase font-semibold text-transparent bg-clip-text bg-gradient-to-r from-[#7367F0] to-[#8e84fc]">
|
||||
Phim HOT</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="slider_recommend" class="owl-carousel owl-theme absolute">
|
||||
@foreach ($recommendations ?? [] as $movie)
|
||||
<a href="{{ $movie->getUrl() }}" title="{{ $movie->name ?? '' }}"
|
||||
class="block row-span-1 col-span-1 md:row-span-2 md:col-span-2 overflow-hidden relative group">
|
||||
<img style="aspect-ratio: 256/340" src="{{ $movie->getThumbUrl() }}" alt="{{ $movie->name ?? '' }}"
|
||||
class="group-hover:opacity-60 transition-all duration-500 transform group-hover:bg-opacity-60" />
|
||||
<span
|
||||
class="absolute bottom-0 px-2 pb-2 pt-16 bg-gradient-to-t from-main-900 w-full text-main-warning">
|
||||
<div class="font-bold overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{{ $movie->name }}
|
||||
</div>
|
||||
<div
|
||||
class="text-sm text-gray-300 italic overflow-hidden overflow-ellipsis whitespace-nowrap">
|
||||
{{ $movie->origin_name }} ({{ $movie->publish_year }})
|
||||
</div>
|
||||
</span>
|
||||
<span
|
||||
class="absolute top-0 left-0 p-0.5 bg-main-primary text-sm">{{ $movie->episode_current }}</span>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@endif
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<h1 class="h-text text-white uppercase mb-2">{{ $title }}</h1>
|
||||
@foreach ($data as $item)
|
||||
<section class="w-full mt-2">
|
||||
<div class="flex justify-between">
|
||||
<div class="py-2 w-max">
|
||||
<h3
|
||||
class="text-base md:text-2xl uppercase font-semibold text-transparent bg-clip-text bg-gradient-to-r from-[#7367F0] to-[#8e84fc]">
|
||||
{{ $item['label'] }}</h3>
|
||||
<div class="w-full h-0.5 bg-main-indigo bg-gradient-to-r from-[#7367F0] to-[#8e84fc]"></div>
|
||||
</div>
|
||||
|
||||
<a href="{{ $item['link'] }}" title="{{ $item['label'] }}">
|
||||
<div class="px-2 py-2 flex gap-x-2 text-md text-main-purple hover:text-main-orange items-center">
|
||||
<span>Xem thêm</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 4.5l7.5 7.5-7.5 7.5m-6-15l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
@include('themes::september.inc.section.' . $item['template'])
|
||||
</section>
|
||||
@endforeach
|
||||
@endsection
|
||||
|
||||
@section('footer')
|
||||
@parent
|
||||
<link rel="stylesheet" href="/themes/september/plugins/owlcarousel/owl.carousel.min.css">
|
||||
<link rel="stylesheet" href="/themes/september/plugins/owlcarousel/owl.theme.default.min.css">
|
||||
<script src="/themes/september/plugins/owlcarousel/owl.carousel.min.js"></script>
|
||||
@if (count($recommendations))
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#slider_recommend").owlCarousel({
|
||||
items: 2,
|
||||
center: false,
|
||||
loop: true,
|
||||
dots: false,
|
||||
nav: true,
|
||||
margin: 10,
|
||||
stagePadding: 0,
|
||||
stageOuterClass: 'owl-stage-outer',
|
||||
responsive: {
|
||||
1400: {
|
||||
items: 8
|
||||
},
|
||||
1280: {
|
||||
items: 6
|
||||
},
|
||||
1024: {
|
||||
items: 4
|
||||
},
|
||||
768: {
|
||||
items: 3
|
||||
},
|
||||
},
|
||||
scrollPerPage: true,
|
||||
lazyLoad: true,
|
||||
slideSpeed: 800,
|
||||
paginationSpeed: 400,
|
||||
stopOnHover: true,
|
||||
autoplay: true,
|
||||
navText: [
|
||||
`<button class="block absolute top-0 left-0 text-white bg-main-900/60 h-[90%]">
|
||||
<span style="display: none" aria-label="Previous">‹</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-8 h-8 px-1 top-0 text-2xl">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18.75 19.5l-7.5-7.5 7.5-7.5m-6 15L5.25 12l7.5-7.5" />
|
||||
</svg>
|
||||
</button>`,
|
||||
`<button class="block absolute top-0 right-0 text-white bg-main-900/60 h-[90%]">
|
||||
<span style="display: none" aria-label="Next">›</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-8 h-8 px-1 top-0 text-2xl">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 4.5l7.5 7.5-7.5 7.5m-6-15l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</button>`
|
||||
],
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endif
|
||||
@endsection
|
||||
90
resources/views/september/layout.blade.php
Executable file
@@ -0,0 +1,90 @@
|
||||
@extends('themes::layout')
|
||||
@php
|
||||
$menu = \VsMov\Core\Models\Menu::getTree();
|
||||
$tops = Cache::remember('site.movies.tops', setting('site_cache_ttl', 5 * 60), function () {
|
||||
$lists = preg_split('/[\n\r]+/', get_theme_option('hotest'));
|
||||
$data = [];
|
||||
foreach ($lists as $list) {
|
||||
if (trim($list)) {
|
||||
$list = explode('|', $list);
|
||||
[$label, $relation, $field, $val, $sortKey, $alg, $limit, $template] = array_merge($list, ['Phim hot', '', 'type', 'series', 'view_total', 'desc', 4, 'top_thumb']);
|
||||
try {
|
||||
$data[] = [
|
||||
'label' => $label,
|
||||
'template' => $template,
|
||||
'data' => \VsMov\Core\Models\Movie::when($relation, function ($query) use ($relation, $field, $val) {
|
||||
$query->whereHas($relation, function ($rel) use ($field, $val) {
|
||||
$rel->where($field, $val);
|
||||
});
|
||||
})
|
||||
->when(!$relation, function ($query) use ($field, $val) {
|
||||
$query->where($field, $val);
|
||||
})
|
||||
->orderBy($sortKey, $alg)
|
||||
->limit($limit)
|
||||
->get(),
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
# code
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
});
|
||||
@endphp
|
||||
|
||||
@push('header')
|
||||
<link href="/themes/september/css/styles.min.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,300;0,400;0,500;0,600;1,400;1,500;1,600" rel="stylesheet" />
|
||||
@endpush
|
||||
|
||||
@section('body')
|
||||
@include('themes::september.inc.header')
|
||||
@include('themes::september.inc.nav')
|
||||
@if (get_theme_option('ads_header'))
|
||||
<div class="container mx-auto items-center text-center mt-2">
|
||||
{!! get_theme_option('ads_header') !!}
|
||||
</div>
|
||||
@endif
|
||||
@yield('slider_recommended')
|
||||
@yield('breadcrumb')
|
||||
@yield('player_wraper')
|
||||
<div class="container mx-auto lg:flex px-2 md:px-0">
|
||||
<div class="lg:w-3/4 lg:pr-4">
|
||||
@yield('content')
|
||||
</div>
|
||||
<aside class="lg:w-1/4">
|
||||
@include('themes::september.inc.aside')
|
||||
</aside>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('footer')
|
||||
<footer class="bg-main-800 mt-2 relative bottom-0 clear-both">
|
||||
<div class="container mx-auto flex py-8">
|
||||
{!! get_theme_option('footer') !!}
|
||||
</div>
|
||||
</footer>
|
||||
@if (get_theme_option('ads_header'))
|
||||
<div class="relative">
|
||||
<div class="container mx-auto px-4 md:px-8 xl:px-40 fixed items-center text-center bottom-0 right-0 left-0 z-40">
|
||||
{!! get_theme_option('ads_catfish') !!}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<script src="/themes/september/js/jquery-3.6.0.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#header-search-button").click(() => {
|
||||
$("#header-search-form").toggle("fast", "swing");
|
||||
})
|
||||
|
||||
$("#header-menu-button").click(() => {
|
||||
$("#nav-menu").toggle("fast", "swing");
|
||||
})
|
||||
});
|
||||
</script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.2/lazysizes.min.js" integrity="sha512-q583ppKrCRc7N5O0n2nzUiJ+suUv7Et1JGels4bXOaMFQcamPk9HjdUknZuuFjBNs7tsMuadge5k9RzdmO+1GQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
{!! setting('site_scripts_google_analytics') !!}
|
||||
@endsection
|
||||
385
resources/views/september/single.blade.php
Normal file
@@ -0,0 +1,385 @@
|
||||
@extends('themes::september.layout')
|
||||
|
||||
@section('breadcrumb')
|
||||
<div class="breadcrumb block md:flex items-center container mx-auto mt-2 w-full line-clamp-1">
|
||||
<div class="ml-2 md:ml-0">
|
||||
<ol class="flex flex-wrap items-center gap-1" itemScope itemType="https://schema.org/BreadcrumbList">
|
||||
<li itemProp="itemListElement" itemScope itemType="http://schema.org/ListItem">
|
||||
<a class="flex items-center gap-x-1" itemProp="item" title="Xem phim" href="/">
|
||||
<span class="flex items-center gap-x-1 text-main-primary hover:text-main-warning" itemProp="name">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M8.25 21v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21m0 0h4.5V3.545M12.75 21h7.5V10.75M2.25 21h1.5m18 0h-18M2.25 9l4.5-1.636M18.75 3l-1.5.545m0 6.205l3 1m1.5.5l-1.5-.5M6.75 7.364V3h-3v18m3-13.636l10.5-3.819" />
|
||||
</svg>
|
||||
Xem phim
|
||||
</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 inline">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
<meta itemProp="position" content="1" />
|
||||
</a>
|
||||
</li>
|
||||
@foreach ($currentMovie->regions as $region)
|
||||
<li itemprop="itemListElement" itemscope="" itemtype="http://schema.org/ListItem">
|
||||
<a class="text-main-primary hover:text-main-warning" itemprop="item" href="{{ $region->getUrl() }}"
|
||||
title="{{ $region->name }}">
|
||||
<span itemprop="name">
|
||||
{{ $region->name }}
|
||||
</span>
|
||||
</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 inline">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
<meta itemprop="position" content="3">
|
||||
</li>
|
||||
@endforeach
|
||||
@foreach ($currentMovie->categories as $category)
|
||||
<li itemprop="itemListElement" itemscope="" itemtype="http://schema.org/ListItem">
|
||||
<a class="text-main-primary hover:text-main-warning" itemprop="item"
|
||||
href="{{ $category->getUrl() }}" title="{{ $category->name }}">
|
||||
<span itemprop="name">
|
||||
{{ $category->name }}
|
||||
</span>
|
||||
</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 inline">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
<meta itemprop="position" content="3">
|
||||
</li>
|
||||
@endforeach
|
||||
<li class="inline text-gray-400" itemprop="itemListElement" itemscope=""
|
||||
itemtype="http://schema.org/ListItem">
|
||||
<a itemprop="item" href="{{ $currentMovie->getUrl() }}" title="{{ $currentMovie->name }}">
|
||||
<span itemprop="name">
|
||||
{{ $currentMovie->name }}
|
||||
</span>
|
||||
</a>
|
||||
<meta itemprop="position" content="4">
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
@if ($currentMovie->showtimes && $currentMovie->showtimes != '')
|
||||
<div class="mt-2.5 p-2 bg-main-800 mb-1 border-main-warning/60 border-[1px] border-dashed text-gray-300">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 animate-pulse inline">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5m-9-6h.008v.008H12v-.008zM12 15h.008v.008H12V15zm0 2.25h.008v.008H12v-.008zM9.75 15h.008v.008H9.75V15zm0 2.25h.008v.008H9.75v-.008zM7.5 15h.008v.008H7.5V15zm0 2.25h.008v.008H7.5v-.008zm6.75-4.5h.008v.008h-.008v-.008zm0 2.25h.008v.008h-.008V15zm0 2.25h.008v.008h-.008v-.008zm2.25-4.5h.008v.008H16.5v-.008zm0 2.25h.008v.008H16.5V15z" />
|
||||
</svg>
|
||||
Lịch chiếu: <span class="text-yellow-500">{!! $currentMovie->showtimes !!}</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($currentMovie->notify && $currentMovie->notify != '')
|
||||
<div class="mt-2.5 p-2 bg-main-800 mb-1 border-main-warning/60 border-[1px] border-dashed text-gray-300">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 animate-pulse inline">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0M3.124 7.5A8.969 8.969 0 015.292 3m13.416 0a8.969 8.969 0 012.168 4.5" />
|
||||
</svg>
|
||||
Thông báo: <span class="text-main-blue">{{ strip_tags($currentMovie->notify) }}</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="bg-main-800/40 p-2 mt-2 md:flex flex-wrap">
|
||||
<div class="relative overflow-hidden w-full h-full pt-0 md:w-1/3 lg:w-1/3 xl:w-1/4">
|
||||
<img style="aspect-ratio: 256/340" src="{{ $currentMovie->getThumbUrl() }}"
|
||||
alt="{{ $currentMovie->name }} - {{ $currentMovie->origin_name }} ({{ $currentMovie->publish_year }})"
|
||||
class="w-full h-auto max-h-96" />
|
||||
|
||||
@if ($currentMovie->is_copyright)
|
||||
<div
|
||||
class="absolute top-[7%] -left-[34%] text-white uppercase py-[4px] px-0 text-[12px] w-full text-center -rotate-45 bg-gradient-to-r from-red-500">
|
||||
bản quyền</div>
|
||||
@endif
|
||||
|
||||
<div class="absolute top-0 right-0 bg-main-800/80 p-2 flex items-center gap-x-1 text-main-success">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
<span class="text-xs">
|
||||
{{ $currentMovie->view_total }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-4 text-center w-full bg-main-700 bg-opacity-40 py-2 m-0">
|
||||
@if ($currentMovie->trailer_url)
|
||||
<label id="toggleModal-trailer"
|
||||
class="bg-main-blue text-gray-50 inline-block px-3 py-2 shadow-none hover:shadow-secondary duration-150 cursor-pointer">
|
||||
Trailer
|
||||
</label>
|
||||
@endif
|
||||
{{-- optimize sau --}}
|
||||
@if (!$currentMovie->is_copyright && count($currentMovie->episodes) && $currentMovie->episodes[0]['link'] != '')
|
||||
<a href="{{ $currentMovie->episodes->sortBy([['server', 'asc']])->groupBy('server')->first()->sortByDesc('name', SORT_NATURAL)->groupBy('name')->last()->sortByDesc('type')->first()->getUrl() }}">
|
||||
<div
|
||||
class="bg-main-primary text-gray-50 inline-block px-3 py-2 shadow-none hover:shadow-primary duration-150">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
stroke="currentColor" class="w-6 h-6 inline">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M15.91 11.672a.375.375 0 010 .656l-5.603 3.113a.375.375 0 01-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112z" />
|
||||
</svg>
|
||||
Xem phim
|
||||
</div>
|
||||
</a>
|
||||
@else
|
||||
<div class="text-white">Đang cập nhật...</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($currentMovie->trailer_url)
|
||||
@php
|
||||
parse_str(parse_url($currentMovie->trailer_url, PHP_URL_QUERY), $parse_url);
|
||||
$trailer_id = $parse_url['v'];
|
||||
@endphp
|
||||
<div id="modal-trailer" class="relative z-10 hidden" aria-labelledby="modal-title" role="dialog"
|
||||
aria-modal="true">
|
||||
<div class="fixed inset-0 bg-black bg-opacity-75 transition-opacity"></div>
|
||||
<div class="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<div id="modal-trailer-iframe"
|
||||
class="relative transform overflow-hidden text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg">
|
||||
<div class=" bg-main-800/60 p-2">
|
||||
<iframe width="100%" height="315"
|
||||
src="https://www.youtube.com/embed/{{ $trailer_id }}" title="YouTube video player"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
frameborder="0" scrolling="no" allowfullscreen></iframe>
|
||||
</div>
|
||||
<div class="bg-main-900/60 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
|
||||
<button type="button" id="close-trailer-modal"
|
||||
class="mt-3 inline-flex w-full justify-center border border-gray-300 bg-white px-2 py-1 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">Đóng</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="w-full md:w-2/3 lg:w-2/3 xl:w-3/4 md:pl-2 pt-0 md:mt-0 text-sm">
|
||||
<div class="bg-main-700/40 text-center py-2">
|
||||
<h1 class="uppercase text-xl font-extrabold text-main-primary">{{ $currentMovie->name }}</h1>
|
||||
<h2 class="italic text-main-orange">{{ $currentMovie->origin_name ?? '' }}</h2>
|
||||
<div class="mt-2">
|
||||
<div class="items-center text-center gap-x-2">
|
||||
<div id="movies-rating-star" class="flex justify-center" style="height: 18px;"></div>
|
||||
<div class="text-white align-middle">
|
||||
({{ $currentMovie->getRatingStar() }}
|
||||
sao
|
||||
/
|
||||
{{$currentMovie->getRatingCount()}} đánh giá)
|
||||
</div>
|
||||
<div id="movies-rating-msg" class="text-[#FDB813] mb-2 font-bold text-sm mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="grid grid-flow-row grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 mt-2 gap-2">
|
||||
<li>
|
||||
<label class="font-bold text-white">Trạng thái: </label>
|
||||
<span class="px-1 bg-main-labelbgWarning text-main-warning">{{ $currentMovie->getStatus() }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<label class="font-bold text-white">Tập hiện tại: </label>
|
||||
<span class="px-1 bg-main-labelbgSuccess text-main-success">{{ $currentMovie->episode_current }} {{ $currentMovie->language }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<label class="font-bold text-white">Số tập: </label>
|
||||
<span class="px-1 bg-main-labelbgInfo text-main-info">
|
||||
{{ $currentMovie->episode_total ?? 'N/A' }}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<label class="font-bold text-white">Thời lượng: </label>
|
||||
<span class="">{{ $currentMovie->episode_time ?? 'N/A' }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<label class="font-bold text-white">Năm phát hành: </label>
|
||||
<span class="">{{ $currentMovie->publish_year }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<label class="font-bold text-white">Chất lượng: </label>
|
||||
<span class="">{{ $currentMovie->quality }}</span>
|
||||
</li>
|
||||
|
||||
<li class="col-span-1 md:col-span-2 lg:col-span-2 xl:col-span-3 line-clamp-2">
|
||||
<label class="font-bold text-white">Thể loại: </label>
|
||||
<span class="">
|
||||
{!! $currentMovie->categories->map(function ($category) {
|
||||
return '<a href="' .
|
||||
$category->getUrl() .
|
||||
'" title="' .
|
||||
$category->name .
|
||||
'" class="text-main-primary hover:text-main-orange">' .
|
||||
$category->name .
|
||||
'</a>';
|
||||
})->implode(', ') !!}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="col-span-1 md:col-span-2 lg:col-span-2 xl:col-span-3 line-clamp-2">
|
||||
<label class="font-bold text-white">Quốc gia: </label>
|
||||
<span class="">
|
||||
{!! $currentMovie->regions->map(function ($region) {
|
||||
return '<a href="' .
|
||||
$region->getUrl() .
|
||||
'" title="' .
|
||||
$region->name .
|
||||
'" class="text-main-primary hover:text-main-orange">' .
|
||||
$region->name .
|
||||
'</a>';
|
||||
})->implode(', ') !!}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="col-span-1 md:col-span-2 lg:col-span-2 xl:col-span-3 line-clamp-2">
|
||||
<label class="font-bold text-white">Diễn viên: </label>
|
||||
<span class="">
|
||||
{!! $currentMovie->actors->map(function ($actor) {
|
||||
return '<a href="' .
|
||||
$actor->getUrl() .
|
||||
'" tite="Diễn viên ' .
|
||||
$actor->name .
|
||||
'" class="text-main-primary hover:text-main-orange">' .
|
||||
$actor->name .
|
||||
'</a>';
|
||||
})->implode(', ') !!}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="col-span-1 md:col-span-2 lg:col-span-2 xl:col-span-3 line-clamp-2">
|
||||
<label class="font-bold text-white">Đạo diễn: </label>
|
||||
<span class="">
|
||||
{!! $currentMovie->directors->map(function ($director) {
|
||||
return '<a href="' .
|
||||
$director->getUrl() .
|
||||
'" tite="Đạo diễn ' .
|
||||
$director->name .
|
||||
'" class="text-main-primary hover:text-main-orange">' .
|
||||
$director->name .
|
||||
'</a>';
|
||||
})->implode(', ') !!}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
@if ($currentMovie->type === "series" && !$currentMovie->is_copyright && count($currentMovie->episodes) && $currentMovie->episodes[0]['link'] != '')
|
||||
<li class="col-span-1 md:col-span-2 lg:col-span-2 xl:col-span-3">
|
||||
<label class="font-bold text-white">Tập mới nhất: </label>
|
||||
<span class="flex gap-1">
|
||||
@php
|
||||
$currentMovie->episodes
|
||||
->sortBy([['name', 'desc'], ['type', 'desc']])
|
||||
->sortByDesc('name', SORT_NATURAL)
|
||||
->unique('name')
|
||||
->take(3)
|
||||
->map(function ($episode) use ($currentMovie) {
|
||||
echo '<a href="' . $episode->getUrl() . '" title="'. $currentMovie->name .' tập '. $episode->name .'" class="w-content text-center bg-main-labelbgSecondary text-white hover:shadow-menu hover:bg-main-primary duration-150 px-4 py-1 overflow-hidden overflow-ellipsis whitespace-nowrap">' . $episode->name . '</a>';
|
||||
});
|
||||
@endphp
|
||||
</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<article class="block bg-main-800/40 p-2 mt-2">
|
||||
<h3 class="font-medium text-xl pb-2">Nội dung phim</h3>
|
||||
<div class="border-b-[1px] border-main-secondary border-opacity-25 py-1">
|
||||
@if ($currentMovie->content)
|
||||
<div class="whitespace-pre-wrap">{!! $currentMovie->content !!}</div>
|
||||
@else
|
||||
<p>Đang cập nhật ...</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-medium text-xl py-2">Tags</h3>
|
||||
<ul class="flex flex-wrap gap-1 py-1">
|
||||
@foreach ($currentMovie->tags as $tag)
|
||||
<li class="bg-main-labelbgPrimary text-white hover:text-main-warning px-2">
|
||||
<a href="{{ $tag->getUrl() }}" title="{{ $tag->name }}">{{ $tag->name }}</a>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<div class="bg-white mt-2 w-full" style="background-color: #ffffff">
|
||||
<div class="fb-comments w-full" data-href="{{ $currentMovie->getUrl() }}" data-width="100%"
|
||||
data-numposts="5" data-colorscheme="light" data-lazy="true">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="w-full mt-2">
|
||||
<div class="flex justify-between">
|
||||
<div class="py-2 w-max">
|
||||
<h3
|
||||
class="text-base md:text-2xl uppercase font-semibold text-transparent bg-clip-text bg-gradient-to-r from-[#7367F0] to-[#8e84fc]">
|
||||
Có thể bạn muốn xem</h3>
|
||||
</div>
|
||||
</div>
|
||||
@php
|
||||
$item['data'] = $movie_related;
|
||||
@endphp
|
||||
@include('themes::september.inc.section.section_thumb')
|
||||
</section>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script type="text/javascript">
|
||||
$("#toggleModal-trailer").click(() => {
|
||||
$("#modal-trailer").toggle("hidden");
|
||||
})
|
||||
$('body').click(function(event) {
|
||||
if (($(event.target).closest("#modal-trailer").length && !$(event.target).closest(
|
||||
"#modal-trailer-iframe").length) || $(event.target).closest("#close-trailer-modal").length) {
|
||||
$("#modal-trailer").toggle("hidden");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="/themes/september/plugins/jquery-raty/jquery.raty.js"></script>
|
||||
<link href="/themes/september/plugins/jquery-raty/jquery.raty.css" rel="stylesheet" type="text/css" />
|
||||
|
||||
<script>
|
||||
var rated = false;
|
||||
$('#movies-rating-star').raty({
|
||||
score: {{ $currentMovie->getRatingStar() }},
|
||||
number: 10,
|
||||
numberMax: 10,
|
||||
hints: ['quá tệ', 'tệ', 'không hay', 'không hay lắm', 'bình thường', 'xem được', 'có vẻ hay', 'hay',
|
||||
'rất hay', 'siêu phẩm'
|
||||
],
|
||||
starOff: '/themes/september/plugins/jquery-raty/images/star-off.png',
|
||||
starOn: '/themes/september/plugins/jquery-raty/images/star-on.png',
|
||||
starHalf: '/themes/september/plugins/jquery-raty/images/star-half.png',
|
||||
click: function(score, evt) {
|
||||
if (rated) return
|
||||
fetch("{{ route('movie.rating', ['movie' => $currentMovie->slug]) }}", {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'X-CSRF-TOKEN': document.querySelector(
|
||||
'meta[name="csrf-token"]')
|
||||
.getAttribute(
|
||||
'content')
|
||||
},
|
||||
body: JSON.stringify({
|
||||
rating: score
|
||||
})
|
||||
});
|
||||
rated = true;
|
||||
$('#movies-rating-star').data('raty').readOnly(true);
|
||||
$('#movies-rating-msg').html(`Bạn đã đánh giá ${score} sao cho phim này!`);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{!! setting('site_scripts_facebook_sdk') !!}
|
||||
@endpush
|
||||
54
routes/web.php
Executable file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use VsMov\September\Controllers\SeptemberController;
|
||||
|
||||
// --------------------------
|
||||
// Custom Backpack Routes
|
||||
// --------------------------
|
||||
// This route file is loaded automatically by Backpack\Base.
|
||||
// Routes you generate using Backpack\Generators will be placed here.
|
||||
|
||||
Route::group([
|
||||
'middleware' => array_merge(
|
||||
(array) config('backpack.base.web_middleware', 'web'),
|
||||
),
|
||||
], function () {
|
||||
Route::get('/', [SeptemberController::class, 'index']);
|
||||
|
||||
Route::get(setting('site_routes_category', '/the-loai/{category}'), [SeptemberController::class, 'getMovieOfCategory'])
|
||||
->where(['category' => '.+', 'id' => '[0-9]+'])
|
||||
->name('categories.movies.index');
|
||||
|
||||
Route::get(setting('site_routes_region', '/quoc-gia/{region}'), [SeptemberController::class, 'getMovieOfRegion'])
|
||||
->where(['region' => '.+', 'id' => '[0-9]+'])
|
||||
->name('regions.movies.index');
|
||||
|
||||
Route::get(setting('site_routes_tag', '/tu-khoa/{tag}'), [SeptemberController::class, 'getMovieOfTag'])
|
||||
->where(['tag' => '.+', 'id' => '[0-9]+'])
|
||||
->name('tags.movies.index');
|
||||
|
||||
Route::get(setting('site_routes_types', '/danh-sach/{type}'), [SeptemberController::class, 'getMovieOfType'])
|
||||
->where(['type' => '.+', 'id' => '[0-9]+'])
|
||||
->name('types.movies.index');
|
||||
|
||||
Route::get(setting('site_routes_actors', '/dien-vien/{actor}'), [SeptemberController::class, 'getMovieOfActor'])
|
||||
->where(['actor' => '.+', 'id' => '[0-9]+'])
|
||||
->name('actors.movies.index');
|
||||
|
||||
Route::get(setting('site_routes_directors', '/dao-dien/{director}'), [SeptemberController::class, 'getMovieOfDirector'])
|
||||
->where(['director' => '.+', 'id' => '[0-9]+'])
|
||||
->name('directors.movies.index');
|
||||
|
||||
Route::get(setting('site_routes_episode', '/phim/{movie}/{episode}-{id}'), [SeptemberController::class, 'getEpisode'])
|
||||
->where(['movie' => '.+', 'movie_id' => '[0-9]+', 'episode' => '.+', 'id' => '[0-9]+'])
|
||||
->name('episodes.show');
|
||||
|
||||
Route::post(sprintf('/%s/{movie}/{episode}-{id}/report', config('vsmov.routes.movie', 'phim')), [SeptemberController::class, 'reportEpisode'])
|
||||
->where(['movie' => '.+', 'episode' => '.+', 'id' => '[0-9]+'])->name('episodes.report');
|
||||
Route::post(sprintf('/%s/{movie}/rate', config('vsmov.routes.movie', 'phim')), [SeptemberController::class, 'rateMovie'])->name('movie.rating');
|
||||
|
||||
Route::get(setting('site_routes_movie', '/phim/{movie}'), [SeptemberController::class, 'getMovieOverview'])
|
||||
->where(['movie' => '.+', 'id' => '[0-9]+'])
|
||||
->name('movies.show');
|
||||
});
|
||||
289
src/Controllers/SeptemberController.php
Executable file
@@ -0,0 +1,289 @@
|
||||
<?php
|
||||
|
||||
namespace VsMov\September\Controllers;
|
||||
|
||||
use Backpack\Settings\app\Models\Setting;
|
||||
use Illuminate\Http\Request;
|
||||
use VsMov\Core\Models\Actor;
|
||||
use VsMov\Core\Models\Catalog;
|
||||
use VsMov\Core\Models\Category;
|
||||
use VsMov\Core\Models\Director;
|
||||
use VsMov\Core\Models\Episode;
|
||||
use VsMov\Core\Models\Movie;
|
||||
use VsMov\Core\Models\Region;
|
||||
use VsMov\Core\Models\Tag;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class SeptemberController
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
if ($request['search'] || $request['filter']) {
|
||||
$data = Movie::when(!empty($request['filter']['category']), function ($movie) {
|
||||
$movie->whereHas('categories', function ($categories) {
|
||||
$categories->where('id', request('filter')['category']);
|
||||
});
|
||||
})->when(!empty($request['filter']['region']), function ($movie) {
|
||||
$movie->whereHas('regions', function ($regions) {
|
||||
$regions->where('id', request('filter')['region']);
|
||||
});
|
||||
})->when(!empty($request['filter']['year']), function ($movie) {
|
||||
$movie->where('publish_year', request('filter')['year']);
|
||||
})->when(!empty($request['filter']['type']), function ($movie) {
|
||||
$movie->where('type', request('filter')['type']);
|
||||
})->when(!empty($request['search']), function ($query, $movie) {
|
||||
$query->where(function ($query) {
|
||||
$query->where('name', 'like', '%' . request('search') . '%')
|
||||
->orWhere('origin_name', 'like', '%' . request('search') . '%');
|
||||
})->orderBy('name', 'desc');
|
||||
})->when(!empty($request['filter']['sort']), function ($movie) {
|
||||
if (request('filter')['sort'] == 'create') {
|
||||
return $movie->orderBy('created_at', 'desc');
|
||||
}
|
||||
if (request('filter')['sort'] == 'update') {
|
||||
return $movie->orderBy('updated_at', 'desc');
|
||||
}
|
||||
if (request('filter')['sort'] == 'year') {
|
||||
return $movie->orderBy('publish_year', 'desc');
|
||||
}
|
||||
if (request('filter')['sort'] == 'view') {
|
||||
return $movie->orderBy('view_total', 'desc');
|
||||
}
|
||||
})->paginate(get_theme_option('per_page_limit'));
|
||||
|
||||
return view('themes::september.catalog', [
|
||||
'data' => $data,
|
||||
'search' => $request['search'],
|
||||
'section_name' => "Tìm kiếm phim: $request->search"
|
||||
]);
|
||||
}
|
||||
return view('themes::september.index', [
|
||||
'title' => Setting::get('site_homepage_title')
|
||||
]);
|
||||
}
|
||||
|
||||
public function getMovieOverview(Request $request)
|
||||
{
|
||||
/** @var Movie */
|
||||
$movie = Movie::fromCache()->find($request->movie ?: $request->id);
|
||||
|
||||
if (is_null($movie)) abort(404);
|
||||
|
||||
$movie->generateSeoTags();
|
||||
|
||||
$movie->increment('view_total', 1);
|
||||
$movie->increment('view_day', 1);
|
||||
$movie->increment('view_week', 1);
|
||||
$movie->increment('view_month', 1);
|
||||
|
||||
$movie_related_cache_key = 'movie_related.' . $movie->id;
|
||||
$movie_related = Cache::get($movie_related_cache_key);
|
||||
if(is_null($movie_related)) {
|
||||
$movie_related = $movie->categories[0]->movies()->inRandomOrder()->limit(get_theme_option('movie_related_limit'))->get();
|
||||
Cache::put($movie_related_cache_key, $movie_related, setting('site_cache_ttl', 5 * 60));
|
||||
}
|
||||
|
||||
return view('themes::september.single', [
|
||||
'currentMovie' => $movie,
|
||||
'title' => $movie->getTitle(),
|
||||
'movie_related' => $movie_related
|
||||
]);
|
||||
}
|
||||
|
||||
public function getEpisode(Request $request)
|
||||
{
|
||||
$movie = Movie::fromCache()->find($request->movie ?: $request->movie_id)->load('episodes');
|
||||
|
||||
if (is_null($movie)) abort(404);
|
||||
|
||||
/** @var Episode */
|
||||
$episode_id = $request->id;
|
||||
$episode = $movie->episodes->when($episode_id, function ($collection, $episode_id) {
|
||||
return $collection->where('id', $episode_id);
|
||||
})->firstWhere('slug', $request->episode);
|
||||
|
||||
if (is_null($episode)) abort(404);
|
||||
|
||||
$episode->generateSeoTags();
|
||||
|
||||
$movie->increment('view_total', 1);
|
||||
$movie->increment('view_day', 1);
|
||||
$movie->increment('view_week', 1);
|
||||
$movie->increment('view_month', 1);
|
||||
|
||||
$movie_related_cache_key = 'movie_related.' . $movie->id;
|
||||
$movie_related = Cache::get($movie_related_cache_key);
|
||||
if(is_null($movie_related)) {
|
||||
$movie_related = $movie->categories[0]->movies()->inRandomOrder()->limit(get_theme_option('movie_related_limit'))->get();
|
||||
Cache::put($movie_related_cache_key, $movie_related, setting('site_cache_ttl', 5 * 60));
|
||||
}
|
||||
|
||||
return view('themes::september.episode', [
|
||||
'currentMovie' => $movie,
|
||||
'movie_related' => $movie_related,
|
||||
'episode' => $episode,
|
||||
'title' => $episode->getTitle()
|
||||
]);
|
||||
}
|
||||
|
||||
public function reportEpisode(Request $request, $movie, $slug, $id)
|
||||
{
|
||||
$movie = Movie::fromCache()->find($movie)->load('episodes');
|
||||
|
||||
$episode = $movie->episodes->when($id, function ($collection, $id) {
|
||||
return $collection->where('id', $id);
|
||||
})->firstWhere('slug', $slug);
|
||||
|
||||
$episode->update([
|
||||
'report_message' => request('message', ''),
|
||||
'has_report' => true
|
||||
]);
|
||||
|
||||
return response([], 204);
|
||||
}
|
||||
|
||||
public function rateMovie(Request $request, $movie)
|
||||
{
|
||||
|
||||
$movie = Movie::fromCache()->find($movie);
|
||||
|
||||
$movie->refresh()->increment('rating_count', 1, [
|
||||
'rating_star' => $movie->rating_star + ((int) request('rating') - $movie->rating_star) / ($movie->rating_count + 1)
|
||||
]);
|
||||
|
||||
return response([], 204);
|
||||
}
|
||||
|
||||
public function getMovieOfCategory(Request $request)
|
||||
{
|
||||
/** @var Category */
|
||||
$category = Category::fromCache()->find($request->category ?: $request->id);
|
||||
|
||||
if (is_null($category)) abort(404);
|
||||
|
||||
$category->generateSeoTags();
|
||||
|
||||
$movies = $category->movies()->orderBy('created_at', 'desc')->paginate(get_theme_option('per_page_limit'));
|
||||
|
||||
return view('themes::september.catalog', [
|
||||
'data' => $movies,
|
||||
'category' => $category,
|
||||
'title' => $category->seo_title ?: $category->getTitle(),
|
||||
'section_name' => "Phim thể loại $category->name"
|
||||
]);
|
||||
}
|
||||
|
||||
public function getMovieOfRegion(Request $request)
|
||||
{
|
||||
/** @var Region */
|
||||
$region = Region::fromCache()->find($request->region ?: $request->id);
|
||||
|
||||
if (is_null($region)) abort(404);
|
||||
|
||||
$region->generateSeoTags();
|
||||
|
||||
$movies = $region->movies()->orderBy('created_at', 'desc')->paginate(get_theme_option('per_page_limit'));
|
||||
|
||||
return view('themes::september.catalog', [
|
||||
'data' => $movies,
|
||||
'region' => $region,
|
||||
'title' => $region->seo_title ?: $region->getTitle(),
|
||||
'section_name' => "Phim quốc gia $region->name"
|
||||
]);
|
||||
}
|
||||
|
||||
public function getMovieOfActor(Request $request)
|
||||
{
|
||||
/** @var Actor */
|
||||
$actor = Actor::fromCache()->find($request->actor ?: $request->id);
|
||||
|
||||
if (is_null($actor)) abort(404);
|
||||
|
||||
$actor->generateSeoTags();
|
||||
|
||||
$movies = $actor->movies()->orderBy('created_at', 'desc')->paginate(get_theme_option('per_page_limit'));
|
||||
|
||||
return view('themes::september.catalog', [
|
||||
'data' => $movies,
|
||||
'person' => $actor,
|
||||
'title' => $actor->getTitle(),
|
||||
'section_name' => "Diễn viên $actor->name"
|
||||
]);
|
||||
}
|
||||
|
||||
public function getMovieOfDirector(Request $request)
|
||||
{
|
||||
/** @var Director */
|
||||
$director = Director::fromCache()->find($request->director ?: $request->id);
|
||||
|
||||
if (is_null($director)) abort(404);
|
||||
|
||||
$director->generateSeoTags();
|
||||
|
||||
$movies = $director->movies()->orderBy('created_at', 'desc')->paginate(get_theme_option('per_page_limit'));
|
||||
|
||||
return view('themes::september.catalog', [
|
||||
'data' => $movies,
|
||||
'person' => $director,
|
||||
'title' => $director->getTitle(),
|
||||
'section_name' => "Đạo diễn $director->name"
|
||||
]);
|
||||
}
|
||||
|
||||
public function getMovieOfTag(Request $request)
|
||||
{
|
||||
/** @var Tag */
|
||||
$tag = Tag::fromCache()->find($request->tag ?: $request->id);
|
||||
|
||||
if (is_null($tag)) abort(404);
|
||||
|
||||
$tag->generateSeoTags();
|
||||
|
||||
$movies = $tag->movies()->orderBy('created_at', 'desc')->paginate(get_theme_option('per_page_limit'));
|
||||
return view('themes::september.catalog', [
|
||||
'data' => $movies,
|
||||
'tag' => $tag,
|
||||
'title' => $tag->getTitle(),
|
||||
'section_name' => "Tags: $tag->name"
|
||||
]);
|
||||
}
|
||||
|
||||
public function getMovieOfType(Request $request)
|
||||
{
|
||||
/** @var Catalog */
|
||||
$catalog = Catalog::fromCache()->find($request->type ?: $request->id);
|
||||
|
||||
if (is_null($catalog)) abort(404);
|
||||
|
||||
$catalog->generateSeoTags();
|
||||
|
||||
$cache_key = 'catalog.' . $catalog->id . '.page.' . $request['page'];
|
||||
$movies = Cache::get($cache_key);
|
||||
if(is_null($movies)) {
|
||||
$value = explode('|', trim($catalog->value));
|
||||
[$relation_config, $field, $val, $sortKey, $alg] = array_merge($value, ['', 'is_copyright', 0, 'created_at', 'desc']);
|
||||
$relation_config = explode(',', $relation_config);
|
||||
|
||||
[$relation_table, $relation_field, $relation_val] = array_merge($relation_config, ['', '', '']);
|
||||
try {
|
||||
$movies = \VsMov\Core\Models\Movie::when($relation_table, function ($query) use ($relation_table, $relation_field, $relation_val, $field, $val) {
|
||||
$query->whereHas($relation_table, function ($rel) use ($relation_field, $relation_val, $field, $val) {
|
||||
$rel->where($relation_field, $relation_val)->where(array_combine(explode(",", $field), explode(",", $val)));
|
||||
});
|
||||
})->when(!$relation_table, function ($query) use ($field, $val) {
|
||||
$query->where(array_combine(explode(",", $field), explode(",", $val)));
|
||||
})
|
||||
->orderBy($sortKey, $alg)
|
||||
->paginate($catalog->paginate);
|
||||
|
||||
Cache::put($cache_key, $movies, setting('site_cache_ttl', 5 * 60));
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
|
||||
return view('themes::september.catalog', [
|
||||
'data' => $movies,
|
||||
'section_name' => "Danh sách $catalog->name"
|
||||
]);
|
||||
}
|
||||
}
|
||||
190
src/SeptemberServiceProvider.php
Executable file
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
namespace VsMov\September;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
class SeptemberServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
$this->setupDefaultThemeCustomizer();
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
$this->loadViewsFrom(__DIR__ . '/../resources/views/', 'themes');
|
||||
|
||||
$this->publishes([
|
||||
__DIR__ . '/../resources/assets' => public_path('themes/september')
|
||||
], 'september-assets');
|
||||
}
|
||||
|
||||
protected function setupDefaultThemeCustomizer()
|
||||
{
|
||||
config(['themes' => array_merge(config('themes', []), [
|
||||
'september' => [
|
||||
'name' => 'September',
|
||||
'author' => 'vsmov@gmail.com',
|
||||
'package_name' => 'vsmov/vsmov-september',
|
||||
'publishes' => ['september-assets'],
|
||||
'preview_image' => '',
|
||||
'options' => [
|
||||
[
|
||||
'name' => 'recommendations_limit',
|
||||
'label' => 'Recommended movies limit',
|
||||
'type' => 'number',
|
||||
'value' => 10,
|
||||
'wrapperAttributes' => [
|
||||
'class' => 'form-group col-md-4',
|
||||
],
|
||||
'tab' => 'List'
|
||||
],
|
||||
[
|
||||
'name' => 'per_page_limit',
|
||||
'label' => 'Pages limit',
|
||||
'type' => 'number',
|
||||
'value' => 36,
|
||||
'wrapperAttributes' => [
|
||||
'class' => 'form-group col-md-4',
|
||||
],
|
||||
'tab' => 'List'
|
||||
],
|
||||
[
|
||||
'name' => 'movie_related_limit',
|
||||
'label' => 'Movies related limit',
|
||||
'type' => 'number',
|
||||
'value' => 12,
|
||||
'wrapperAttributes' => [
|
||||
'class' => 'form-group col-md-4',
|
||||
],
|
||||
'tab' => 'List'
|
||||
],
|
||||
[
|
||||
'name' => 'latest',
|
||||
'label' => 'Home Page',
|
||||
'type' => 'code',
|
||||
'hint' => 'display_label|relation|find_by_field|value|limit|show_more_url|show_template (slider_poster|slider_thumb|section_poster|section_thumb)',
|
||||
'value' => "Phim bộ mới||type|series|12|/danh-sach/phim-bo|section_thumb\r\nPhim lẻ mới||type|single|12|/danh-sach/phim-bo|section_poster\r\nPhim lẻ mới||type|single|10|/danh-sach/phim-bo|slider_thumb\r\nPhim lẻ mới||type|single|10|/danh-sach/phim-bo|slider_poster",
|
||||
'attributes' => [
|
||||
'rows' => 5
|
||||
],
|
||||
'tab' => 'List'
|
||||
],
|
||||
[
|
||||
'name' => 'hotest',
|
||||
'label' => 'Danh sách hot',
|
||||
'type' => 'code',
|
||||
'hint' => 'Label|relation|find_by_field|value|sort_by_field|sort_algo|limit|show_template (top_text|top_thumb)',
|
||||
'value' => "Top phim lẻ||type|single|view_total|desc|9|top_text\r\nTop phim bộ||type|series|view_total|desc|9|top_thumb",
|
||||
'attributes' => [
|
||||
'rows' => 5
|
||||
],
|
||||
'tab' => 'List'
|
||||
],
|
||||
[
|
||||
'name' => 'additional_css',
|
||||
'label' => 'Additional CSS',
|
||||
'type' => 'code',
|
||||
'value' => "",
|
||||
'tab' => 'Custom CSS'
|
||||
],
|
||||
[
|
||||
'name' => 'body_attributes',
|
||||
'label' => 'Body attributes',
|
||||
'type' => 'text',
|
||||
'value' => "class='bg-main-900 text-gray-300 font-montserrat leading-normal tracking-normal'",
|
||||
'tab' => 'Custom CSS'
|
||||
],
|
||||
[
|
||||
'name' => 'additional_header_js',
|
||||
'label' => 'Header JS',
|
||||
'type' => 'code',
|
||||
'value' => "",
|
||||
'tab' => 'Custom JS'
|
||||
],
|
||||
[
|
||||
'name' => 'additional_body_js',
|
||||
'label' => 'Body JS',
|
||||
'type' => 'code',
|
||||
'value' => "",
|
||||
'tab' => 'Custom JS'
|
||||
],
|
||||
[
|
||||
'name' => 'additional_footer_js',
|
||||
'label' => 'Footer JS',
|
||||
'type' => 'code',
|
||||
'value' => "",
|
||||
'tab' => 'Custom JS'
|
||||
],
|
||||
[
|
||||
'name' => 'footer',
|
||||
'label' => 'Footer',
|
||||
'type' => 'code',
|
||||
'value' => <<<EOT
|
||||
<div class="w-full mx-auto flex flex-wrap">
|
||||
<div class="flex w-full lg:w-3/5">
|
||||
<div class="px-2">
|
||||
<span class="font-bold text-gray-100">Giới Thiệu</span>
|
||||
<p class="text-gray-300 text-sm pt-3">
|
||||
<b>VsMov</b> - Website xem phim online miễn phí.
|
||||
</p>
|
||||
<p class="text-gray-300 text-sm">Với giao diện trực quan, thuận tiện, tốc độ tải nhanh, ít quảng cáo
|
||||
hứa
|
||||
hẹn sẽ đem lại những trải nghiệm tốt cho người xem.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-1/3 lg:w-1/5">
|
||||
<div class="px-2">
|
||||
<span class="font-bold text-gray-100">Kết Nối</span>
|
||||
<ul class="list-reset items-center text-sm pt-3">
|
||||
<li>
|
||||
<a class="inline-block text-gray-50 no-underline hover:text-main-primary hover:text-underline py-1"
|
||||
href="#">
|
||||
FanPage
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="inline-block text-gray-50 no-underline hover:text-main-primary hover:text-underline py-1"
|
||||
href="#">
|
||||
Group
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-2/3 lg:w-1/5">
|
||||
<div class="px-2">
|
||||
<span class="font-bold text-gray-100">Liên hệ</span>
|
||||
<ul class="list-reset items-center text-sm pt-3">
|
||||
<li class="text-gray-300">email@gmail.com</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
EOT,
|
||||
'tab' => 'Custom HTML'
|
||||
],
|
||||
[
|
||||
'name' => 'ads_header',
|
||||
'label' => 'Ads header',
|
||||
'type' => 'code',
|
||||
'value' => <<<EOT
|
||||
<img src="" alt="">
|
||||
EOT,
|
||||
'tab' => 'Ads'
|
||||
],
|
||||
[
|
||||
'name' => 'ads_catfish',
|
||||
'label' => 'Ads catfish',
|
||||
'type' => 'code',
|
||||
'value' => <<<EOT
|
||||
<img src="" alt="">
|
||||
EOT,
|
||||
'tab' => 'Ads'
|
||||
]
|
||||
],
|
||||
]
|
||||
])]);
|
||||
}
|
||||
}
|
||||
BIN
src/screenshot.png
Executable file
|
After Width: | Height: | Size: 615 KiB |