mirror of
https://github.com/claradex/nativegallery.git
synced 2024-11-15 03:31:10 +03:00
Vehicles Search + Vehicles Pin + Faces Recognition
сегодня день обновлений
This commit is contained in:
parent
5d9e930d30
commit
42730bac08
14 changed files with 1038 additions and 54 deletions
|
@ -1,11 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Controllers\Api\Images\Comments;
|
namespace App\Controllers\Api\Vehicles;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
use App\Services\{Auth, Router, GenerateRandomStr, DB, Json, EXIF};
|
use App\Services\{Auth, Router, GenerateRandomStr, DB, Json, EXIF};
|
||||||
use App\Models\{User, Vote, Comment};
|
use App\Models\{User, Vote, Comment, Vehicle};
|
||||||
|
|
||||||
|
|
||||||
class Load
|
class Load
|
||||||
|
@ -13,7 +13,36 @@ class Load
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
|
||||||
$comments = DB::query('SELECT * FROM entities_data WHERE photo_id=:pid', array(':pid' => explode('/', $_SERVER['REQUEST_URI'])[4]));
|
$entities_data = DB::query('SELECT * FROM entities_data WHERE (LOWER(title) LIKE :value) OR (LOWER(id) LIKE :value) AND entityid=:pid', array(':pid' => $_GET['type'], ':value'=>'%'.$_GET['num'].'%'));
|
||||||
|
echo '<table>';
|
||||||
|
foreach ($entities_data as $e) {
|
||||||
|
$vehicle = new Vehicle($e['entityid']);
|
||||||
|
echo '<tbody class="found_vehicle s11" data-state="1" data-vid="'.$e['id'].'" data-cid="2" data-type="3" data-twoside="0">
|
||||||
|
<tr>
|
||||||
|
<th width="100">ID</th>
|
||||||
|
<th width="90%">Название</th>
|
||||||
|
<th>Примечание</th>
|
||||||
|
<th class="c nw">Тип</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding:10px"><a href="/vehicle/'.$e['id'].'" target="_blank" class="num pcnt">'.$e['id'].'</a>
|
||||||
|
</td>
|
||||||
|
<td style="padding:10px; font-size:16px" class="mname">hhhhh</td>
|
||||||
|
<td style="padding:10px" class="d">
|
||||||
|
'.$e['comment'].'
|
||||||
|
</td>
|
||||||
|
<td style="padding:10px" class="d">
|
||||||
|
'.$vehicle->i('title').'
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</tbody>';
|
||||||
|
}
|
||||||
|
echo '</table>';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ use \App\Controllers\Api\Images\Comments\Edit as PhotoCommentEdit;
|
||||||
use \App\Controllers\Api\Images\Comments\Delete as PhotoCommentDelete;
|
use \App\Controllers\Api\Images\Comments\Delete as PhotoCommentDelete;
|
||||||
use \App\Controllers\Api\Images\Comments\Load as PhotoCommentLoad;
|
use \App\Controllers\Api\Images\Comments\Load as PhotoCommentLoad;
|
||||||
use \App\Controllers\Api\Images\Comments\Rate as PhotoCommentVote;
|
use \App\Controllers\Api\Images\Comments\Rate as PhotoCommentVote;
|
||||||
|
use \App\Controllers\Api\Vehicles\Load as VehiclesLoad;
|
||||||
use \App\Controllers\Api\Profile\Update as ProfileUpdate;
|
use \App\Controllers\Api\Profile\Update as ProfileUpdate;
|
||||||
use \App\Controllers\Api\Users\LoadUser as UserLoad;
|
use \App\Controllers\Api\Users\LoadUser as UserLoad;
|
||||||
use \App\Controllers\Api\Admin\Images\SetVisibility as AdminPhotoSetVisibility;
|
use \App\Controllers\Api\Admin\Images\SetVisibility as AdminPhotoSetVisibility;
|
||||||
|
@ -91,6 +92,9 @@ class ApiController
|
||||||
public static function admingetvehicleinputs() {
|
public static function admingetvehicleinputs() {
|
||||||
return new AdminGetVehicleInputs();
|
return new AdminGetVehicleInputs();
|
||||||
}
|
}
|
||||||
|
public static function vehiclesload() {
|
||||||
|
return new VehiclesLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -61,6 +61,7 @@ class Routes
|
||||||
Router::get('/api/photo/comment/rate', 'ApiController@photocommentvote');
|
Router::get('/api/photo/comment/rate', 'ApiController@photocommentvote');
|
||||||
Router::post('/api/photo/comment/$id/edit', 'ApiController@photocommentedit');
|
Router::post('/api/photo/comment/$id/edit', 'ApiController@photocommentedit');
|
||||||
Router::post('/api/photo/comment/$id/delete', 'ApiController@photocommentdelete');
|
Router::post('/api/photo/comment/$id/delete', 'ApiController@photocommentdelete');
|
||||||
|
Router::get('/api/vehicles/load', 'ApiController@vehiclesload');
|
||||||
if ($user->i('admin') > 0) {
|
if ($user->i('admin') > 0) {
|
||||||
Router::any('/admin', 'AdminController@index');
|
Router::any('/admin', 'AdminController@index');
|
||||||
Router::any('/api/admin/images/setvisibility', 'ApiController@adminsetvis');
|
Router::any('/api/admin/images/setvisibility', 'ApiController@adminsetvis');
|
||||||
|
|
|
@ -13,7 +13,6 @@ class Vote
|
||||||
if ($type < 0) {
|
if ($type < 0) {
|
||||||
$type = -1;
|
$type = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $type;
|
return $type;
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -98,6 +98,36 @@ a:hover {
|
||||||
background-color:var(--theme-link-hover-bg-color);
|
background-color:var(--theme-link-hover-bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:checked[type=checkbox] {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e") !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked {
|
||||||
|
background-color: #292929 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
input[type=checkbox]{
|
||||||
|
border-radius: 2px;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
margin-top: .25em;
|
||||||
|
vertical-align: top;
|
||||||
|
background-color: #fff;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: contain;
|
||||||
|
border: 1px solid rgba(0, 0, 0, .25);
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
a.und {
|
a.und {
|
||||||
border-bottom:solid 1px var(--theme-link-underline-color);
|
border-bottom:solid 1px var(--theme-link-underline-color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,86 @@
|
||||||
var gal_cid = -1;
|
var gal_cid = -1;
|
||||||
var new_vehicle_idx = 0;
|
var new_vehicle_idx = 0;
|
||||||
var modified = false;
|
var modified = false;
|
||||||
|
var cnames = {2: 'Санкт-Петербург'};
|
||||||
|
var binds = [
|
||||||
|
{ value: 1, item: '<b>Основная — ТС на переднем плане</b>', label: '<b>Основная</b>' },
|
||||||
|
{ value: 0, item: 'Второстепенная — ТС на заднем плане', label: 'Второстепенная' },
|
||||||
|
{ value: 2, item: '<i>Условная — ТС указано предположительно</i>', label: '<i>Условная</i>' }
|
||||||
|
];
|
||||||
|
|
||||||
|
addTexts({
|
||||||
|
'UP_WRONGTYPE': 'Недопустимый тип файла',
|
||||||
|
'UP_TOOSMALL': 'Выбранное изображение слишком маленькое — его длина по широкой стороне составляет %d пикселей. Для загрузки на сайт она должна быть не менее %d пикселей',
|
||||||
|
'UP_OVERSIZE_JPG': 'Выбранное изображение слишком большое — сумма его ширины и высоты составляет %d пикселей. Для изображений JPEG и WEBP она не должна превышать %d пикселей',
|
||||||
|
'UP_OVERSIZE_PNG': 'Выбранное изображение слишком большое — его длина по широкой стороне составляет %d пикселей. Для изображений GIF и PNG она не должна превышать %d пикселей',
|
||||||
|
'UP_LARGEFILE_JPG': 'Этот файл слишком большой — %d КБ. Вы можете загружать файлы JPEG и WEBP объёмом до %d КБ',
|
||||||
|
'UP_LARGEFILE_PNG': 'Этот файл слишком большой — %d МБ. Вы можете загружать файлы GIF и PNG объёмом до %d МБ',
|
||||||
|
'UP_NEEDRESIZE': 'Выбранное фото превышает %d пикселей по сумме ширины и высоты,<br />поэтому оно будет уменьшено до %d пикселей по широкой стороне',
|
||||||
|
'UP_NULART': '(подходящей галереи нет в списке, требуется создать новую)',
|
||||||
|
'UP_LOADING': 'Загрузка...',
|
||||||
|
'UP_SEARCHING': 'Идёт поиск...',
|
||||||
|
'UP_OTHER': 'Новая',
|
||||||
|
'UP_NOFILE': 'Не выбран файл для загрузки',
|
||||||
|
'UP_NOCOORDS': 'Поместите маркер на карте в точку, с которой вы производили съёмку. Для установки маркера достаточно кликнуть по карте в нужном месте.',
|
||||||
|
'UP_NODIR': 'Укажите направление съёмки.',
|
||||||
|
'UP_NOCITY': 'Вы не указали город. Нажмите кнопку подтверждения для отправки фотографии, если города съемки нет в списке. Нажмите кнопку отмены, если Вы забыли указать город.',
|
||||||
|
'UP_OTHERCITY': 'Город съёмки не соответствует привязанным ТС и/или галереям. Продолжить отправку фото?',
|
||||||
|
'UP_ERROR': 'Фото не было загружено :-(',
|
||||||
|
'UP_SUCCESS': 'Фотография успешно загружена!',
|
||||||
|
'UP_NOTHING': 'К сожалению, ничего подходящего найти не удалось',
|
||||||
|
'UP_V_LINKED': 'Это ТС уже привязано к данному фото',
|
||||||
|
'UP_G_LINKED': 'Эта галерея уже привязана к данному фото',
|
||||||
|
'UP_CREATIVE': 'Фотография, загружаемая в «Фотозарисовки» или «Художественную галерею», не может быть привязана к ТС и другим галереям.',
|
||||||
|
'UP_NOLINKS': 'Фотография ни к чему не привязана',
|
||||||
|
'UP_NO_PRI': 'Фотография не может иметь только второстепенные привязки.',
|
||||||
|
'UP_NODATE': 'Вы не указали дату снимка. Нажмите «OK», если так и должно быть, или «Отмена», если хотите добавить дату съёмки',
|
||||||
|
'UP_NOPLACE': 'Вы не указали место съёмки. Нажмите «OK», если так и должно быть, или «Отмена», если хотите добавить место съёмки',
|
||||||
|
'UP_ARTICLE': 'Галерея',
|
||||||
|
'UP_LIMITEXC': 'Сегодня Вы уже загрузили максимально возможное число фотографий. Следующие фотографии Вы можете загрузить завтра',
|
||||||
|
'UP_ROUTE': 'Маршрут',
|
||||||
|
'UP_NOTES': 'примечание',
|
||||||
|
'VIEW': 'Ракурс',
|
||||||
|
'UP_NOVIEW': 'Не для всех ТС указан ракурс съёмки.',
|
||||||
|
'UP_TOQUEUE': 'Это фото не может быть опубликовано без модерации, поэтому оно было помещено в очередь',
|
||||||
|
'UP_BIND': 'Привязка',
|
||||||
|
'UP_BIND_PRI': 'Основная — ТС на переднем плане',
|
||||||
|
'UP_BIND_SEC': 'Второстепенная — ТС на заднем плане',
|
||||||
|
'UP_BIND_CON': 'Условная — ТС указано предположительно',
|
||||||
|
'UP_NAA_ALLOW_NO': 'Не указано разрешение на публикацию.',
|
||||||
|
'UP_TWOSIDE': 'Вы выбрали тип ПС (двухсторонний/односторонний), не совпадающий с указанным для данной модели. Так и должно быть?',
|
||||||
|
'MAP_SEARCH': 'Адрес или объект...',
|
||||||
|
'MAP_NOTFOUND': 'На карте не удалось найти указанное место.',
|
||||||
|
'MAP_OSM': 'Карта OpenStreetMap',
|
||||||
|
'MAP_OSM_BW': 'Чёрно-белая карта OpenStreetMap',
|
||||||
|
'MAP_OSM_HOT': 'Карта Humanitarian OpenStreetMap Team',
|
||||||
|
'MAP_TOPO': 'Карта OpenTopoMap',
|
||||||
|
'MAP_WIKIMEDIA': 'Карта Wikimedia',
|
||||||
|
'MAP_OPNV': 'Карта ÖPNVKarte',
|
||||||
|
'MAP_OPENPTMAP': 'Общественный транспорт от OpenPtMap',
|
||||||
|
'MAP_RAILWAY': 'Железная дорога от OpenRailwayMap',
|
||||||
|
'MAP_BING': 'Спутник Bing',
|
||||||
|
'MAP_YANDEX': 'Карта Яндекс',
|
||||||
|
'MAP_YANDSAT': 'Спутник Яндекс'
|
||||||
|
});
|
||||||
|
|
||||||
|
var views = {
|
||||||
|
0: '<span class="s5"> Не указан </span>',
|
||||||
|
1: 'Спереди-справа (двери)',
|
||||||
|
2: 'Спереди-слева (окна)',
|
||||||
|
3: 'Сзади-справа (двери)',
|
||||||
|
4: 'Сзади-слева (окна)',
|
||||||
|
5: 'Вид строго спереди',
|
||||||
|
6: 'Правый борт',
|
||||||
|
7: 'Вид строго сзади',
|
||||||
|
8: 'Левый борт',
|
||||||
|
9: 'Салон, вид вперёд',
|
||||||
|
10: 'Салон, вид назад',
|
||||||
|
11: 'Кабина',
|
||||||
|
12: 'Заводская табличка',
|
||||||
|
13: 'Отдельные элементы ТС',
|
||||||
|
14: 'Не определяется (двухстороннее ТС)',
|
||||||
|
20: 'Вид сверху',
|
||||||
|
40: 'Вид снизу'};
|
||||||
|
|
||||||
$(document).ready(function()
|
$(document).ready(function()
|
||||||
{
|
{
|
||||||
|
@ -42,29 +121,21 @@ $(document).ready(function()
|
||||||
var html = '<tbody data-nid="' + nid + '" data-vid="' + vid + '" data-twoside="' + $(this).data('twoside') + '" class="s' + $(this).data('state') + '">\n';
|
var html = '<tbody data-nid="' + nid + '" data-vid="' + vid + '" data-twoside="' + $(this).data('twoside') + '" class="s' + $(this).data('state') + '">\n';
|
||||||
|
|
||||||
html += '<tr>\n';
|
html += '<tr>\n';
|
||||||
html += '<td style="padding:3px 10px 5px"><input type="hidden" name="nids[]" value="' + nid + '"><input type="hidden" name="cids[]" value="' + cid + '"><a href="' + (nid > 0 ? '/vehicle/' + vid + '/#n' + nid : '/lk/vehicles.php?action=edit&vid=' + (-nid)) + '" target="_blank" class="num pcnt">' + $('.num', this).html() + '</a></td>\n';
|
html += '<td style="padding:3px 10px 5px"><input type="hidden" name="nids[]" value="' + nid + '"><input type="hidden" name="cids[]" value="' + cid + '"><a href="' + (nid > 0 ? '/vehicle/' + vid : '/lk/vehicles.php?action=edit&vid=' + (-nid)) + '" target="_blank" class="num pcnt">' + $('.num', this).html() + '</a></td>\n';
|
||||||
html += '<td style="padding:3px 10px 6px">' + $('.mname', this).html() + '</td>\n';
|
html += '<td style="padding:3px 10px 6px">' + $('.mname', this).html() + '</td>\n';
|
||||||
html += '<td style="padding:3px 0 6px 10px; color:#777" class="r">' + _text['UP_ROUTE'] + ':</td>\n';
|
html += '<td style="padding:3px 0 6px 10px; color:#777" class="r">' + _text['UP_ROUTE'] + ':</td>\n';
|
||||||
html += '<td style="padding:3px 7px" class="nw"><input type="text" class="route" name="route[' + nid + ']" style="width:40px; font-weight:bold; text-align:center" maxlength="7" value="">, <input type="text" class="notes" name="notes[' + nid + ']" style="width:170px" maxlength="100" value="" placeholder="' + _text['UP_NOTES'] + '"></td>\n';
|
html += '<td style="padding:3px 7px" class="nw"><input type="text" class="route" name="route[' + nid + ']" style="width:40px; font-weight:bold; text-align:center" maxlength="7" value="">, <input type="text" class="notes" name="notes[' + nid + ']" style="width:170px" maxlength="100" value="" placeholder="' + _text['UP_NOTES'] + '"></td>\n';
|
||||||
html += '<td class="r"><a href="#" class="delLink" style="font-size:16px">×</a></td>\n';
|
html += '<td class="r"><a href="#" class="delLink" style="font-size:16px">×</a></td>\n';
|
||||||
html += '</tr>\n';
|
html += '</tr>\n';
|
||||||
|
|
||||||
html += '<tr>\n';
|
|
||||||
html += '<td style="padding:0 12px 7px" colspan="2"><a href="/city/' + cid + '/" target="_blank">' + cname + '</a></td>\n';
|
|
||||||
html += '<td style="padding:0 0 7px; color:#777" class="r">' + _text['VIEW'] + ':</td>\n';
|
|
||||||
html += '<td style="padding:0 7px 7px" colspan="2"><input type="hidden" class="view" name="view[' + nid + ']" value="0"><a href="#" class="view_link dot">' + views[0] + '</a></td>\n';
|
|
||||||
html += '</tr>\n';
|
|
||||||
|
|
||||||
html += '<tr>\n';
|
|
||||||
html += '<td colspan="2"></td>\n';
|
|
||||||
html += '<td style="padding:0 0 7px; color:#777" class="r">' + _text['UP_BIND'] + ':</td>\n';
|
|
||||||
html += '<td style="padding:0 7px 7px" colspan="2"><input type="hidden" name="pri[' + nid + ']" class="pri-value" value="1"><a class="pri-label dot" href="#">' + binds[0].label + '</a></td>\n';
|
|
||||||
html += '</tr>\n';
|
|
||||||
|
|
||||||
html += '</tbody>\n';
|
html += '</tbody>\n';
|
||||||
|
|
||||||
var row = $(html);
|
var row = $(html);
|
||||||
$('#conn_veh').append(row).show().tablesort('recountRows');
|
$('#conn_veh').append(row).show();
|
||||||
$('.pri-label', row).selector2(binds);
|
$('.pri-label', row).selector2(binds);
|
||||||
|
|
||||||
$('.no-links').hide();
|
$('.no-links').hide();
|
||||||
|
@ -84,6 +155,11 @@ $(document).ready(function()
|
||||||
|
|
||||||
setTimeout(function() { $('#conn_veh tbody[data-nid="' + nid + '"] .view_link').click(); }, 100);
|
setTimeout(function() { $('#conn_veh tbody[data-nid="' + nid + '"] .view_link').click(); }, 100);
|
||||||
});
|
});
|
||||||
|
$('#vlist').on('mouseenter mouseleave', '#add_new_vehicle', function()
|
||||||
|
{
|
||||||
|
var state = parseInt($(this).data('state'));
|
||||||
|
$(this).toggleClass('s' + state + ' s' + (state+10));
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -225,12 +301,12 @@ function searchVehicles(by_gos)
|
||||||
$('#search_cid, #search_type, #search_num, #search_gos').prop('disabled', true);
|
$('#search_cid, #search_type, #search_num, #search_gos').prop('disabled', true);
|
||||||
$('#vlist').html('<div class="nw" style="padding:6px 10px">' + _text['UP_SEARCHING'] + '</div>').show();
|
$('#vlist').html('<div class="nw" style="padding:6px 10px">' + _text['UP_SEARCHING'] + '</div>').show();
|
||||||
|
|
||||||
var data = { cid: $('#search_cid').val(), type: $('#search_type').val(), pub_pid: pub_pid };
|
var data = { cid: $('#search_cid').val(), type: $('#search_type').val() };
|
||||||
if (!by_gos)
|
if (!by_gos)
|
||||||
data.num = $('#search_num').val().trim();
|
data.num = $('#search_num').val().trim();
|
||||||
else data.gos = $('#search_gos').val().trim();
|
else data.gos = $('#search_gos').val().trim();
|
||||||
|
|
||||||
$.get('/api.php?action=upload-search-vehicles', data, function (r)
|
$.get('/api/vehicles/load', data, function (r)
|
||||||
{
|
{
|
||||||
$('#vlist').html(r);
|
$('#vlist').html(r);
|
||||||
$('#search_cid, #search_type, #search_num, #search_gos').prop('disabled', false);
|
$('#search_cid, #search_type, #search_num, #search_gos').prop('disabled', false);
|
||||||
|
@ -238,6 +314,17 @@ function searchVehicles(by_gos)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.onclick = function(e)
|
||||||
|
{
|
||||||
|
e = e || window.event;
|
||||||
|
E = e.target || e.srcElement;
|
||||||
|
if (E.id != 'phint' && E.parentNode.id != 'phint' && E != _getID('mform').place) $('#phint').slideUp();
|
||||||
|
|
||||||
|
if (E.className != 'searchVehiclesBtn' && E.id != 'vlist_table' && E.className != 'num' && $('#vlist').css('display') == 'block') $('#vlist').hide().html('');
|
||||||
|
|
||||||
|
if ($(E).closest('#views-selector').length == 0) $('#views-selector').hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -300,7 +387,6 @@ function showDefaultCity()
|
||||||
keys = Object.keys(cnames);
|
keys = Object.keys(cnames);
|
||||||
$('#main-cid').val(keys[0]);
|
$('#main-cid').val(keys[0]);
|
||||||
$('#main-cname').val(cnames[keys[0]]);
|
$('#main-cname').val(cnames[keys[0]]);
|
||||||
selectCity(keys[0]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
211
static/js/selector.js
Normal file
211
static/js/selector.js
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
function hlText(text, val)
|
||||||
|
{
|
||||||
|
val = val.replace(' -', ' —');
|
||||||
|
var p = text.toLowerCase().indexOf(val.toLowerCase());
|
||||||
|
if (p != -1) text = text.substring(0, p) + '<span class="hl">' + text.substring(p, p + val.length) + '</span>' + text.substring(p + val.length);
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(document).ready(function() { $('head').append('<link rel="stylesheet" href="/css/ui-lightness/jquery-ui-1.8.20.custom.css">'); });
|
||||||
|
|
||||||
|
|
||||||
|
(function($)
|
||||||
|
{
|
||||||
|
$.fn.autocompleteSelector = function(valfield, query, options)
|
||||||
|
{
|
||||||
|
if (this.length == 0) return;
|
||||||
|
|
||||||
|
function idToJQ(id)
|
||||||
|
{
|
||||||
|
if (!id) return null;
|
||||||
|
if (id instanceof jQuery) return id;
|
||||||
|
if (typeof id === 'string' || id instanceof String) return $('#' + id);
|
||||||
|
return $(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueField = idToJQ(valfield);
|
||||||
|
var labelField = this;
|
||||||
|
|
||||||
|
this.savedLabel = this.val();
|
||||||
|
var makeBold = (this.css('font-weight') == '700');
|
||||||
|
|
||||||
|
var defaults = {
|
||||||
|
minLength: 2,
|
||||||
|
params: {},
|
||||||
|
paramsCallback: null,
|
||||||
|
selectCallback: null,
|
||||||
|
focusCallback: null,
|
||||||
|
blurCallback: null,
|
||||||
|
renderItem: null,
|
||||||
|
defaultValue: 0,
|
||||||
|
defaultLabel: null,
|
||||||
|
valueName: 'value',
|
||||||
|
labelName: 'label',
|
||||||
|
flag: null,
|
||||||
|
flagValueName: 'rid',
|
||||||
|
flagLabelName: null,
|
||||||
|
hlFlag: false,
|
||||||
|
clearField: false,
|
||||||
|
method: 'get'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options == undefined) options = {};
|
||||||
|
var opts = $.extend({}, defaults, options);
|
||||||
|
|
||||||
|
var flagImg;
|
||||||
|
if (!opts.flag)
|
||||||
|
{
|
||||||
|
if (opts.flagLabelName && valfield && (typeof valfield === 'string' || valfield instanceof String))
|
||||||
|
{
|
||||||
|
flagImg = $('#rid_' + valfield);
|
||||||
|
if (flagImg.length == 0) flagImg = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else flagImg = idToJQ(opts.flag);
|
||||||
|
|
||||||
|
var xsign;
|
||||||
|
if (valueField && opts.defaultLabel)
|
||||||
|
{
|
||||||
|
xsign = $('<div class="xsign" />');
|
||||||
|
|
||||||
|
xsign.insertAfter(labelField).on('click', function()
|
||||||
|
{
|
||||||
|
valueField.val(opts.defaultValue);
|
||||||
|
labelField.val(opts.defaultLabel).trigger('item-select');
|
||||||
|
if (opts.selectCallback) opts.selectCallback(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
var paddingRight = parseInt(labelField.css('padding-right'));
|
||||||
|
|
||||||
|
function labelFieldChange()
|
||||||
|
{
|
||||||
|
if (labelField.val().trim() != opts.defaultLabel.trim())
|
||||||
|
{
|
||||||
|
labelField.css('padding-right', (paddingRight + 22) + 'px');
|
||||||
|
xsign.show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
labelField.css('padding-right', paddingRight);
|
||||||
|
xsign.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labelField.on('item-select', labelFieldChange);
|
||||||
|
labelFieldChange();
|
||||||
|
}
|
||||||
|
else xsign = null;
|
||||||
|
|
||||||
|
this.autocomplete({
|
||||||
|
minLength: opts.minLength,
|
||||||
|
source: function(request, response)
|
||||||
|
{
|
||||||
|
if (opts.paramsCallback) opts.paramsCallback(opts.params);
|
||||||
|
opts.params.term = request.term;
|
||||||
|
|
||||||
|
if (opts.method == 'post')
|
||||||
|
$.post(query, opts.params, response, 'json').fail(function(jx) { if (jx.responseText != '') alert(jx.responseText); });
|
||||||
|
else $.get(query, opts.params, response, 'json').fail(function(jx) { if (jx.responseText != '') alert(jx.responseText); });
|
||||||
|
},
|
||||||
|
focus: function(event, ui)
|
||||||
|
{
|
||||||
|
if (event.pageX == undefined)
|
||||||
|
{
|
||||||
|
labelField.val(ui.item.label);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select: function(event, ui)
|
||||||
|
{
|
||||||
|
if (valueField) valueField.val(ui.item[opts.valueName]);
|
||||||
|
|
||||||
|
if (!opts.clearField)
|
||||||
|
{
|
||||||
|
var label = $('<div />').html(ui.item[opts.labelName]).text();
|
||||||
|
labelField.val(label).trigger('item-select');
|
||||||
|
labelField.savedLabel = label;
|
||||||
|
|
||||||
|
if (flagImg) flagImg.attr('src', '/img/r/' + ui.item[opts.flagValueName] + '.gif');
|
||||||
|
if (makeBold) labelField.css('font-weight', 'bold');
|
||||||
|
}
|
||||||
|
else labelField.val(labelField.savedLabel);
|
||||||
|
|
||||||
|
if (opts.selectCallback) opts.selectCallback(ui.item);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.focus(function()
|
||||||
|
{
|
||||||
|
if (valueField)
|
||||||
|
{
|
||||||
|
labelField.savedLabel = labelField.val();
|
||||||
|
if (makeBold) labelField.css('font-weight', 'normal');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.focusCallback) opts.focusCallback();
|
||||||
|
})
|
||||||
|
.blur(function()
|
||||||
|
{
|
||||||
|
var val = labelField.val().trim();
|
||||||
|
if (opts.defaultLabel && val == '')
|
||||||
|
{
|
||||||
|
if (valueField) valueField.val(opts.defaultValue);
|
||||||
|
labelField.val(opts.defaultLabel).trigger('item-select');
|
||||||
|
if (flagImg) flagImg.attr('src', '/img/r/0.gif');
|
||||||
|
if (opts.selectCallback) opts.selectCallback(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (val != labelField.savedLabel) labelField.val(labelField.savedLabel);
|
||||||
|
|
||||||
|
if (valueField && makeBold) labelField.css('font-weight', 'bold');
|
||||||
|
|
||||||
|
if (opts.blurCallback) opts.blurCallback();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!opts.renderItem)
|
||||||
|
{
|
||||||
|
if (!opts.flagLabelName)
|
||||||
|
opts.renderItem = function(ul, item) { return $('<li><a><b>' + hlText(item[opts.labelName], this.element.val()) + '</b></a></li>').appendTo(ul); };
|
||||||
|
else opts.renderItem = function(ul, item) { return $('<li><a><div style="float:right; position:relative; top:1px" class="sm"> ' + (item[opts.flagLabelName] != undefined ? (opts.hlFlag ? hlText(item[opts.flagLabelName], this.element.val()) : item[opts.flagLabelName]) + ' ' : '') + '<img src="/img/r/' + item.rid + '.gif" style="position:relative; top:-1px"></div><b>' + hlText(item[opts.labelName], this.element.val()) + '</b></a></li>').appendTo(ul); };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data('ui-autocomplete')._renderItem = opts.renderItem;
|
||||||
|
this.on('click', function() { if (labelField.savedLabel == this.value) { this.select(); } });
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$.fn.citySelector = function(valfield, options)
|
||||||
|
{
|
||||||
|
if (options == undefined) options = {};
|
||||||
|
options.flagLabelName = 'rname';
|
||||||
|
options.hlFlag = true;
|
||||||
|
this.autocompleteSelector(valfield, '/api.php?action=get-cities', options);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$.fn.groupSelector = function(valfield, options)
|
||||||
|
{
|
||||||
|
if (options == undefined) options = {};
|
||||||
|
options.flagLabelName = 'rname';
|
||||||
|
options.hlFlag = true;
|
||||||
|
this.autocompleteSelector(valfield, '/api.php?action=get-groups', options);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$.fn.regionSelector = function(valfield, options)
|
||||||
|
{
|
||||||
|
if (options == undefined) options = {};
|
||||||
|
options.flagValueName = 'value';
|
||||||
|
if (!options.renderItem) options.renderItem = function(ul, item) { return $('<li><a style="line-height:14px; padding:3px 5px"><img src="/img/r/' + item.value + '.gif"> <b>' + hlText(item.label, this.element.val()) + '</b></a></li>').appendTo(ul); };
|
||||||
|
this.autocompleteSelector(valfield, '/api.php?action=get-regions', options);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$.fn.autocompleteHL = function(options)
|
||||||
|
{
|
||||||
|
this.autocomplete(options).data('ui-autocomplete')._renderItem = function(ul, item) { return $('<li><a style="line-height:14px; padding:3px 5px" class="sm">' + hlText(item.label, this.element.val()) + '</a></li>').appendTo(ul); };
|
||||||
|
}
|
||||||
|
})(jQuery);
|
97
static/js/selector2.js
Normal file
97
static/js/selector2.js
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
(function($)
|
||||||
|
{
|
||||||
|
$.fn.selector2 = function(items, options)
|
||||||
|
{
|
||||||
|
if (this.length == 0) return;
|
||||||
|
|
||||||
|
this.each(function()
|
||||||
|
{
|
||||||
|
var labelElement = $(this);
|
||||||
|
var valueField = $('input', labelElement.parent());
|
||||||
|
var currentIndex = -1;
|
||||||
|
|
||||||
|
var defaults = {
|
||||||
|
selectCallback: null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options == undefined) options = {};
|
||||||
|
var opts = $.extend({}, defaults, options);
|
||||||
|
|
||||||
|
|
||||||
|
labelElement.on('click', function()
|
||||||
|
{
|
||||||
|
var currentValue = valueField.val();
|
||||||
|
|
||||||
|
var val, html = '<div class="selector2-helper">';
|
||||||
|
for (var i = 0; i < items.length; i++)
|
||||||
|
{
|
||||||
|
html += '<div data-index="' + i + '" data-value="' + items[i].value + '"';
|
||||||
|
if (items[i].value == currentValue)
|
||||||
|
{
|
||||||
|
currentIndex = i;
|
||||||
|
html += ' class="hov"';
|
||||||
|
}
|
||||||
|
html += '>' + (items[i].item || items[i].label) + '</div>';
|
||||||
|
}
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
var helper = $(html).appendTo('body');
|
||||||
|
var offset = $(this).offset();
|
||||||
|
|
||||||
|
helper.css('top', Math.max(0, offset.top - currentIndex * 22 - 1) + 'px');
|
||||||
|
helper.css('left', (offset.left - 7) + 'px');
|
||||||
|
|
||||||
|
helper.on('click', '> div', function()
|
||||||
|
{
|
||||||
|
var el = $(this);
|
||||||
|
var val = el.data('value');
|
||||||
|
var idx = el.data('index');
|
||||||
|
|
||||||
|
valueField.val(val);
|
||||||
|
labelElement.html(items[idx].label);
|
||||||
|
if (opts.selectCallback) opts.selectCallback.call(labelElement, val);
|
||||||
|
|
||||||
|
helper.remove();
|
||||||
|
$(document).off('.selector2');
|
||||||
|
})
|
||||||
|
.on('mouseenter', '> div', function() { var el = $(this); el.addClass('hov').siblings().removeClass('hov'); currentIndex = el.data('index'); });
|
||||||
|
|
||||||
|
$(document).on('click.selector2', function(e)
|
||||||
|
{
|
||||||
|
if (!$(e.target).is(helper)) helper.remove();
|
||||||
|
$(document).off('.selector2');
|
||||||
|
})
|
||||||
|
.on('keydown.selector2', function(e)
|
||||||
|
{
|
||||||
|
if (e.which == 40 || e.which == 38)
|
||||||
|
{
|
||||||
|
e.preventDefault();
|
||||||
|
$('> div', helper).removeClass('hov');
|
||||||
|
if (e.which == 40 && ++currentIndex == items.length) currentIndex = 0; else
|
||||||
|
if (e.which == 38 && --currentIndex < 0) currentIndex = items.length - 1;
|
||||||
|
$('div[data-index="' + currentIndex + '"]', helper).addClass('hov');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if ((e.which == 13 || e.which == 32) && currentIndex != -1)
|
||||||
|
{
|
||||||
|
e.preventDefault();
|
||||||
|
$('div[data-index="' + currentIndex + '"]', helper).trigger('click');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (e.which == 27 || e.which == 8)
|
||||||
|
{
|
||||||
|
e.preventDefault();
|
||||||
|
helper.remove();
|
||||||
|
$(document).off('.selector2');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
helper.show();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
})(jQuery);
|
193
static/js/tablesort.js
Normal file
193
static/js/tablesort.js
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
/**
|
||||||
|
* tablesort plugin for jQuery
|
||||||
|
* Written by Alexander Konov
|
||||||
|
* Based on TableDnD ideas (https://github.com/isocra/TableDnD)
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function($)
|
||||||
|
{
|
||||||
|
$.fn.tablesort = function(options)
|
||||||
|
{
|
||||||
|
function recountRows(tbl)
|
||||||
|
{
|
||||||
|
var opts = tbl.data('opts');
|
||||||
|
var rows = $(opts.rowSelector, tbl);
|
||||||
|
var rowsCount = rows.length;
|
||||||
|
var handle = (opts.dragHandle ? $(opts.dragHandle, tbl) : tbl);
|
||||||
|
|
||||||
|
tbl.data('rowsCount', rowsCount);
|
||||||
|
handle[rowsCount > 1 ? 'addClass' : 'removeClass']('tablesort-active');
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(this).each(function()
|
||||||
|
{
|
||||||
|
var table = $(this);
|
||||||
|
|
||||||
|
// Метод recountRows нужно вызывать извне при добавлении или удалении строк в таблице
|
||||||
|
// Это требуется для автоотключения сортировки, если в таблице только одна строка
|
||||||
|
if (typeof options == 'string' && options == 'recountRows')
|
||||||
|
{
|
||||||
|
recountRows(table);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var eventStart = 'touchstart.tablesort mousedown.tablesort';
|
||||||
|
var eventMove = 'touchmove.tablesort mousemove.tablesort';
|
||||||
|
var eventEnd = 'touchend.tablesort mouseup.tablesort';
|
||||||
|
|
||||||
|
var dragObject = null;
|
||||||
|
var mouseOffset = null;
|
||||||
|
var oldY = 0;
|
||||||
|
|
||||||
|
var defaults = {
|
||||||
|
rowSelector: 'tr', // Можно указать tbody, если надо перемещать блоки, состоящие из нескольких строк
|
||||||
|
dragHandle: null, // Селектор "ручки" для переноса (по умолчанию можно перемещать, потянув за любое место строки, кроме <a> и <input>)
|
||||||
|
sensitivity: 10, // Sensitivity setting will throttle the trigger rate for movement detection
|
||||||
|
onChange: null // Функция onChange
|
||||||
|
};
|
||||||
|
|
||||||
|
if (options == undefined) options = {};
|
||||||
|
var opts = $.extend({}, defaults, options);
|
||||||
|
|
||||||
|
table.data('opts', opts);
|
||||||
|
|
||||||
|
|
||||||
|
if (opts.dragHandle)
|
||||||
|
table.on(eventStart, opts.dragHandle, function(e) { startDrag($(this).closest(opts.rowSelector), e); });
|
||||||
|
else table.on(eventStart, opts.rowSelector, function(e) { startDrag($(this), e); });
|
||||||
|
|
||||||
|
recountRows(table);
|
||||||
|
|
||||||
|
|
||||||
|
function startDrag(dragObj, e)
|
||||||
|
{
|
||||||
|
if (table.data('rowsCount') <= 1) return; // Некуда перетаскивать - всего одна строка в таблице
|
||||||
|
|
||||||
|
var target = $(e.target);
|
||||||
|
if (target.closest('a').length != 0 || target.is('input') || target.is('select') || target.is('textarea')) return; // Неподходящие для перетаскивания элементы с собственным поведением
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
dragObject = dragObj;
|
||||||
|
mouseOffset = getMouseY(e) - $(e.target).offset().top;
|
||||||
|
|
||||||
|
$(document).on(eventMove, mouseMove).on(eventEnd, mouseUp);
|
||||||
|
|
||||||
|
$('html').add(opts.dragHandle ? $(opts.dragHandle, table) : table).addClass('tablesort-dragging');
|
||||||
|
toggleObjectClass(dragObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function toggleObjectClass(obj)
|
||||||
|
{
|
||||||
|
var s = obj.attr('class');
|
||||||
|
if (!s) return;
|
||||||
|
|
||||||
|
var i, cls, arr = s.split(' ');
|
||||||
|
|
||||||
|
for (i = 0; i < arr.length; i++)
|
||||||
|
{
|
||||||
|
if (/s[0-9]{1,2}/.test(arr[i]))
|
||||||
|
{
|
||||||
|
cls = parseInt(arr[i].substr(1));
|
||||||
|
arr[i] = 's' + (cls >= 10 && cls <= 19 || cls >= 30 && cls <= 39 ? cls-10 : cls+10);
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.attr('class', arr.join(' '));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getMouseY(e)
|
||||||
|
{
|
||||||
|
if (e.originalEvent.changedTouches) return e.originalEvent.changedTouches[0].clientY + $(document).scrollTop();
|
||||||
|
if (e.pageY) return e.pageY;
|
||||||
|
return e.clientY + $(document).scrollTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function checkPageScroll(e)
|
||||||
|
{
|
||||||
|
var y = (e.originalEvent.changedTouches) ? e.originalEvent.changedTouches[0].clientY : e.clientY;
|
||||||
|
var h = $(window).innerHeight();
|
||||||
|
|
||||||
|
if (y < 25) window.scrollBy(0, -10); else
|
||||||
|
if (y > h - 25) window.scrollBy(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function rowMoving(dir, currentRow)
|
||||||
|
{
|
||||||
|
if (!dir || !currentRow) return;
|
||||||
|
currentRow[dir > 0 ? 'after' : 'before'](dragObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function mouseMove(e)
|
||||||
|
{
|
||||||
|
if (!dragObject) return false;
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
checkPageScroll(e);
|
||||||
|
|
||||||
|
var mouseY = getMouseY(e);
|
||||||
|
rowMoving(findDragDirection(mouseY), findDropTargetRow(dragObject, mouseY));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function findDragDirection(y)
|
||||||
|
{
|
||||||
|
var yMin = oldY - opts.sensitivity;
|
||||||
|
var yMax = oldY + opts.sensitivity;
|
||||||
|
|
||||||
|
var dir = y >= yMin && y <= yMax ? 0 : y > oldY ? 1 : -1;
|
||||||
|
if (dir) oldY = y;
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function findDropTargetRow(draggedRow, y)
|
||||||
|
{
|
||||||
|
var row, rowY;
|
||||||
|
var rows = recountRows(table);
|
||||||
|
var rowsCount = table.data('rowsCount');
|
||||||
|
|
||||||
|
for (var i = 0; i < rowsCount; i++)
|
||||||
|
{
|
||||||
|
row = rows.eq(i);
|
||||||
|
rowY = row.offset().top;
|
||||||
|
|
||||||
|
if (y >= rowY && y <= rowY + row.outerHeight()) return draggedRow.is(row) ? null : row;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function mouseUp(e)
|
||||||
|
{
|
||||||
|
if (!dragObject) return null;
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
$(document).off(eventMove + ' ' + eventEnd);
|
||||||
|
|
||||||
|
toggleObjectClass(dragObject);
|
||||||
|
dragObject = null;
|
||||||
|
|
||||||
|
$('html').add('.tablesort-dragging').removeClass('tablesort-dragging');
|
||||||
|
|
||||||
|
if (opts.onChange) opts.onChange.call(table);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return table;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})(jQuery);
|
|
@ -24,6 +24,9 @@
|
||||||
<script src="/static/js/comments.js<?php if (NGALLERY['root']['cloudflare-caching'] === true) { echo '?'.time(); } ?>"></script>
|
<script src="/static/js/comments.js<?php if (NGALLERY['root']['cloudflare-caching'] === true) { echo '?'.time(); } ?>"></script>
|
||||||
<script src="/static/js/newcore.js<?php if (NGALLERY['root']['cloudflare-caching'] === true) { echo '?'.time(); } ?>"></script>
|
<script src="/static/js/newcore.js<?php if (NGALLERY['root']['cloudflare-caching'] === true) { echo '?'.time(); } ?>"></script>
|
||||||
<script src="/static/js/act.js<?php if (NGALLERY['root']['cloudflare-caching'] === true) { echo '?'.time(); } ?>"></script>
|
<script src="/static/js/act.js<?php if (NGALLERY['root']['cloudflare-caching'] === true) { echo '?'.time(); } ?>"></script>
|
||||||
|
<script src="/static/js/selector2.js<?php if (NGALLERY['root']['cloudflare-caching'] === true) { echo '?'.time(); } ?>"></script>
|
||||||
|
<script src="/static/js/selector.js<?php if (NGALLERY['root']['cloudflare-caching'] === true) { echo '?'.time(); } ?>"></script>
|
||||||
|
<script src="/static/js/tablesort.js<?php if (NGALLERY['root']['cloudflare-caching'] === true) { echo '?'.time(); } ?>"></script>
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
|
||||||
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
|
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
|
||||||
<div class="progress-container fixed-top">
|
<div class="progress-container fixed-top">
|
||||||
|
|
|
@ -241,6 +241,8 @@ if ($photo->i('id') !== null) {
|
||||||
<a href="#" vote="0" class="vote_btn <?php if (Vote::photo(Auth::userid(), $id) === 0) {
|
<a href="#" vote="0" class="vote_btn <?php if (Vote::photo(Auth::userid(), $id) === 0) {
|
||||||
echo 'voted';
|
echo 'voted';
|
||||||
} ?>"><span>Мне не нравится</span></a>
|
} ?>"><span>Мне не нравится</span></a>
|
||||||
|
<a class="konk_btn" vote="1" href="#" ><span>Красиво, на конкурс!</span></a>
|
||||||
|
<a href="#" vote="0" class="konk_btn"><span>Неконкурсное фото</span></a>
|
||||||
</div>
|
</div>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
<div id="votes" class="votes">
|
<div id="votes" class="votes">
|
||||||
|
|
|
@ -20,6 +20,8 @@ $user = new User(Auth::userid());
|
||||||
<table class="tmain">
|
<table class="tmain">
|
||||||
<?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/Navbar.php'); ?>
|
<?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/Navbar.php'); ?>
|
||||||
<tr>
|
<tr>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/blazeface"></script>
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
|
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
|
||||||
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
||||||
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
|
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
|
||||||
|
@ -261,9 +263,124 @@ $user = new User(Auth::userid());
|
||||||
JPG, JPEG, PNG, GIF, WEBP, MP4, AVI, 3GP, MKV<br>
|
JPG, JPEG, PNG, GIF, WEBP, MP4, AVI, 3GP, MKV<br>
|
||||||
Для наибольшей совместимости, ваше видео будет обработано в формат MP4 в кодеке H264
|
Для наибольшей совместимости, ваше видео будет обработано в формат MP4 в кодеке H264
|
||||||
</div>
|
</div>
|
||||||
<div id="preview"></div>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<tr id="tableFaces" style="display: none;">
|
||||||
|
|
||||||
|
|
||||||
|
<td></td>
|
||||||
|
<td style="padding:2px 15px 5px 2px">
|
||||||
|
<div id="faceNotify">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
<tr id="tableFaces2" style="display: none;">
|
||||||
|
|
||||||
|
|
||||||
|
<td></td>
|
||||||
|
<td style="padding:2px 15px 5px 2px">
|
||||||
|
<br>
|
||||||
|
<div id="facesCanvas"></div>
|
||||||
|
<br><br>
|
||||||
|
<div id="inputFields"></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const imageUpload = document.getElementById('image');
|
||||||
|
|
||||||
|
const inputFieldsContainer = document.getElementById('inputFields');
|
||||||
|
|
||||||
|
let model;
|
||||||
|
|
||||||
|
async function loadModel() {
|
||||||
|
model = await blazeface.load();
|
||||||
|
console.log("BlazeFace model loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function detectFaces(image) {
|
||||||
|
const predictions = await model.estimateFaces(image, false);
|
||||||
|
return predictions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
loadModel();
|
||||||
|
|
||||||
|
imageUpload.addEventListener('change', async () => {
|
||||||
|
|
||||||
|
const file = imageUpload.files[0];
|
||||||
|
const img = new Image();
|
||||||
|
img.src = URL.createObjectURL(file);
|
||||||
|
img.onload = async () => {
|
||||||
|
|
||||||
|
|
||||||
|
inputFieldsContainer.innerHTML = '';
|
||||||
|
const predictions = await detectFaces(img);
|
||||||
|
|
||||||
|
if (predictions.length > 0) {
|
||||||
|
const facesTable = document.getElementById('tableFaces');
|
||||||
|
const facesTable2 = document.getElementById('tableFaces2');
|
||||||
|
if (facesTable) {
|
||||||
|
facesTable.removeAttribute('style');
|
||||||
|
}
|
||||||
|
if (facesTable2) {
|
||||||
|
facesTable2.removeAttribute('style');
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#faceNotify').html(' <div style="float:left; border:solid 1px #8C4800; padding:6px 10px 7px; margin-bottom:13px; background-color:#FADD90"><b>Обнаружены лица на фотографии</b><br>Будьте внимательны, фотография будет отклонена, если: <br>· Она нацелена на травлю, буллинг<br>· Ведёт в заблуждение пользователей<br>· Содержит в себе оскорбления<br><br><b>В случае намеренной публикации фотографий, нарушающих правила портала,<br> администрация оставляет за собой право в выдачи ограничений<br> вплоть до полной блокировки аккаунта. Спасибо за понимание</b></div>');
|
||||||
|
$('#facesCanvas').html('<canvas style="width: 500px;" id="canvas"></canvas>');
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
predictions.forEach((prediction, index) => {
|
||||||
|
const [x, y, width, height] = prediction.topLeft.concat(prediction.bottomRight).flat();
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(x, y, width - x, height - y);
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.strokeStyle = 'white';
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.fillStyle = 'white';
|
||||||
|
ctx.font = '16px Arial';
|
||||||
|
ctx.fillText(`Лицо ${index + 1}`, x, y > 10 ? y - 10 : 10);
|
||||||
|
const inputField = document.createElement('input');
|
||||||
|
inputField.type = 'text';
|
||||||
|
inputField.name = `facename_${index + 1}`; // Добавляем динамический name
|
||||||
|
inputField.placeholder = `Имя участника №${index + 1}`;
|
||||||
|
inputFieldsContainer.appendChild(inputField);
|
||||||
|
inputFieldsContainer.appendChild(document.createElement('br'));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$('#faceNotify').html('');
|
||||||
|
$('#facesCanvas').html('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="lcol">Дата съёмки:</td>
|
<td class="lcol">Дата съёмки:</td>
|
||||||
<td style="padding-bottom:12px">
|
<td style="padding-bottom:12px">
|
||||||
|
@ -502,7 +619,7 @@ $user = new User(Auth::userid());
|
||||||
<?php
|
<?php
|
||||||
$galleries = DB::query('SELECT * FROM galleries');
|
$galleries = DB::query('SELECT * FROM galleries');
|
||||||
foreach ($galleries as $g) {
|
foreach ($galleries as $g) {
|
||||||
echo '<option value="'.$g['id'].'">'.$g['title'].'</option>';
|
echo '<option value="' . $g['id'] . '">' . $g['title'] . '</option>';
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</select>
|
</select>
|
||||||
|
@ -544,7 +661,7 @@ $user = new User(Auth::userid());
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding:0; vertical-align:middle">
|
<td style="padding:0; vertical-align:middle">
|
||||||
<input type="text" name="search_num" id="search_num" maxlength="15" style="width:150px; height:22px" onfocus="showHint('num')" onblur="hideHint('num')"><input type="button" id="searchVehiclesByNumBtn" class="searchVehiclesBtn" style="height:22px; padding-top:0; position:relative; left:-1px" onclick="searchVehicles(0)" value="Найти в БД" disabled="">
|
<input type="text" name="search_num" id="search_num" maxlength="15" style="width:150px; height:22px" onfocus="showHint('num')" onblur="hideHint('num')"><input type="button" id="searchVehiclesByNumBtn" class="searchVehiclesBtn" style="height:22px; padding-top:0; position:relative; left:-1px" onclick="searchVehicles(0)" value="Найти в БД">
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:0; position:relative; vertical-align:top">
|
<td style="padding:0; position:relative; vertical-align:top">
|
||||||
<div style="border: 1px dashed rgb(255, 151, 151); color: red; background-color: rgb(255, 255, 153); padding: 2px 4px 3px; margin: 0px 5px; white-space: nowrap; position: absolute; display: none;" id="num_hint"><small>Введите название модели, или её уникальный ID на сервере <?= NGALLERY['root']['title'] ?>.</small></div>
|
<div style="border: 1px dashed rgb(255, 151, 151); color: red; background-color: rgb(255, 255, 153); padding: 2px 4px 3px; margin: 0px 5px; white-space: nowrap; position: absolute; display: none;" id="num_hint"><small>Введите название модели, или её уникальный ID на сервере <?= NGALLERY['root']['title'] ?>.</small></div>
|
||||||
|
@ -552,6 +669,139 @@ $user = new User(Auth::userid());
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<div style="position:relative; z-index:2000; padding-top:4px"><div id="vlist" class="shadow"></div></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding-top:15px; width:200px" class="lcol">Привязка:</td>
|
||||||
|
<td style="width:90%; padding:13px 15px 8px 2px">
|
||||||
|
<div id="views-selector" style="position:absolute; z-index:2000; padding:7px; display:none" class="p20 shadow">
|
||||||
|
<table id="views">
|
||||||
|
<tbody><tr>
|
||||||
|
<td colspan="3" style="text-align:center"><input type="checkbox" name="view_top" value="20" id="v20"> <label for="v20">Вид сверху</label></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><input type="radio" name="view_s" value="4" title="Сзади-слева (окна)" class="views-radio-single" style="position:relative; top:7px; left:7px"></td>
|
||||||
|
<td style="text-align:center">
|
||||||
|
<input type="radio" name="view_s" value="8" title="Левый борт" class="views-radio-single">
|
||||||
|
</td>
|
||||||
|
<td><input type="radio" name="view_s" value="2" title="Спереди-слева (окна)" style="position:relative; top:7px; left:-7px"></td>
|
||||||
|
<td style="padding:0 35px; line-height:23px" rowspan="3">
|
||||||
|
<div><input type="radio" name="view_s" value="12" id="v12"> <label for="v12">Заводская табличка</label></div>
|
||||||
|
<div><input type="radio" name="view_s" value="13" id="v13"> <label for="v13">Отдельные элементы ТС</label></div>
|
||||||
|
<div class="twoside-old"><input type="radio" name="view_s" value="14" id="v14"> <label for="v14">Не определяется (двухстороннее ТС)</label></div>
|
||||||
|
<div><input type="radio" name="view_s" value="0" id="v0"> <label for="v0"><span class="s5"> Не указан </span></label></div>
|
||||||
|
<div class="sm" style="margin-top:15px"><a href="#" class="views-toggle-link dot">Переключить на: <span class="twoside-single">Одностороннее ТС</span><span class="twoside-twoside">Двухстороннее ТС</span></a></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 2px"><input type="radio" name="view_s" value="7" title="Вид строго сзади" class="views-radio-single"></td>
|
||||||
|
<td class="views-image">
|
||||||
|
<table style="width:138px; height:82px">
|
||||||
|
<tbody><tr>
|
||||||
|
<td style="text-align:left; padding-left:25px">
|
||||||
|
<input type="radio" name="view_s" value="9" title="Салон, вид вперёд">
|
||||||
|
</td>
|
||||||
|
<td style="text-align:right; padding:0">
|
||||||
|
<input type="radio" name="view_s" value="10" title="Салон, вид назад" class="views-radio-single">
|
||||||
|
<input type="radio" name="view_s" value="11" title="Кабина" style="position:relative; top:-7px">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
</td>
|
||||||
|
<td style="padding:0 2px"><input type="radio" name="view_s" value="5" title="Вид строго спереди"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><input type="radio" name="view_s" value="3" title="Сзади-справа (двери)" class="views-radio-single" style="position:relative; top:-7px; left:7px"></td>
|
||||||
|
<td style="text-align:center">
|
||||||
|
<input type="radio" name="view_s" value="6" title="Правый борт">
|
||||||
|
</td>
|
||||||
|
<td><input type="radio" name="view_s" value="1" title="Спереди-справа (двери)" style="position:relative; top:-7px; left:-7px"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" style="text-align:center"><input type="checkbox" name="view_bottom" value="40" id="v40"> <label for="v40">Вид снизу</label></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</tbody></table>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function openViewSelector(val, el, twoside)
|
||||||
|
{
|
||||||
|
var selector = $('#views-selector');
|
||||||
|
|
||||||
|
var view = val % 20;
|
||||||
|
var modifier = val - view;
|
||||||
|
|
||||||
|
$('input[value="' + view + '"]', selector).prop('checked', true);
|
||||||
|
$('#v20').prop('checked', modifier == 20);
|
||||||
|
$('#v40').prop('checked', modifier == 40);
|
||||||
|
|
||||||
|
if (view != 14)
|
||||||
|
{
|
||||||
|
selector.attr('data-twoside', twoside);
|
||||||
|
$('.twoside-old').hide();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selector.attr('data-twoside', 1);
|
||||||
|
$('.twoside-old').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var p = el.offset();
|
||||||
|
selector.css('left', p.left + 'px').css('top', (p.top + el.height() + 3) + 'px').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setViewSelectorCallback(func)
|
||||||
|
{
|
||||||
|
var selector = $('#views-selector');
|
||||||
|
|
||||||
|
$('input[type="radio"]', selector).on('click', function(e)
|
||||||
|
{
|
||||||
|
var view = parseInt($('input[type="radio"]:checked', selector).val());
|
||||||
|
var modifier = parseInt($('input[type="checkbox"]:checked', selector).val());
|
||||||
|
if (isNaN(modifier)) modifier = 0;
|
||||||
|
|
||||||
|
var label = view || !modifier ? views[view] : '';
|
||||||
|
if (label != '' && modifier) label += ' + ';
|
||||||
|
if (modifier) label += views[modifier];
|
||||||
|
|
||||||
|
func(e, view, modifier, label);
|
||||||
|
|
||||||
|
selector.hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('input[type="checkbox"]', selector).on('click', function()
|
||||||
|
{
|
||||||
|
if ($(this).is('#v20:checked')) $('#v40').prop('checked', false); else
|
||||||
|
if ($(this).is('#v40:checked')) $('#v20').prop('checked', false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(document).ready(function()
|
||||||
|
{
|
||||||
|
$('.views-toggle-link').on('click', function()
|
||||||
|
{
|
||||||
|
var selector = $('#views-selector');
|
||||||
|
var twoside = selector.attr('data-twoside');
|
||||||
|
|
||||||
|
selector.attr('data-twoside', twoside == 1 ? 0 : 1);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
<div class="no-links" style="padding-top:2px; margin-bottom:7px"><i>Фотография ни к чему не привязана.</i></div>
|
||||||
|
<div id="links">
|
||||||
|
<table id="conn_veh" style="margin-bottom:7px; display:none">
|
||||||
|
</table>
|
||||||
|
<table id="conn_gid" style="margin-bottom:7px; display:none">
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,11 @@ use \App\Services\{Auth, DB, Date};
|
||||||
use \App\Models\Vehicle;
|
use \App\Models\Vehicle;
|
||||||
|
|
||||||
$id = explode('/', $_SERVER['REQUEST_URI'])[2];
|
$id = explode('/', $_SERVER['REQUEST_URI'])[2];
|
||||||
$vehicle = new Vehicle($id);
|
$data = DB::query('SELECT * FROM entities_data WHERE id=:id', array(':id' => $id))[0];
|
||||||
|
$vehicle = new Vehicle($data['entityid']);
|
||||||
|
|
||||||
|
$vehicledatavariables = json_decode($data['content'], true);
|
||||||
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
@ -12,7 +16,7 @@ $vehicle = new Vehicle($id);
|
||||||
<html lang="ru">
|
<html lang="ru">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/LoadHead.php'); ?>
|
<?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/LoadHead.php'); ?>
|
||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
@ -24,15 +28,17 @@ $vehicle = new Vehicle($id);
|
||||||
<?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/Navbar.php'); ?>
|
<?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/Navbar.php'); ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="main">
|
<td class="main">
|
||||||
<h1>Пиксельск, бутылка pepsi № 001</h1>
|
<h1><?= $data['title'] ?></h1>
|
||||||
|
|
||||||
<table class="horlines">
|
<table class="horlines">
|
||||||
<col width="150">
|
<col width="150">
|
||||||
<col>
|
<col>
|
||||||
<?php
|
<?php
|
||||||
$vehiclevariables = json_decode($vehicle->getvehicle('sampledata'), true);
|
$vehiclevariables = json_decode($vehicle->getvehicle('sampledata'), true);
|
||||||
|
$num = 1;
|
||||||
foreach ($vehiclevariables as $vb) {
|
foreach ($vehiclevariables as $vb) {
|
||||||
echo '<tr class="h21"><td class="ds nw">'.$vb['name'].':</td><td class="d"><b>1975</b></td></tr>';
|
echo '<tr class="h21"><td class="ds nw">' . $vb['name'] . ':</td><td class="d"><b>' . $vehicledatavariables[$num]['value'] . '</b></td></tr>';
|
||||||
|
$num++;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
@ -42,21 +48,10 @@ $vehicle = new Vehicle($id);
|
||||||
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="footer"><b><a href="/">Главная</a> <a href="/lk/">Личный кабинет</a> <a href="https://forum.transphoto.org">Форум</a> <a href="/rules/">Правила</a> <a href="/admin/">Редколлегия</a></b><br>
|
|
||||||
<a href="/set.php?dark=0" style="display:inline-block; padding:1px 10px; margin-top:5px; background-color:#ddd; color:#333">Светлая тема</a>
|
|
||||||
<div class="sitecopy">© Администрация ТрансФото и авторы материалов, 2002—2024<br>Использование фотографий и иных материалов, опубликованных на сайте, допускается только с разрешения их авторов.</div>
|
|
||||||
<div style="margin:15px 0">
|
|
||||||
<noindex>
|
|
||||||
|
|
||||||
<!-- Yandex.Metrika informer -->
|
<?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/Footer.php'); ?>
|
||||||
<a href="https://metrika.yandex.ru/stat/?id=73971775&from=informer" target="_blank" rel="nofollow"><img src="https://informer.yandex.ru/informer/73971775/3_0_DDDDDDFF_DDDDDDFF_0_pageviews"
|
|
||||||
style="width:88px; height:31px; border:0;" alt="Яндекс.Метрика" title="Яндекс.Метрика: данные за сегодня (просмотры, визиты и уникальные посетители)" class="ym-advanced-informer" data-cid="73971775" data-lang="ru" /></a>
|
|
||||||
<!-- /Yandex.Metrika informer -->
|
|
||||||
|
|
||||||
</noindex>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,90 @@
|
||||||
<?php
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Face Detection with TensorFlow.js</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Face Detection using TensorFlow.js</h1>
|
||||||
|
<input type="file" id="imageUpload" accept="image/*">
|
||||||
|
<br><br>
|
||||||
|
<canvas style="width: 100px;" id="canvas"></canvas>
|
||||||
|
<br><br>
|
||||||
|
<div id="inputFields"></div> <!-- Контейнер для динамически создаваемых полей ввода -->
|
||||||
|
<br>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/blazeface"></script>
|
||||||
|
<script>
|
||||||
|
// app.js
|
||||||
|
const imageUpload = document.getElementById('imageUpload');
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const inputFieldsContainer = document.getElementById('inputFields');
|
||||||
|
|
||||||
$videoFile = $_SERVER['DOCUMENT_ROOT'].'/static/2.mp4';
|
// Переменная для хранения модели
|
||||||
$ffmpegPath = 'E:\Maksim\kandle\app\Controllers\Video\Exec\ffmpeg.exe';
|
let model;
|
||||||
$output = exec($ffmpegPath. ' -i' .$vid, $outputt);
|
|
||||||
var_dump($output);
|
// Функция для загрузки модели
|
||||||
|
async function loadModel() {
|
||||||
|
model = await blazeface.load();
|
||||||
|
console.log("BlazeFace model loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для распознавания лиц
|
||||||
|
async function detectFaces(image) {
|
||||||
|
const predictions = await model.estimateFaces(image, false);
|
||||||
|
return predictions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка модели при инициализации
|
||||||
|
loadModel();
|
||||||
|
|
||||||
|
imageUpload.addEventListener('change', async () => {
|
||||||
|
const file = imageUpload.files[0];
|
||||||
|
const img = new Image();
|
||||||
|
img.src = URL.createObjectURL(file);
|
||||||
|
img.onload = async () => {
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
|
||||||
|
// Очищаем предыдущие поля ввода
|
||||||
|
inputFieldsContainer.innerHTML = '';
|
||||||
|
|
||||||
|
// Используем кэшированную модель для распознавания
|
||||||
|
const predictions = await detectFaces(img);
|
||||||
|
|
||||||
|
if (predictions.length > 0) {
|
||||||
|
predictions.forEach((prediction, index) => {
|
||||||
|
// Получаем координаты лица
|
||||||
|
const [x, y, width, height] = prediction.topLeft.concat(prediction.bottomRight).flat();
|
||||||
|
|
||||||
|
// Рисуем рамку вокруг лица
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(x, y, width - x, height - y);
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.strokeStyle = 'red';
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Добавляем номер лица на изображении
|
||||||
|
ctx.fillStyle = 'red';
|
||||||
|
ctx.font = '16px Arial';
|
||||||
|
ctx.fillText(`Лицо ${index + 1}`, x, y > 10 ? y - 10 : 10);
|
||||||
|
|
||||||
|
// Создаем поле ввода для каждого распознанного лица
|
||||||
|
const inputField = document.createElement('input');
|
||||||
|
inputField.type = 'text';
|
||||||
|
inputField.placeholder = `Информация о лице ${index + 1}`;
|
||||||
|
inputFieldsContainer.appendChild(inputField);
|
||||||
|
inputFieldsContainer.appendChild(document.createElement('br'));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
alert('No faces detected');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
Loading…
Reference in a new issue