feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
This commit is contained in:
1169
modules/backend/widgets/Filter.php
Normal file
1169
modules/backend/widgets/Filter.php
Normal file
File diff suppressed because it is too large
Load Diff
1492
modules/backend/widgets/Form.php
Normal file
1492
modules/backend/widgets/Form.php
Normal file
File diff suppressed because it is too large
Load Diff
2156
modules/backend/widgets/Lists.php
Normal file
2156
modules/backend/widgets/Lists.php
Normal file
File diff suppressed because it is too large
Load Diff
1379
modules/backend/widgets/MediaManager.php
Normal file
1379
modules/backend/widgets/MediaManager.php
Normal file
File diff suppressed because it is too large
Load Diff
513
modules/backend/widgets/ReportContainer.php
Normal file
513
modules/backend/widgets/ReportContainer.php
Normal file
@@ -0,0 +1,513 @@
|
||||
<?php namespace Backend\Widgets;
|
||||
|
||||
use File;
|
||||
use Lang;
|
||||
use Flash;
|
||||
use Request;
|
||||
use BackendAuth;
|
||||
use Backend\Classes\WidgetBase;
|
||||
use Backend\Classes\WidgetManager;
|
||||
use Backend\Models\UserPreference;
|
||||
use System\Models\Parameter as SystemParameters;
|
||||
use ApplicationException;
|
||||
|
||||
/**
|
||||
* Report Container Widget
|
||||
* Creates an area hosting report widgets.
|
||||
*
|
||||
* @package winter\wn-backend-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class ReportContainer extends WidgetBase
|
||||
{
|
||||
//
|
||||
// Configurable properties
|
||||
//
|
||||
|
||||
/**
|
||||
* @var string The unique report context name
|
||||
* Defines the context where the container is used.
|
||||
* Widget settings are saved in a specific context. This allows to
|
||||
* have multiple report containers on different pages that have
|
||||
* different widgets and widget settings. Context names can contain
|
||||
* only Latin letters.
|
||||
*/
|
||||
public $context = 'dashboard';
|
||||
|
||||
/**
|
||||
* @var string Determines whether widgets could be added and deleted.
|
||||
*/
|
||||
public $canAddAndDelete = true;
|
||||
|
||||
/**
|
||||
* @var array A list of default widgets to load.
|
||||
* This structure could be defined in the widget configuration file (for example config_report_container.yaml).
|
||||
* Example YAML structure:
|
||||
*
|
||||
* defaultWidgets:
|
||||
* trafficOverview:
|
||||
* class: Winter\GoogleAnalytics\ReportWidgets\TrafficOverview
|
||||
* sortOrder: 1
|
||||
* configuration:
|
||||
* title: 'Traffic overview'
|
||||
* ocWidgetWidth: 12
|
||||
*/
|
||||
public $defaultWidgets = [];
|
||||
|
||||
//
|
||||
// Object properties
|
||||
//
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $defaultAlias = 'reportContainer';
|
||||
|
||||
/**
|
||||
* @var array Collection of all report widgets used by this container.
|
||||
*/
|
||||
protected $reportWidgets = [];
|
||||
|
||||
/**
|
||||
* @var boolean Determines if report widgets have been created.
|
||||
*/
|
||||
protected $reportsDefined = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct($controller, $configuration = null)
|
||||
{
|
||||
if (!$configuration) {
|
||||
$configuration = 'config_report_container.yaml';
|
||||
}
|
||||
|
||||
if (is_string($configuration)) {
|
||||
$path = $controller->getConfigPath($configuration);
|
||||
if (File::isFile($path)) {
|
||||
$configuration = $this->makeConfig($path);
|
||||
}
|
||||
else {
|
||||
$configuration = [];
|
||||
}
|
||||
}
|
||||
|
||||
parent::__construct($controller, $configuration);
|
||||
|
||||
$this->fillFromConfig();
|
||||
$this->bindToController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure report widgets are registered so they can also be bound to
|
||||
* the controller this allows their AJAX features to operate.
|
||||
* @return void
|
||||
*/
|
||||
public function bindToController()
|
||||
{
|
||||
$this->defineReportWidgets();
|
||||
parent::bindToController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders this widget along with its collection of report widgets.
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$this->defineReportWidgets();
|
||||
$this->vars['widgets'] = $this->reportWidgets;
|
||||
return $this->makePartial('container');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function loadAssets()
|
||||
{
|
||||
$this->addCss('css/reportcontainer.css', 'core');
|
||||
$this->addJs('vendor/isotope/jquery.isotope.min.js', 'core');
|
||||
$this->addJs('js/reportcontainer.js', 'core');
|
||||
}
|
||||
|
||||
//
|
||||
// Event handlers
|
||||
//
|
||||
|
||||
public function onResetWidgets()
|
||||
{
|
||||
$this->resetWidgets();
|
||||
|
||||
$this->vars['widgets'] = $this->reportWidgets;
|
||||
|
||||
Flash::success(Lang::get('backend::lang.dashboard.reset_layout_success'));
|
||||
|
||||
return ['#'.$this->getId('container-list') => $this->makePartial('widget_list')];
|
||||
}
|
||||
|
||||
public function onMakeLayoutDefault()
|
||||
{
|
||||
if (!BackendAuth::getUser()->hasAccess('backend.manage_default_dashboard')) {
|
||||
throw new ApplicationException("You do not have permission to do that.");
|
||||
}
|
||||
|
||||
$widgets = $this->getWidgetsFromUserPreferences();
|
||||
|
||||
SystemParameters::set($this->getSystemParametersKey(), $widgets);
|
||||
|
||||
Flash::success(Lang::get('backend::lang.dashboard.make_default_success'));
|
||||
}
|
||||
|
||||
public function onUpdateWidget()
|
||||
{
|
||||
$alias = Request::input('alias');
|
||||
|
||||
$widget = $this->findWidgetByAlias($alias);
|
||||
|
||||
$widget->setProperties(json_decode(Request::input('fields'), true));
|
||||
|
||||
$this->saveWidgetProperties($alias, $widget->getProperties());
|
||||
|
||||
return [
|
||||
'#'.$alias => $widget->render()
|
||||
];
|
||||
}
|
||||
|
||||
public function onRemoveWidget()
|
||||
{
|
||||
$alias = Request::input('alias');
|
||||
|
||||
$this->removeWidget($alias);
|
||||
}
|
||||
|
||||
public function onLoadAddPopup()
|
||||
{
|
||||
$sizes = [];
|
||||
for ($i = 1; $i <= 12; $i++) {
|
||||
$sizes[$i] = $i < 12 ? $i : $i.' (' . Lang::get('backend::lang.dashboard.full_width') . ')';
|
||||
}
|
||||
|
||||
$this->vars['sizes'] = $sizes;
|
||||
$this->vars['widgets'] = WidgetManager::instance()->listReportWidgets();
|
||||
|
||||
return $this->makePartial('new_widget_popup');
|
||||
}
|
||||
|
||||
public function onAddWidget()
|
||||
{
|
||||
$className = trim(Request::input('className'));
|
||||
$size = trim(Request::input('size'));
|
||||
|
||||
if (!$className) {
|
||||
throw new ApplicationException('Please select a widget to add.');
|
||||
}
|
||||
|
||||
if (!class_exists($className)) {
|
||||
throw new ApplicationException('The selected class doesn\'t exist.');
|
||||
}
|
||||
|
||||
$widget = new $className($this->controller);
|
||||
if (!($widget instanceof \Backend\Classes\ReportWidgetBase)) {
|
||||
throw new ApplicationException('The selected class is not a report widget.');
|
||||
}
|
||||
|
||||
$widgetInfo = $this->addWidget($widget, $size);
|
||||
|
||||
return [
|
||||
'@#'.$this->getId('container-list') => $this->makePartial('widget', [
|
||||
'widget' => $widget,
|
||||
'widgetAlias' => $widgetInfo['alias'],
|
||||
'sortOrder' => $widgetInfo['sortOrder']
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
public function addWidget($widget, $size)
|
||||
{
|
||||
if (!$this->canAddAndDelete) {
|
||||
throw new ApplicationException('Access denied.');
|
||||
}
|
||||
|
||||
$widgets = $this->getWidgetsFromUserPreferences();
|
||||
|
||||
$num = count($widgets);
|
||||
do {
|
||||
$num++;
|
||||
$alias = 'report_container_'.$this->context.'_'.$num;
|
||||
}
|
||||
while (array_key_exists($alias, $widgets));
|
||||
|
||||
// Ensure that the widget's alias is correctly set for this request
|
||||
$widget->alias = $alias;
|
||||
|
||||
$sortOrder = 0;
|
||||
foreach ($widgets as $widgetInfo) {
|
||||
$sortOrder = max($sortOrder, $widgetInfo['sortOrder']);
|
||||
}
|
||||
|
||||
$sortOrder++;
|
||||
|
||||
$widget->setProperty('ocWidgetWidth', $size);
|
||||
|
||||
$widgets[$alias] = [
|
||||
'class' => get_class($widget),
|
||||
'configuration' => $widget->getProperties(),
|
||||
'sortOrder' => $sortOrder
|
||||
];
|
||||
|
||||
$this->setWidgetsToUserPreferences($widgets);
|
||||
|
||||
return [
|
||||
'alias' => $alias,
|
||||
'sortOrder' => $widgets[$alias]['sortOrder']
|
||||
];
|
||||
}
|
||||
|
||||
public function onSetWidgetOrders()
|
||||
{
|
||||
$aliases = trim(Request::input('aliases'));
|
||||
$orders = trim(Request::input('orders'));
|
||||
|
||||
if (!$aliases) {
|
||||
throw new ApplicationException('Invalid aliases string.');
|
||||
}
|
||||
|
||||
if (!$orders) {
|
||||
throw new ApplicationException('Invalid orders string.');
|
||||
}
|
||||
|
||||
$aliases = explode(',', $aliases);
|
||||
$orders = explode(',', $orders);
|
||||
|
||||
if (count($aliases) != count($orders)) {
|
||||
throw new ApplicationException('Invalid data posted.');
|
||||
}
|
||||
|
||||
$widgets = $this->getWidgetsFromUserPreferences();
|
||||
foreach ($aliases as $index => $alias) {
|
||||
if (isset($widgets[$alias])) {
|
||||
$widgets[$alias]['sortOrder'] = $orders[$index];
|
||||
}
|
||||
}
|
||||
|
||||
$this->setWidgetsToUserPreferences($widgets);
|
||||
}
|
||||
|
||||
//
|
||||
// Methods for internal use
|
||||
//
|
||||
|
||||
/**
|
||||
* Registers the report widgets that will be included in this container.
|
||||
* The chosen widgets are based on the user preferences.
|
||||
*/
|
||||
protected function defineReportWidgets()
|
||||
{
|
||||
if ($this->reportsDefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
$result = [];
|
||||
$widgets = $this->getWidgetsFromUserPreferences();
|
||||
|
||||
foreach ($widgets as $alias => $widgetInfo) {
|
||||
if ($widget = $this->makeReportWidget($alias, $widgetInfo)) {
|
||||
$result[$alias] = $widget;
|
||||
}
|
||||
}
|
||||
|
||||
uasort($result, function ($a, $b) {
|
||||
return $a['sortOrder'] - $b['sortOrder'];
|
||||
});
|
||||
|
||||
$this->reportWidgets = $result;
|
||||
|
||||
$this->reportsDefined = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a single report widget object, returned array index:
|
||||
* - widget: The widget object (Backend\Classes\ReportWidgetBase)
|
||||
* - sortOrder: The current sort order
|
||||
*
|
||||
* @param string $alias
|
||||
* @param array $widgetInfo
|
||||
* @return array
|
||||
*/
|
||||
protected function makeReportWidget($alias, $widgetInfo)
|
||||
{
|
||||
$configuration = $widgetInfo['configuration'];
|
||||
$configuration['alias'] = $alias;
|
||||
|
||||
$className = $widgetInfo['class'];
|
||||
$availableReportWidgets = array_keys(WidgetManager::instance()->listReportWidgets());
|
||||
if (!class_exists($className) || !in_array($className, $availableReportWidgets)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$widget = new $className($this->controller, $configuration);
|
||||
$widget->bindToController();
|
||||
|
||||
return ['widget' => $widget, 'sortOrder' => $widgetInfo['sortOrder']];
|
||||
}
|
||||
|
||||
protected function resetWidgets()
|
||||
{
|
||||
$this->resetWidgetsUserPreferences();
|
||||
|
||||
$this->reportsDefined = false;
|
||||
|
||||
$this->defineReportWidgets();
|
||||
}
|
||||
|
||||
protected function removeWidget($alias)
|
||||
{
|
||||
if (!$this->canAddAndDelete) {
|
||||
throw new ApplicationException('Access denied.');
|
||||
}
|
||||
|
||||
$widgets = $this->getWidgetsFromUserPreferences();
|
||||
|
||||
if (isset($widgets[$alias])) {
|
||||
unset($widgets[$alias]);
|
||||
}
|
||||
|
||||
$this->setWidgetsToUserPreferences($widgets);
|
||||
}
|
||||
|
||||
protected function findWidgetByAlias($alias)
|
||||
{
|
||||
$this->defineReportWidgets();
|
||||
|
||||
$widgets = $this->reportWidgets;
|
||||
if (!isset($widgets[$alias])) {
|
||||
throw new ApplicationException('The specified widget is not found.');
|
||||
}
|
||||
|
||||
return $widgets[$alias]['widget'];
|
||||
}
|
||||
|
||||
protected function getWidgetPropertyConfig($widget)
|
||||
{
|
||||
$properties = $widget->defineProperties();
|
||||
|
||||
$property = [
|
||||
'property' => 'ocWidgetWidth',
|
||||
'title' => Lang::get('backend::lang.dashboard.widget_columns_label', ['columns' => '(1-12)']),
|
||||
'description' => Lang::get('backend::lang.dashboard.widget_columns_description'),
|
||||
'type' => 'dropdown',
|
||||
'validationPattern' => '^[0-9]+$',
|
||||
'validationMessage' => Lang::get('backend::lang.dashboard.widget_columns_error'),
|
||||
'options' => [
|
||||
1 => '1 ' . Lang::choice('backend::lang.dashboard.columns', 1),
|
||||
2 => '2 ' . Lang::choice('backend::lang.dashboard.columns', 2),
|
||||
3 => '3 ' . Lang::choice('backend::lang.dashboard.columns', 3),
|
||||
4 => '4 ' . Lang::choice('backend::lang.dashboard.columns', 4),
|
||||
5 => '5 ' . Lang::choice('backend::lang.dashboard.columns', 5),
|
||||
6 => '6 ' . Lang::choice('backend::lang.dashboard.columns', 6),
|
||||
7 => '7 ' . Lang::choice('backend::lang.dashboard.columns', 7),
|
||||
8 => '8 ' . Lang::choice('backend::lang.dashboard.columns', 8),
|
||||
9 => '9 ' . Lang::choice('backend::lang.dashboard.columns', 9),
|
||||
10 => '10 ' . Lang::choice('backend::lang.dashboard.columns', 10),
|
||||
11 => '11 ' . Lang::choice('backend::lang.dashboard.columns', 11),
|
||||
12 => '12 ' . Lang::choice('backend::lang.dashboard.columns', 12)
|
||||
]
|
||||
];
|
||||
$result[] = $property;
|
||||
|
||||
$property = [
|
||||
'property' => 'ocWidgetNewRow',
|
||||
'title' => Lang::get('backend::lang.dashboard.widget_new_row_label'),
|
||||
'description' => Lang::get('backend::lang.dashboard.widget_new_row_description'),
|
||||
'type' => 'checkbox'
|
||||
];
|
||||
|
||||
$result[] = $property;
|
||||
foreach ($properties as $name => $params) {
|
||||
$property = [
|
||||
'property' => $name,
|
||||
'title' => isset($params['title']) ? Lang::get($params['title']) : $name,
|
||||
'type' => $params['type'] ?? 'string'
|
||||
];
|
||||
|
||||
foreach ($params as $name => $value) {
|
||||
if (isset($property[$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$property[$name] = !is_array($value) ? Lang::get($value) : $value;
|
||||
}
|
||||
|
||||
$result[] = $property;
|
||||
}
|
||||
|
||||
return json_encode($result);
|
||||
}
|
||||
|
||||
protected function getWidgetPropertyValues($widget)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
$properties = $widget->defineProperties();
|
||||
foreach ($properties as $name => $params) {
|
||||
$value = $widget->property($name);
|
||||
if (is_string($value)) {
|
||||
$value = Lang::get($value);
|
||||
}
|
||||
$result[$name] = $value;
|
||||
}
|
||||
|
||||
$result['ocWidgetWidth'] = $widget->property('ocWidgetWidth');
|
||||
$result['ocWidgetNewRow'] = $widget->property('ocWidgetNewRow');
|
||||
|
||||
return json_encode($result);
|
||||
}
|
||||
|
||||
//
|
||||
// User and system value storage
|
||||
//
|
||||
|
||||
protected function getWidgetsFromUserPreferences()
|
||||
{
|
||||
$defaultWidgets = SystemParameters::get($this->getSystemParametersKey(), $this->defaultWidgets);
|
||||
|
||||
$widgets = UserPreference::forUser()
|
||||
->get($this->getUserPreferencesKey(), $defaultWidgets);
|
||||
|
||||
if (!is_array($widgets)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $widgets;
|
||||
}
|
||||
|
||||
protected function setWidgetsToUserPreferences($widgets)
|
||||
{
|
||||
UserPreference::forUser()->set($this->getUserPreferencesKey(), $widgets);
|
||||
}
|
||||
|
||||
protected function resetWidgetsUserPreferences()
|
||||
{
|
||||
UserPreference::forUser()->reset($this->getUserPreferencesKey());
|
||||
}
|
||||
|
||||
protected function saveWidgetProperties($alias, $properties)
|
||||
{
|
||||
$widgets = $this->getWidgetsFromUserPreferences();
|
||||
|
||||
if (isset($widgets[$alias])) {
|
||||
$widgets[$alias]['configuration'] = $properties;
|
||||
|
||||
$this->setWidgetsToUserPreferences($widgets);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getUserPreferencesKey()
|
||||
{
|
||||
return 'backend::reportwidgets.'.$this->context;
|
||||
}
|
||||
|
||||
protected function getSystemParametersKey()
|
||||
{
|
||||
return 'backend::reportwidgets.default.'.$this->context;
|
||||
}
|
||||
}
|
||||
174
modules/backend/widgets/Search.php
Normal file
174
modules/backend/widgets/Search.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php namespace Backend\Widgets;
|
||||
|
||||
use Lang;
|
||||
use Backend\Classes\WidgetBase;
|
||||
|
||||
/**
|
||||
* Search Widget
|
||||
* Used for building a toolbar, Renders a search container.
|
||||
*
|
||||
* @package winter\wn-backend-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Search extends WidgetBase
|
||||
{
|
||||
//
|
||||
// Configurable properties
|
||||
//
|
||||
|
||||
/**
|
||||
* @var string Search placeholder text.
|
||||
*/
|
||||
public $prompt;
|
||||
|
||||
/**
|
||||
* @var bool Field show grow when selected.
|
||||
*/
|
||||
public $growable = true;
|
||||
|
||||
/**
|
||||
* @var string Custom partial file definition, in context of the controller.
|
||||
*/
|
||||
public $partial;
|
||||
|
||||
/**
|
||||
* @var string Defines the search mode. Commonly passed to the searchWhere() query.
|
||||
*/
|
||||
public $mode;
|
||||
|
||||
/**
|
||||
* @var string Custom scope method name. Commonly passed to the query.
|
||||
*/
|
||||
public $scope;
|
||||
|
||||
/**
|
||||
* @var bool Search on enter key instead of every key stroke.
|
||||
*/
|
||||
public $searchOnEnter = false;
|
||||
|
||||
//
|
||||
// Object properties
|
||||
//
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $defaultAlias = 'search';
|
||||
|
||||
/**
|
||||
* @var string Active search term pulled from session data.
|
||||
*/
|
||||
protected $activeTerm;
|
||||
|
||||
/**
|
||||
* @var array List of CSS classes to apply to the list container element.
|
||||
*/
|
||||
public $cssClasses = [];
|
||||
|
||||
/**
|
||||
* Initialize the widget, called by the constructor and free from its parameters.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->fillFromConfig([
|
||||
'prompt',
|
||||
'partial',
|
||||
'growable',
|
||||
'scope',
|
||||
'mode',
|
||||
'searchOnEnter',
|
||||
]);
|
||||
|
||||
/*
|
||||
* Add CSS class styles
|
||||
*/
|
||||
$this->cssClasses[] = 'icon search';
|
||||
|
||||
if ($this->growable) {
|
||||
$this->cssClasses[] = 'growable';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the widget.
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$this->prepareVars();
|
||||
|
||||
if ($this->partial) {
|
||||
return $this->controller->makePartial($this->partial);
|
||||
}
|
||||
|
||||
return $this->makePartial('search');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the view data
|
||||
*/
|
||||
public function prepareVars()
|
||||
{
|
||||
$this->vars['cssClasses'] = implode(' ', $this->cssClasses);
|
||||
$this->vars['placeholder'] = Lang::get($this->prompt);
|
||||
$this->vars['value'] = $this->getActiveTerm();
|
||||
$this->vars['searchOnEnter'] = $this->searchOnEnter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search field has been submitted.
|
||||
*/
|
||||
public function onSubmit()
|
||||
{
|
||||
/*
|
||||
* Save or reset search term in session
|
||||
*/
|
||||
$this->setActiveTerm(post($this->getName()));
|
||||
|
||||
/*
|
||||
* Trigger class event, merge results as viewable array
|
||||
*/
|
||||
$params = func_get_args();
|
||||
try {
|
||||
$result = $this->fireEvent('search.submit', [$params]);
|
||||
} catch (\Throwable $e) {
|
||||
// Remove the search term from the session if the search has failed.
|
||||
$this->setActiveTerm('');
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($result && is_array($result)) {
|
||||
return call_user_func_array('array_merge', $result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an active search term for this widget instance.
|
||||
*/
|
||||
public function getActiveTerm()
|
||||
{
|
||||
return $this->activeTerm = $this->getSession('term', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an active search term for this widget instance.
|
||||
*/
|
||||
public function setActiveTerm($term)
|
||||
{
|
||||
if (strlen($term)) {
|
||||
$this->putSession('term', $term);
|
||||
} else {
|
||||
$this->resetSession();
|
||||
}
|
||||
|
||||
$this->activeTerm = $term;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value suitable for the field name property.
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->alias . '[term]';
|
||||
}
|
||||
}
|
||||
322
modules/backend/widgets/Table.php
Normal file
322
modules/backend/widgets/Table.php
Normal file
@@ -0,0 +1,322 @@
|
||||
<?php namespace Backend\Widgets;
|
||||
|
||||
use Config;
|
||||
use Backend;
|
||||
use Lang;
|
||||
use Input;
|
||||
use Request;
|
||||
use Backend\Classes\WidgetBase;
|
||||
use Winter\Storm\Html\Helper as HtmlHelper;
|
||||
use SystemException;
|
||||
|
||||
/**
|
||||
* Table Widget.
|
||||
*
|
||||
* Represents an editable tabular control.
|
||||
*
|
||||
* @package winter\wn-backend-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Table extends WidgetBase
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $defaultAlias = 'table';
|
||||
|
||||
/**
|
||||
* @var array Table columns
|
||||
*/
|
||||
protected $columns = [];
|
||||
|
||||
/**
|
||||
* @var boolean Show data table header
|
||||
*/
|
||||
protected $showHeader = true;
|
||||
|
||||
/**
|
||||
* @var Backend\Widgets\Table\DatasourceBase
|
||||
*/
|
||||
protected $dataSource;
|
||||
|
||||
/**
|
||||
* @var string Field name used for request data.
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $recordsKeyFrom;
|
||||
|
||||
protected $dataSourceAliases = [
|
||||
'client' => '\Backend\Widgets\Table\ClientMemoryDataSource',
|
||||
'server' => '\Backend\Widgets\Table\ServerEventDataSource'
|
||||
];
|
||||
|
||||
/**
|
||||
* Initialize the widget, called by the constructor and free from its parameters.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->columns = $this->getConfig('columns', []);
|
||||
|
||||
$this->fieldName = $this->getConfig('fieldName', $this->alias);
|
||||
|
||||
$this->recordsKeyFrom = $this->getConfig('keyFrom', 'id');
|
||||
|
||||
$dataSourceClass = $this->getConfig('dataSource');
|
||||
if (!strlen($dataSourceClass)) {
|
||||
throw new SystemException('The Table widget data source is not specified in the configuration.');
|
||||
}
|
||||
|
||||
if (array_key_exists($dataSourceClass, $this->dataSourceAliases)) {
|
||||
$dataSourceClass = $this->dataSourceAliases[$dataSourceClass];
|
||||
}
|
||||
|
||||
if (!class_exists($dataSourceClass)) {
|
||||
throw new SystemException(sprintf('The Table widget data source class "%s" is could not be found.', $dataSourceClass));
|
||||
}
|
||||
|
||||
$this->dataSource = new $dataSourceClass($this->recordsKeyFrom);
|
||||
|
||||
if (Request::method() == 'POST' && $this->isClientDataSource()) {
|
||||
// Use dot notation for request data field
|
||||
$requestDataField = implode('.', HtmlHelper::nameToArray($this->fieldName));
|
||||
|
||||
if (Request::exists($requestDataField)) {
|
||||
// Load data into the client memory data source on POST
|
||||
$this->dataSource->purge();
|
||||
$this->dataSource->initRecords(Request::input($requestDataField));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data source object.
|
||||
* @return \Backend\Widgets\Table\DataSourceBase
|
||||
*/
|
||||
public function getDataSource()
|
||||
{
|
||||
return $this->dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the widget.
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$this->prepareVars();
|
||||
return $this->makePartial('table');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the view data
|
||||
*/
|
||||
public function prepareVars()
|
||||
{
|
||||
$this->vars['columns'] = $this->prepareColumnsArray();
|
||||
$this->vars['recordsKeyFrom'] = $this->recordsKeyFrom;
|
||||
|
||||
$this->vars['recordsPerPage'] = $this->getConfig('recordsPerPage', false) ?: 'false';
|
||||
$this->vars['postbackHandlerName'] = $this->getConfig('postbackHandlerName');
|
||||
$this->vars['searching'] = $this->getConfig('searching', false);
|
||||
$this->vars['adding'] = $this->getConfig('adding', true);
|
||||
$this->vars['deleting'] = $this->getConfig('deleting', true);
|
||||
$this->vars['toolbar'] = $this->getConfig('toolbar', true);
|
||||
$this->vars['height'] = $this->getConfig('height', false) ?: 'false';
|
||||
$this->vars['dynamicHeight'] = $this->getConfig('dynamicHeight', false) ?: 'false';
|
||||
|
||||
$this->vars['btnAddRowLabel'] = Lang::get($this->getConfig('btnAddRowLabel', 'backend::lang.form.insert_row'));
|
||||
$this->vars['btnAddRowBelowLabel'] = Lang::get($this->getConfig('btnAddRowBelowLabel', 'backend::lang.form.insert_row_below'));
|
||||
$this->vars['btnDeleteRowLabel'] = Lang::get($this->getConfig('btnDeleteRowLabel', 'backend::lang.form.delete_row'));
|
||||
|
||||
$isClientDataSource = $this->isClientDataSource();
|
||||
|
||||
$this->vars['clientDataSourceClass'] = $isClientDataSource ? 'client' : 'server';
|
||||
$this->vars['data'] = json_encode(
|
||||
$isClientDataSource ? $this->dataSource->getAllRecords() : []
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
// Internals
|
||||
//
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function loadAssets()
|
||||
{
|
||||
$this->addCss('css/table.css', 'core');
|
||||
|
||||
if (Config::get('develop.decompileBackendAssets', false)) {
|
||||
$scripts = Backend::decompileAsset($this->getAssetPath('js/build.js'));
|
||||
foreach ($scripts as $script) {
|
||||
$this->addJs($script, 'core');
|
||||
}
|
||||
} else {
|
||||
$this->addJs('js/build-min.js', 'core');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the columns associative array to a regular array and translates column headers and drop-down options.
|
||||
* Working with regular arrays is much faster in JavaScript.
|
||||
* References:
|
||||
* - https://www.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/
|
||||
* - https://jsperf.com/performance-of-array-vs-object/3
|
||||
*/
|
||||
protected function prepareColumnsArray()
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->columns as $key => $data) {
|
||||
$data['key'] = $key;
|
||||
|
||||
if (isset($data['title'])) {
|
||||
$data['title'] = trans($data['title']);
|
||||
}
|
||||
|
||||
if (isset($data['options'])) {
|
||||
foreach ($data['options'] as &$option) {
|
||||
$option = trans($option);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['validation'])) {
|
||||
foreach ($data['validation'] as &$validation) {
|
||||
if (isset($validation['message'])) {
|
||||
$validation['message'] = trans($validation['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result[] = $data;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function isClientDataSource()
|
||||
{
|
||||
return $this->dataSource instanceof \Backend\Widgets\Table\ClientMemoryDataSource;
|
||||
}
|
||||
|
||||
//
|
||||
// Event handlers
|
||||
//
|
||||
|
||||
public function onServerGetRecords()
|
||||
{
|
||||
// Disable asset broadcasting
|
||||
$this->controller->flushAssets();
|
||||
|
||||
if ($this->isClientDataSource()) {
|
||||
throw new SystemException('The Table widget is not configured to use the server data source.');
|
||||
}
|
||||
|
||||
$count = post('count');
|
||||
|
||||
// Oddly, JS may pass false as a string (@todo)
|
||||
if ($count === 'false') {
|
||||
$count = false;
|
||||
}
|
||||
|
||||
return [
|
||||
'records' => $this->dataSource->getRecords(post('offset'), $count),
|
||||
'count' => $this->dataSource->getCount()
|
||||
];
|
||||
}
|
||||
|
||||
public function onServerSearchRecords()
|
||||
{
|
||||
// Disable asset broadcasting
|
||||
$this->controller->flushAssets();
|
||||
|
||||
if ($this->isClientDataSource()) {
|
||||
throw new SystemException('The Table widget is not configured to use the server data source.');
|
||||
}
|
||||
|
||||
$count = post('count');
|
||||
|
||||
// Oddly, JS may pass false as a string (@todo)
|
||||
if ($count === 'false') {
|
||||
$count = false;
|
||||
}
|
||||
|
||||
return [
|
||||
'records' => $this->dataSource->searchRecords(post('query'), post('offset'), $count),
|
||||
'count' => $this->dataSource->getCount()
|
||||
];
|
||||
}
|
||||
|
||||
public function onServerCreateRecord()
|
||||
{
|
||||
if ($this->isClientDataSource()) {
|
||||
throw new SystemException('The Table widget is not configured to use the server data source.');
|
||||
}
|
||||
|
||||
$this->dataSource->createRecord(
|
||||
post('recordData'),
|
||||
post('placement'),
|
||||
post('relativeToKey')
|
||||
);
|
||||
|
||||
return $this->onServerGetRecords();
|
||||
}
|
||||
|
||||
public function onServerUpdateRecord()
|
||||
{
|
||||
if ($this->isClientDataSource()) {
|
||||
throw new SystemException('The Table widget is not configured to use the server data source.');
|
||||
}
|
||||
|
||||
$this->dataSource->updateRecord(post('key'), post('recordData'));
|
||||
}
|
||||
|
||||
public function onServerDeleteRecord()
|
||||
{
|
||||
if ($this->isClientDataSource()) {
|
||||
throw new SystemException('The Table widget is not configured to use the server data source.');
|
||||
}
|
||||
|
||||
$this->dataSource->deleteRecord(post('key'));
|
||||
|
||||
return $this->onServerGetRecords();
|
||||
}
|
||||
|
||||
public function onGetDropdownOptions()
|
||||
{
|
||||
$columnName = Input::get('column');
|
||||
$rowData = Input::get('rowData');
|
||||
|
||||
$eventResults = $this->fireEvent('table.getDropdownOptions', [$columnName, $rowData]);
|
||||
|
||||
$options = [];
|
||||
if (count($eventResults)) {
|
||||
$options = $eventResults[0];
|
||||
}
|
||||
|
||||
return [
|
||||
'options' => $options
|
||||
];
|
||||
}
|
||||
|
||||
public function onGetAutocompleteOptions()
|
||||
{
|
||||
$columnName = Input::get('column');
|
||||
$rowData = Input::get('rowData');
|
||||
|
||||
$eventResults = $this->fireEvent('table.getAutocompleteOptions', [$columnName, $rowData]);
|
||||
|
||||
$options = [];
|
||||
if (count($eventResults)) {
|
||||
$options = $eventResults[0];
|
||||
}
|
||||
|
||||
return [
|
||||
'options' => $options
|
||||
];
|
||||
}
|
||||
}
|
||||
106
modules/backend/widgets/Toolbar.php
Normal file
106
modules/backend/widgets/Toolbar.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php namespace Backend\Widgets;
|
||||
|
||||
use Backend\Classes\WidgetBase;
|
||||
|
||||
/**
|
||||
* Toolbar Widget
|
||||
* Used for building a toolbar, renders a toolbar.
|
||||
*
|
||||
* @package winter\wn-backend-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Toolbar extends WidgetBase
|
||||
{
|
||||
//
|
||||
// Configurable properties
|
||||
//
|
||||
|
||||
/**
|
||||
* @var string Partial name containing the toolbar buttons
|
||||
*/
|
||||
public $buttons;
|
||||
|
||||
/**
|
||||
* @var array|string Search widget configuration or partial name, optional.
|
||||
*/
|
||||
public $search;
|
||||
|
||||
//
|
||||
// Object properties
|
||||
//
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $defaultAlias = 'toolbar';
|
||||
|
||||
/**
|
||||
* @var WidgetBase Reference to the search widget object.
|
||||
*/
|
||||
protected $searchWidget;
|
||||
|
||||
/**
|
||||
* @var array List of CSS classes to apply to the toolbar container element
|
||||
*/
|
||||
public $cssClasses = [];
|
||||
|
||||
/**
|
||||
* Initialize the widget, called by the constructor and free from its parameters.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->fillFromConfig([
|
||||
'buttons',
|
||||
'search',
|
||||
]);
|
||||
|
||||
/*
|
||||
* Prepare the search widget (optional)
|
||||
*/
|
||||
if (isset($this->search)) {
|
||||
if (is_string($this->search)) {
|
||||
$searchConfig = $this->makeConfig(['partial' => $this->search]);
|
||||
}
|
||||
else {
|
||||
$searchConfig = $this->makeConfig($this->search);
|
||||
}
|
||||
|
||||
$searchConfig->alias = $this->alias . 'Search';
|
||||
$this->searchWidget = $this->makeWidget('Backend\Widgets\Search', $searchConfig);
|
||||
$this->searchWidget->bindToController();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the widget.
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
$this->prepareVars();
|
||||
return $this->makePartial('toolbar');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the view data
|
||||
*/
|
||||
public function prepareVars()
|
||||
{
|
||||
$this->vars['search'] = $this->searchWidget ? $this->searchWidget->render() : '';
|
||||
$this->vars['cssClasses'] = implode(' ', $this->cssClasses);
|
||||
$this->vars['controlPanel'] = $this->makeControlPanel();
|
||||
}
|
||||
|
||||
public function getSearchWidget()
|
||||
{
|
||||
return $this->searchWidget;
|
||||
}
|
||||
|
||||
public function makeControlPanel()
|
||||
{
|
||||
if (!isset($this->buttons)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->controller->makePartial($this->buttons, $this->vars);
|
||||
}
|
||||
}
|
||||
10
modules/backend/widgets/filter/partials/_filter.php
Normal file
10
modules/backend/widgets/filter/partials/_filter.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<div
|
||||
id="<?= $this->getId(); ?>"
|
||||
class="control-filter <?= $cssClasses ?>"
|
||||
data-control="filterwidget"
|
||||
data-options-handler="<?= $this->getEventHandler('onFilterGetOptions') ?>"
|
||||
data-update-handler="<?= $this->getEventHandler('onFilterUpdate') ?>">
|
||||
|
||||
<?= $this->makePartial('filter_scopes') ?>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php foreach ($scopes as $scope): ?>
|
||||
<?= $this->renderScopeElement($scope) ?>
|
||||
<?php endforeach ?>
|
||||
@@ -0,0 +1,14 @@
|
||||
<div
|
||||
class="filter-scope button-group"
|
||||
data-scope-name="<?= e($scope->scopeName) ?>"
|
||||
<?= ($scope->config['required'] ?? false) ? 'data-scope-required="true"' : '' ?>
|
||||
>
|
||||
<?php foreach ($scope->options as $key => $label): ?>
|
||||
<button
|
||||
class="btn <?= $scope->value === $key ? 'btn-primary' : 'btn-default' ?>"
|
||||
data-scope-name="<?= e($scope->scopeName) ?>"
|
||||
data-scope-value="<?= e($key) ?>">
|
||||
<?= e($label) ?>
|
||||
</button>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
<!-- Checkbox scope -->
|
||||
<div
|
||||
class="filter-scope checkbox custom-checkbox"
|
||||
data-scope-name="<?= $scope->scopeName ?>">
|
||||
<input type="checkbox" id="<?= $scope->getId() ?>" <?= $scope->value ? 'checked' : '' ?> />
|
||||
<label for="<?= $scope->getId() ?>"><?= e(trans($scope->label)) ?></label>
|
||||
</div>
|
||||
17
modules/backend/widgets/filter/partials/_scope_date.php
Normal file
17
modules/backend/widgets/filter/partials/_scope_date.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<!-- Date scope -->
|
||||
<a
|
||||
class="filter-scope-date filter-has-popover <?= isset($date) ? 'active' : '' ?>"
|
||||
href="javascript:;"
|
||||
data-scope-name="<?= $scope->scopeName ?>"
|
||||
data-scope-data="<?= e(json_encode([
|
||||
'date' => isset($date) ? $date : null,
|
||||
'minDate' => $scope->minDate,
|
||||
'maxDate' => $scope->maxDate,
|
||||
'firstDay' => $scope->firstDay,
|
||||
'yearRange' => $scope->yearRange,
|
||||
])) ?>"
|
||||
<?= $scope->ignoreTimezone ? 'data-ignore-timezone' : ''; ?>
|
||||
>
|
||||
<span class="filter-label"><?= e(trans($scope->label)) ?>:</span>
|
||||
<span class="filter-setting"><?= isset($dateStr) ? $dateStr : e(trans('backend::lang.filter.date_all')) ?></span>
|
||||
</a>
|
||||
17
modules/backend/widgets/filter/partials/_scope_daterange.php
Normal file
17
modules/backend/widgets/filter/partials/_scope_daterange.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<!-- Date Range scope -->
|
||||
<a
|
||||
class="filter-scope-date filter-has-popover range <?= isset($after) || isset($before) ? 'active' : '' ?>"
|
||||
href="javascript:;"
|
||||
data-scope-name="<?= $scope->scopeName ?>"
|
||||
data-scope-data="<?= e(json_encode([
|
||||
'dates' => [isset($after) ? $after : null, isset($before) ? $before : null],
|
||||
'minDate' => $scope->minDate,
|
||||
'maxDate' => $scope->maxDate,
|
||||
'firstDay' => $scope->firstDay,
|
||||
'yearRange' => $scope->yearRange,
|
||||
])) ?>"
|
||||
<?= $scope->ignoreTimezone ? 'data-ignore-timezone' : ''; ?>
|
||||
>
|
||||
<span class="filter-label"><?= e(trans($scope->label)) ?>:</span>
|
||||
<span class="filter-setting"><?= isset($afterStr) && isset($beforeStr) ? ($afterStr . ' → ' . $beforeStr) : e(trans('backend::lang.filter.date_all')) ?></span>
|
||||
</a>
|
||||
31
modules/backend/widgets/filter/partials/_scope_dropdown.php
Normal file
31
modules/backend/widgets/filter/partials/_scope_dropdown.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
$required = $scope->config['required'] ?? false;
|
||||
$emptyOption = $scope->config['emptyOption'] ?? $scope->label ?? Lang::get('backend::lang.form.select_placeholder');
|
||||
$hasEmpty = !$required && $emptyOption;
|
||||
$selectedValue = $scope->value ?? null;
|
||||
|
||||
// If required and no default, preselect first option
|
||||
if ($required && $selectedValue === null && !empty($scope->options)) {
|
||||
reset($scope->options);
|
||||
$selectedValue = key($scope->options);
|
||||
}
|
||||
?>
|
||||
<div class="filter-scope dropdown" data-scope-name="<?= e($scope->scopeName) ?>">
|
||||
<select
|
||||
class="form-control custom-select select-no-search"
|
||||
data-placeholder="<?= e($emptyOption); ?>"
|
||||
data-dropdown-auto-width="true"
|
||||
data-width="resolve"
|
||||
<?= $required ? 'data-allow-clear="false"' : ''; ?>
|
||||
name="<?= e($scope->scopeName) ?>"
|
||||
>
|
||||
<?php if ($hasEmpty): ?>
|
||||
<option value="" <?= $selectedValue === null || $selectedValue === '' ? 'selected' : '' ?>></option>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($scope->options as $key => $label): ?>
|
||||
<option value="<?= e($key) ?>" <?= $selectedValue == $key ? 'selected' : '' ?>>
|
||||
<?= e($label) ?>
|
||||
</option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
12
modules/backend/widgets/filter/partials/_scope_group.php
Normal file
12
modules/backend/widgets/filter/partials/_scope_group.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<!-- Group scope -->
|
||||
<a
|
||||
class="filter-scope <?= $scope->value ? 'active' : '' ?>"
|
||||
href="javascript:;"
|
||||
data-scope-name="<?= $scope->scopeName ?>"
|
||||
<?php if ($depends = $this->getScopeDepends($scope)): ?>
|
||||
data-scope-depends="<?= $depends ?>"
|
||||
<?php endif ?>
|
||||
>
|
||||
<span class="filter-label"><?= e(trans($scope->label)) ?>:</span>
|
||||
<span class="filter-setting"><?= $scope->value ? count($scope->value) : e(trans('backend::lang.filter.all')) ?></span>
|
||||
</a>
|
||||
15
modules/backend/widgets/filter/partials/_scope_number.php
Normal file
15
modules/backend/widgets/filter/partials/_scope_number.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<!-- Number scope -->
|
||||
<a
|
||||
class="filter-scope-number filter-has-popover <?= isset($number) ? 'active' : '' ?>"
|
||||
href="javascript:;"
|
||||
data-scope-name="<?= $scope->scopeName ?>"
|
||||
data-scope-data="<?= e(json_encode([
|
||||
'number' => isset($number) ? $number : null,
|
||||
'step' => isset($step) ? $step : null,
|
||||
'minValue' => isset($minValue) ? $minValue : null,
|
||||
'maxValue' => isset($maxValue) ? $maxValue : null,
|
||||
]))
|
||||
?>">
|
||||
<span class="filter-label"><?= e(trans($scope->label)) ?>:</span>
|
||||
<span class="filter-setting"><?= isset($number) ? $number : e(trans('backend::lang.filter.number_all')) ?></span>
|
||||
</a>
|
||||
@@ -0,0 +1,15 @@
|
||||
<!-- Number Range scope -->
|
||||
<a
|
||||
class="filter-scope-number filter-has-popover range <?= isset($min) || isset($max) ? 'active' : '' ?>"
|
||||
href="javascript:;"
|
||||
data-scope-name="<?= $scope->scopeName ?>"
|
||||
data-scope-data="<?= e(json_encode([
|
||||
'numbers' => [isset($min) ? $min : null, isset($max) ? $max : null],
|
||||
'step' => isset($step) ? $step : null,
|
||||
'minValue' => isset($minValue) ? $minValue : null,
|
||||
'maxValue' => isset($maxValue) ? $maxValue : null,
|
||||
]))
|
||||
?>">
|
||||
<span class="filter-label"><?= e(trans($scope->label)) ?>:</span>
|
||||
<span class="filter-setting"><?= isset($minStr) && isset($maxStr) ? ($minStr . ' → ' . $maxStr) : e(trans('backend::lang.filter.number_all')) ?></span>
|
||||
</a>
|
||||
@@ -0,0 +1,7 @@
|
||||
<!-- Switch scope -->
|
||||
<div
|
||||
class="filter-scope checkbox custom-checkbox is-indeterminate"
|
||||
data-scope-name="<?= $scope->scopeName ?>">
|
||||
<input type="checkbox" id="<?= $scope->getId() ?>" data-checked="<?= $scope->value ?: '0' ?>" />
|
||||
<label for="<?= $scope->getId() ?>"><?= e(trans($scope->label)) ?></label>
|
||||
</div>
|
||||
15
modules/backend/widgets/filter/partials/_scope_text.php
Normal file
15
modules/backend/widgets/filter/partials/_scope_text.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<div class="filter-scope text loading-indicator-container size-input-text">
|
||||
<label class="filter-label">
|
||||
<?= e(trans($scope->label)) ?>:
|
||||
<input type="text"
|
||||
name="options[value][<?= $scope->scopeName ?>]"
|
||||
data-request="<?= $this->getEventHandler('onFilterUpdate') ?>"
|
||||
data-request-data="'scopeName':'<?= $scope->scopeName ?>'"
|
||||
data-track-input
|
||||
data-load-indicator
|
||||
data-load-indicator-opaque
|
||||
size="<?= $size ?>"
|
||||
value="<?= isset($value) ? e($value):''; ?>"
|
||||
class="form-control"/>
|
||||
</label>
|
||||
</div>
|
||||
391
modules/backend/widgets/form/assets/js/winter.form.js
Normal file
391
modules/backend/widgets/form/assets/js/winter.form.js
Normal file
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* Form Widget
|
||||
*
|
||||
* Dependences:
|
||||
* - Nil
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
var Base = $.wn.foundation.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var FormWidget = function (element, options) {
|
||||
this.$el = $(element)
|
||||
this.options = options || {}
|
||||
this.fieldElementCache = null
|
||||
|
||||
/*
|
||||
* Throttle dependency updating
|
||||
*/
|
||||
this.dependantUpdateInterval = 300
|
||||
this.dependantUpdateTimers = {}
|
||||
|
||||
$.wn.foundation.controlUtils.markDisposable(element)
|
||||
Base.call(this)
|
||||
this.init()
|
||||
}
|
||||
|
||||
FormWidget.prototype = Object.create(BaseProto)
|
||||
FormWidget.prototype.constructor = FormWidget
|
||||
|
||||
FormWidget.prototype.init = function() {
|
||||
|
||||
this.$form = this.$el.closest('form')
|
||||
|
||||
this.bindDependants()
|
||||
this.bindCheckboxlist()
|
||||
this.toggleEmptyTabs()
|
||||
this.bindLazyTabs()
|
||||
this.bindCollapsibleSections()
|
||||
|
||||
this.$el.on('oc.triggerOn.afterUpdate', this.proxy(this.toggleEmptyTabs))
|
||||
this.$el.one('dispose-control', this.proxy(this.dispose))
|
||||
}
|
||||
|
||||
FormWidget.prototype.dispose = function() {
|
||||
this.unbindDependants()
|
||||
this.unbindCheckboxList()
|
||||
this.unbindLazyTabs()
|
||||
this.unbindCollapsibleSections()
|
||||
|
||||
this.$el.off('dispose-control', this.proxy(this.dispose))
|
||||
this.$el.removeData('oc.formwidget')
|
||||
|
||||
this.$el = null
|
||||
this.$form = null
|
||||
this.options = null
|
||||
this.fieldElementCache = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
/*
|
||||
* Logic for checkboxlist
|
||||
*/
|
||||
FormWidget.prototype.bindCheckboxlist = function() {
|
||||
|
||||
var checkAllBoxes = function($field, flag) {
|
||||
$('input[type=checkbox]', $field)
|
||||
.prop('checked', flag)
|
||||
.first()
|
||||
.trigger('change')
|
||||
}
|
||||
|
||||
this.$el.on('click', '[data-field-checkboxlist-all]', function() {
|
||||
checkAllBoxes($(this).closest('.field-checkboxlist'), true)
|
||||
})
|
||||
|
||||
this.$el.on('click', '[data-field-checkboxlist-none]', function() {
|
||||
checkAllBoxes($(this).closest('.field-checkboxlist'), false)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Unbind checkboxlist handlers
|
||||
*/
|
||||
FormWidget.prototype.unbindCheckboxList = function() {
|
||||
this.$el.off('click', '[data-field-checkboxlist-all]')
|
||||
this.$el.off('click', '[data-field-checkboxlist-none]')
|
||||
}
|
||||
|
||||
/*
|
||||
* Get all fields elements that belong to this form, nested form
|
||||
* fields are removed from this collection.
|
||||
*/
|
||||
FormWidget.prototype.getFieldElements = function() {
|
||||
if (this.fieldElementCache !== null) {
|
||||
return this.fieldElementCache
|
||||
}
|
||||
|
||||
var form = this.$el,
|
||||
nestedFields = form.find('[data-control="formwidget"] [data-field-name]')
|
||||
|
||||
return this.fieldElementCache = form.find('[data-field-name]').not(nestedFields)
|
||||
}
|
||||
|
||||
/*
|
||||
* Bind dependant fields
|
||||
*/
|
||||
FormWidget.prototype.bindDependants = function() {
|
||||
var self = this,
|
||||
fieldMap = this._getDependants()
|
||||
|
||||
/*
|
||||
* When a field is updated, refresh its dependents
|
||||
*/
|
||||
$.each(fieldMap, function(fieldName, toRefresh) {
|
||||
$(document).on('change.oc.formwidget',
|
||||
'[data-field-name="' + fieldName + '"]',
|
||||
$.proxy(self.onRefreshDependants, self, fieldName, toRefresh)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Dispose of the dependant field handlers
|
||||
*/
|
||||
FormWidget.prototype.unbindDependants = function() {
|
||||
var fieldMap = this._getDependants()
|
||||
|
||||
$.each(fieldMap, function(fieldName, toRefresh) {
|
||||
$(document).off('change.oc.formwidget', '[data-field-name="' + fieldName + '"]')
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve the dependant fields
|
||||
*/
|
||||
FormWidget.prototype._getDependants = function() {
|
||||
if (!$('[data-field-depends]', this.$el).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fieldMap = {},
|
||||
fieldElements = this.getFieldElements()
|
||||
|
||||
/*
|
||||
* Map master and slave fields
|
||||
*/
|
||||
fieldElements.filter('[data-field-depends]').each(function() {
|
||||
var name = $(this).data('field-name'),
|
||||
depends = $(this).data('field-depends')
|
||||
|
||||
$.each(depends, function(index, depend){
|
||||
if (!fieldMap[depend]) {
|
||||
fieldMap[depend] = { fields: [] }
|
||||
}
|
||||
|
||||
fieldMap[depend].fields.push(name)
|
||||
})
|
||||
})
|
||||
|
||||
return fieldMap
|
||||
}
|
||||
|
||||
/*
|
||||
* Refresh a dependancy field
|
||||
* Uses a throttle to prevent duplicate calls and click spamming.
|
||||
*
|
||||
* The event parameter is passed automatically by jQuery as the third
|
||||
* argument (after the two preset arguments from $.proxy). It carries
|
||||
* a cascadeChain array that tracks which fields have already been
|
||||
* refreshed in the current cascade, preventing infinite loops when
|
||||
* fields have circular dependsOn declarations.
|
||||
*/
|
||||
FormWidget.prototype.onRefreshDependants = function(fieldName, toRefresh, event) {
|
||||
var self = this,
|
||||
form = this.$el,
|
||||
formEl = this.$form,
|
||||
fieldElements = this.getFieldElements(),
|
||||
cascadeChain = (event && event.cascadeChain) || []
|
||||
|
||||
/*
|
||||
* If this field already appears in the cascade chain, we have
|
||||
* a circular dependency. Stop the cascade to prevent an
|
||||
* infinite loop. See: https://github.com/wintercms/winter/issues/421
|
||||
*/
|
||||
if (cascadeChain.indexOf(fieldName) !== -1) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.dependantUpdateTimers[fieldName] !== undefined) {
|
||||
window.clearTimeout(this.dependantUpdateTimers[fieldName])
|
||||
}
|
||||
|
||||
this.dependantUpdateTimers[fieldName] = window.setTimeout(function() {
|
||||
var refreshData = $.extend({},
|
||||
toRefresh,
|
||||
paramToObj('data-refresh-data', self.options.refreshData)
|
||||
)
|
||||
|
||||
formEl.request(self.options.refreshHandler, {
|
||||
data: refreshData
|
||||
}).success(function() {
|
||||
self.toggleEmptyTabs()
|
||||
|
||||
var newChain = cascadeChain.concat([fieldName])
|
||||
$.each(toRefresh.fields, function(key, field) {
|
||||
var cascadeEvent = $.Event('change')
|
||||
cascadeEvent.cascadeChain = newChain
|
||||
$('[data-field-name="' + field + '"]').trigger(cascadeEvent)
|
||||
})
|
||||
})
|
||||
}, this.dependantUpdateInterval)
|
||||
|
||||
$.each(toRefresh.fields, function(index, field) {
|
||||
fieldElements.filter('[data-field-name="'+field+'"]:visible')
|
||||
.addClass('loading-indicator-container size-form-field')
|
||||
.loadIndicator()
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Render tab form fields once a lazy tab is selected.
|
||||
*/
|
||||
FormWidget.prototype.bindLazyTabs = function() {
|
||||
var tabControl = $('[data-control=tab]', this.$el),
|
||||
tabContainer = $('.nav-tabs', tabControl)
|
||||
|
||||
tabContainer.on('click', '.tab-lazy [data-toggle="tab"]', function() {
|
||||
var $el = $(this),
|
||||
handlerName = $el.data('tab-lazy-handler')
|
||||
|
||||
$.request(handlerName, {
|
||||
data: {
|
||||
target: $el.data('target'),
|
||||
name: $el.data('tab-name'),
|
||||
section: $el.data('tab-section'),
|
||||
},
|
||||
success: function(data) {
|
||||
this.success(data)
|
||||
$el.parent().removeClass('tab-lazy')
|
||||
// Trigger all input presets to populate new fields.
|
||||
setTimeout(function() {
|
||||
$('[data-input-preset]').each(function() {
|
||||
var preset = $(this).data('oc.inputPreset')
|
||||
if (preset && preset.$src) {
|
||||
preset.$src.trigger('input')
|
||||
}
|
||||
})
|
||||
}, 0)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// If initial active tab is lazy loaded, load it immediately
|
||||
if ($('> li.active.tab-lazy', tabContainer).length) {
|
||||
$('> li.active.tab-lazy > [data-toggle="tab"]', tabContainer).trigger('click')
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Unbind the lazy tab handlers
|
||||
*/
|
||||
FormWidget.prototype.unbindLazyTabs = function() {
|
||||
var tabControl = $('[data-control=tab]', this.$el)
|
||||
|
||||
$('.nav-tabs', tabControl).off('click', '.tab-lazy [data-toggle="tab"]')
|
||||
}
|
||||
|
||||
/*
|
||||
* Hides tabs that have no content, it is possible this can be
|
||||
* called multiple times in a single cycle due to input.trigger.
|
||||
*/
|
||||
FormWidget.prototype.toggleEmptyTabs = function() {
|
||||
var self = this,
|
||||
form = this.$el
|
||||
|
||||
if (this.toggleEmptyTabsTimer !== undefined) {
|
||||
window.clearTimeout(this.toggleEmptyTabsTimer)
|
||||
}
|
||||
|
||||
this.toggleEmptyTabsTimer = window.setTimeout(function() {
|
||||
|
||||
var tabControl = $('[data-control=tab]', self.$el),
|
||||
tabContainer = $('.nav-tabs', tabControl)
|
||||
|
||||
if (!tabControl.length || !form || !form.length || !$.contains(form.get(0), tabControl.get(0)))
|
||||
return
|
||||
|
||||
/*
|
||||
* Check each tab pane for form field groups
|
||||
*/
|
||||
$('.tab-pane:not(.lazy)', tabControl).each(function() {
|
||||
$('[data-target="#' + $(this).attr('id') + '"]', tabControl)
|
||||
.closest('li')
|
||||
.toggle(!!$('> .form-group:not(:empty):not(.hide)', $(this)).length)
|
||||
})
|
||||
|
||||
/*
|
||||
* If a hidden tab was selected, select the first visible tab
|
||||
*/
|
||||
if (!$('> li.active:visible', tabContainer).length) {
|
||||
$('> li:visible:first', tabContainer)
|
||||
.find('> a:first')
|
||||
.tab('show')
|
||||
}
|
||||
|
||||
}, 1)
|
||||
}
|
||||
|
||||
/*
|
||||
* Makes sections collapsible by targeting every field after
|
||||
* up until the next section
|
||||
*/
|
||||
FormWidget.prototype.bindCollapsibleSections = function() {
|
||||
$('.section-field[data-field-collapsible]', this.$form)
|
||||
.addClass('collapsed')
|
||||
.find('.field-section:first')
|
||||
.addClass('is-collapsible')
|
||||
.end()
|
||||
.on('click', function() {
|
||||
$(this)
|
||||
.toggleClass('collapsed')
|
||||
.nextUntil('.section-field').toggle()
|
||||
})
|
||||
.nextUntil('.section-field').hide()
|
||||
}
|
||||
|
||||
/*
|
||||
* Unbinds collapsible section handlers
|
||||
*/
|
||||
FormWidget.prototype.unbindCollapsibleSections = function() {
|
||||
$('.section-field[data-field-collapsible]', this.$form).off('click')
|
||||
}
|
||||
|
||||
FormWidget.DEFAULTS = {
|
||||
refreshHandler: null,
|
||||
refreshData: {}
|
||||
}
|
||||
|
||||
// FORM WIDGET PLUGIN DEFINITION
|
||||
// ============================
|
||||
|
||||
var old = $.fn.formWidget
|
||||
|
||||
$.fn.formWidget = function (option) {
|
||||
var args = arguments,
|
||||
result
|
||||
|
||||
this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('oc.formwidget')
|
||||
var options = $.extend({}, FormWidget.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
if (!data) $this.data('oc.formwidget', (data = new FormWidget(this, options)))
|
||||
if (typeof option == 'string') result = data[option].call($this)
|
||||
if (typeof result != 'undefined') return false
|
||||
})
|
||||
|
||||
return result ? result : this
|
||||
}
|
||||
|
||||
$.fn.formWidget.Constructor = FormWidget
|
||||
|
||||
// FORM WIDGET NO CONFLICT
|
||||
// =================
|
||||
|
||||
$.fn.formWidget.noConflict = function () {
|
||||
$.fn.formWidget = old
|
||||
return this
|
||||
}
|
||||
|
||||
// FORM WIDGET DATA-API
|
||||
// ==============
|
||||
|
||||
function paramToObj(name, value) {
|
||||
if (value === undefined) value = ''
|
||||
if (typeof value == 'object') return value
|
||||
|
||||
try {
|
||||
return ocJSON("{" + value + "}")
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error('Error parsing the '+name+' attribute value. '+e)
|
||||
}
|
||||
}
|
||||
|
||||
$(document).render(function() {
|
||||
$('[data-control="formwidget"]').formWidget();
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
11
modules/backend/widgets/form/partials/_field-container.php
Normal file
11
modules/backend/widgets/form/partials/_field-container.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<div
|
||||
class="form-group <?= $this->previewMode ? 'form-group-preview' : '' ?> <?= $field->type ?>-field span-<?= $field->span ?> <?= $field->required?'is-required':'' ?> <?= $field->stretch?'layout-relative':'' ?> <?= $field->cssClass ?>"
|
||||
<?php if ($depends = $this->getFieldDepends($field)): ?>
|
||||
data-field-depends="<?= $depends ?>"
|
||||
<?php endif ?>
|
||||
data-field-name="<?= $field->fieldName ?>"
|
||||
<?= $field->getAttributes('container') ?>
|
||||
id="<?= $field->getId('group') ?>"><?=
|
||||
/* Must be on the same line for :empty selector */
|
||||
trim($this->makePartial('field', ['field' => $field]))
|
||||
?></div>
|
||||
30
modules/backend/widgets/form/partials/_field.php
Normal file
30
modules/backend/widgets/form/partials/_field.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php if (!$field->hidden): ?>
|
||||
|
||||
<?php if (!$this->showFieldLabels($field)): ?>
|
||||
|
||||
<?= $this->renderFieldElement($field) ?>
|
||||
|
||||
<?php else: ?>
|
||||
<?php
|
||||
$fieldComment = $field->commentHtml ? trans($field->comment) : e(trans($field->comment));
|
||||
?>
|
||||
|
||||
<?php if ($field->label): ?>
|
||||
<label for="<?= $field->getId() ?>">
|
||||
<?= e(trans($field->label)) ?>
|
||||
</label>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($field->comment && $field->commentPosition == 'above'): ?>
|
||||
<p class="help-block before-field"><?= $fieldComment ?></p>
|
||||
<?php endif ?>
|
||||
|
||||
<?= $this->renderFieldElement($field) ?>
|
||||
|
||||
<?php if ($field->comment && $field->commentPosition == 'below'): ?>
|
||||
<p class="help-block"><?= $fieldComment ?></p>
|
||||
<?php endif ?>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<?php endif ?>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
$fieldOptions = $field->options();
|
||||
?>
|
||||
<!-- Balloon selector -->
|
||||
<div
|
||||
data-control="balloon-selector"
|
||||
id="<?= $field->getId() ?>"
|
||||
class="control-balloon-selector <?= $this->previewMode || $field->disabled ? 'control-disabled' : '' ?>"
|
||||
<?= $field->getAttributes() ?>>
|
||||
<ul>
|
||||
<?php foreach ($fieldOptions as $value => $text): ?>
|
||||
<li data-value="<?= e($value) ?>" class="<?= $field->isSelected($value) ? 'active' : '' ?>"><?= e(trans($text)) ?></li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="<?= $field->getName() ?>"
|
||||
id="<?= $field->getId() ?>"
|
||||
value="<?= e($field->value) ?>"
|
||||
/>
|
||||
</div>
|
||||
74
modules/backend/widgets/form/partials/_field_button.php
Normal file
74
modules/backend/widgets/form/partials/_field_button.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* @var \Winter\Storm\Database\Model $formModel
|
||||
* @var \Backend\Classes\FormField $field
|
||||
*/
|
||||
$action = 'button';
|
||||
$handler = null;
|
||||
$href = null;
|
||||
$target = null;
|
||||
|
||||
if (!empty($field->config['href']) || filter_var($field->value, FILTER_VALIDATE_URL)) {
|
||||
$action = 'link';
|
||||
$href = $field->config['href'] ?? '';
|
||||
$target = $field->config['target'] ?? null;
|
||||
if ($formModel->hasAttribute($href)) {
|
||||
$href = $formModel->getAttribute($href);
|
||||
}
|
||||
if (filter_var($field->value, FILTER_VALIDATE_URL)) {
|
||||
$href = $field->value;
|
||||
}
|
||||
} elseif (!empty($field->config['handler'])) {
|
||||
$action = 'popup';
|
||||
$handler = $field->config['handler'];
|
||||
}
|
||||
|
||||
$element = $action === 'link' ? 'a' : 'button';
|
||||
$label = $field->config['buttonLabel'] ?? '';
|
||||
$buttonType = $field->config['buttonType'] ?? 'default';
|
||||
$classes = implode(' ', array_filter([
|
||||
"btn btn-$buttonType",
|
||||
$field->config['buttonCssClass'] ?? ''
|
||||
]));
|
||||
$request = $field->config['request'] ?? '';
|
||||
|
||||
$loadingText = $field->config['loading'] ?? '';
|
||||
$icon = $field->config['icon'] ?? '';
|
||||
?>
|
||||
<div class="loading-indicator-container">
|
||||
<?php if ($field->path): ?>
|
||||
<?= $this->controller->makePartial($field->path, [
|
||||
'formWidget' => $this,
|
||||
'formModel' => $formModel,
|
||||
'formField' => $field,
|
||||
'formValue' => $field->value,
|
||||
'model' => $formModel,
|
||||
'field' => $field,
|
||||
'value' => $field->value,
|
||||
'action' => $action,
|
||||
'element' => $element,
|
||||
'label' => $label,
|
||||
'buttonType' => $buttonType,
|
||||
'classes' => $classes,
|
||||
'handler' => $handler,
|
||||
'request' => $request,
|
||||
'href' => $href,
|
||||
'target' => $target,
|
||||
'loading' => $loadingText,
|
||||
'icon' => $icon,
|
||||
]) ?>
|
||||
<?php else: ?>
|
||||
<<?= e($element); ?>
|
||||
class="<?= e($classes); ?>"
|
||||
data-load-indicator<?= !empty($loadingText) ? '="' . e(trans($loadingText)) . '"' : ''; ?>
|
||||
<?= $action === 'popup' ? 'data-control="popup"' : ''; ?>
|
||||
<?= !empty($handler) ? 'data-handler="' . e($handler) . '"' : ''; ?>
|
||||
<?= !empty($request) ? 'data-request="' . e($request) . '"' : ''; ?>
|
||||
<?= !empty($href) ? 'href="' . e($href) . '"' : ''; ?>
|
||||
<?= !empty($target) ? 'target="' . e($target) . '"' : ''; ?>
|
||||
>
|
||||
<?= !empty($icon) ? '<i class="' . e($icon) . '"></i>' : '' ?>
|
||||
<?= e(trans($label)); ?>
|
||||
</<?= e($element); ?>>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
23
modules/backend/widgets/form/partials/_field_checkbox.php
Normal file
23
modules/backend/widgets/form/partials/_field_checkbox.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<!-- Checkbox -->
|
||||
<div class="checkbox custom-checkbox" tabindex="0">
|
||||
<input
|
||||
type="hidden"
|
||||
name="<?= $field->getName() ?>"
|
||||
value="0"
|
||||
<?= $this->previewMode ? 'disabled="disabled"' : '' ?>>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="<?= $field->getId() ?>"
|
||||
name="<?= $field->getName() ?>"
|
||||
value="1"
|
||||
<?= $this->previewMode ? 'disabled="disabled"' : '' ?>
|
||||
<?= $field->isSelected() ? 'checked="checked"' : '' ?>
|
||||
<?= $field->getAttributes() ?>>
|
||||
|
||||
<label for="<?= $field->getId() ?>">
|
||||
<?= e(trans($field->label)) ?>
|
||||
</label>
|
||||
<?php if ($field->comment): ?>
|
||||
<p class="help-block"><?= $field->commentHtml ? trans($field->comment) : e(trans($field->comment)) ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
120
modules/backend/widgets/form/partials/_field_checkboxlist.php
Normal file
120
modules/backend/widgets/form/partials/_field_checkboxlist.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
$fieldOptions = $field->options();
|
||||
$checkedValues = (array) $field->value;
|
||||
$isScrollable = count($fieldOptions) > 10;
|
||||
$readOnly = $this->previewMode || $field->readOnly || $field->disabled;
|
||||
$quickselectEnabled = $field->getConfig('quickselect', $isScrollable);
|
||||
?>
|
||||
<!-- Checkbox List -->
|
||||
<?php if ($readOnly && $field->value): ?>
|
||||
|
||||
<div class="field-checkboxlist">
|
||||
<?php
|
||||
$index = 0;
|
||||
foreach ($fieldOptions as $value => $option):
|
||||
$index++;
|
||||
$checkboxId = 'checkbox_'.$field->getId().'_'.$index;
|
||||
if (!in_array($value, $checkedValues)) {
|
||||
continue;
|
||||
}
|
||||
if (!is_array($option)) {
|
||||
$option = [$option];
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="checkbox custom-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="<?= $checkboxId ?>"
|
||||
name="<?= $field->getName() ?>[]"
|
||||
value="<?= e($value) ?>"
|
||||
disabled="disabled"
|
||||
checked="checked">
|
||||
|
||||
<label for="<?= $checkboxId ?>">
|
||||
<?= e(trans($option[0])) ?>
|
||||
</label>
|
||||
<?php if (isset($option[1])): ?>
|
||||
<p class="help-block"><?= e(trans($option[1])) ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
|
||||
<?php elseif (count($fieldOptions)): ?>
|
||||
|
||||
<div class="field-checkboxlist <?= $isScrollable ? 'is-scrollable' : '' ?>">
|
||||
<?php if ($quickselectEnabled): ?>
|
||||
<!-- Quick selection -->
|
||||
<div class="checkboxlist-controls">
|
||||
<div>
|
||||
<a href="javascript:;" data-field-checkboxlist-all>
|
||||
<i class="icon-check-square"></i> <?= e(trans('backend::lang.form.select_all')) ?>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="javascript:;" data-field-checkboxlist-none>
|
||||
<i class="icon-eraser"></i> <?= e(trans('backend::lang.form.select_none')) ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="field-checkboxlist-inner">
|
||||
|
||||
<?php if ($isScrollable): ?>
|
||||
<!-- Scrollable Checkbox list -->
|
||||
<div class="field-checkboxlist-scrollable">
|
||||
<div class="control-scrollbar" data-control="scrollbar">
|
||||
<?php endif ?>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="<?= $field->getName() ?>"
|
||||
value="0" />
|
||||
|
||||
<?php
|
||||
$index = 0;
|
||||
foreach ($fieldOptions as $value => $option):
|
||||
$index++;
|
||||
$checkboxId = 'checkbox_'.$field->getId().'_'.$index;
|
||||
if (!is_array($option)) {
|
||||
$option = [$option];
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="checkbox custom-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="<?= $checkboxId ?>"
|
||||
name="<?= $field->getName() ?>[]"
|
||||
value="<?= e($value) ?>"
|
||||
<?= $readOnly ? 'disabled="disabled"' : '' ?>
|
||||
<?= in_array($value, $checkedValues) ? 'checked="checked"' : '' ?>>
|
||||
|
||||
<label for="<?= $checkboxId ?>">
|
||||
<?= e(trans($option[0])) ?>
|
||||
</label>
|
||||
<?php if (isset($option[1])): ?>
|
||||
<p class="help-block"><?= e(trans($option[1])) ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php if ($isScrollable): ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<!-- No options specified -->
|
||||
<?php if ($field->placeholder): ?>
|
||||
<p><?= e(trans($field->placeholder)) ?></p>
|
||||
<?php endif ?>
|
||||
|
||||
<?php endif ?>
|
||||
43
modules/backend/widgets/form/partials/_field_dropdown.php
Normal file
43
modules/backend/widgets/form/partials/_field_dropdown.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
$fieldOptions = $field->options();
|
||||
if ($fieldOptions instanceof Illuminate\Support\Collection) {
|
||||
$fieldOptions = $fieldOptions->all();
|
||||
}
|
||||
?>
|
||||
|
||||
<!-- Dropdown -->
|
||||
<?php if ($this->previewMode || $field->readOnly): ?>
|
||||
<div class="form-control" <?= $field->readOnly ? 'disabled="disabled"' : ''; ?>>
|
||||
<?= (isset($fieldOptions[$field->value])) ? e(trans($fieldOptions[$field->value])) : '' ?>
|
||||
</div>
|
||||
<input type="hidden" name="<?= $field->getName() ?>" value="<?= $field->value ?>">
|
||||
<?php else:
|
||||
$emptyOption = $field->getConfig('emptyOption', $field->placeholder);
|
||||
$options = $field->getAttributes(htmlBuild:false);
|
||||
$options['id'] = $field->getId();
|
||||
$options['class'] = 'form-control custom-select';
|
||||
if ($field->getConfig('showSearch', true) === false) {
|
||||
$options['class'] .= ' select-no-search';
|
||||
}
|
||||
if ($field->getConfig('allowCustom', false)) {
|
||||
$options['class'] .= ' select-modifiable';
|
||||
}
|
||||
if ($emptyOption) {
|
||||
$options['emptyOption'] = e(trans($emptyOption));
|
||||
}
|
||||
if ($field->placeholder) {
|
||||
$options['data-placeholder'] = e(trans($field->placeholder));
|
||||
}
|
||||
foreach ($fieldOptions as $key => &$value) {
|
||||
if (is_string($value) && str_contains($value, '::')) {
|
||||
$value = e(trans($value));
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?= Form::select(
|
||||
name: $field->getName(),
|
||||
list: $fieldOptions,
|
||||
selected: $field->value,
|
||||
options: $options
|
||||
) ?>
|
||||
<?php endif ?>
|
||||
30
modules/backend/widgets/form/partials/_field_email.php
Normal file
30
modules/backend/widgets/form/partials/_field_email.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<!-- Email -->
|
||||
<div class="input-group static">
|
||||
<span class="input-group-addon">
|
||||
<i class="empty wn-icon-envelope"></i>
|
||||
</span>
|
||||
<?php if ($this->previewMode): ?>
|
||||
<?php if ($field->value): ?>
|
||||
<a
|
||||
href="mailto:<?= e($field->value) ?>"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="form-control"
|
||||
>
|
||||
<?= e($field->value) ?>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span class="form-control"> </span>
|
||||
<?php endif ?>
|
||||
<?php else: ?>
|
||||
<input
|
||||
type="email"
|
||||
name="<?= $field->getName() ?>"
|
||||
id="<?= $field->getId() ?>"
|
||||
value="<?= e($field->value) ?>"
|
||||
placeholder="<?= e(trans($field->placeholder)) ?>"
|
||||
class="form-control"
|
||||
<?= $field->getAttributes() ?>
|
||||
/>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
8
modules/backend/widgets/form/partials/_field_hint.php
Normal file
8
modules/backend/widgets/form/partials/_field_hint.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?= $this->controller->makeHintPartial($field->getId(), $field->path ?: $field->fieldName, [
|
||||
'formModel' => $formModel,
|
||||
'formField' => $field,
|
||||
'formValue' => $field->value,
|
||||
'model' => $formModel,
|
||||
'field' => $field,
|
||||
'value' => $field->value
|
||||
]) ?>
|
||||
26
modules/backend/widgets/form/partials/_field_number.php
Normal file
26
modules/backend/widgets/form/partials/_field_number.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<!-- Number -->
|
||||
<?php if ($this->previewMode): ?>
|
||||
<span class="form-control"><?= isset($field->value) ? e($field->value) : ' ' ?></span>
|
||||
<?php else: ?>
|
||||
<?php
|
||||
$min = isset($field->config['min']) ? $field->config['min'] : false;
|
||||
$max = isset($field->config['max']) ? $field->config['max'] : false;
|
||||
$step = isset($field->config['step']) ? $field->config['step'] : 'any';
|
||||
?>
|
||||
|
||||
<input
|
||||
type="number"
|
||||
step="<?= $step ?>"
|
||||
name="<?= $field->getName() ?>"
|
||||
id="<?= $field->getId() ?>"
|
||||
value="<?= e($field->value) ?>"
|
||||
placeholder="<?= e(trans($field->placeholder)) ?>"
|
||||
class="form-control"
|
||||
autocomplete="off"
|
||||
<?= $min !== false ? 'min="' . $min . '"' : ''; ?>
|
||||
<?= $max !== false ? 'max="' . $max . '"' : ''; ?>
|
||||
<?= $field->hasAttribute('pattern') ? '' : 'pattern="-?\d+(\.\d+)?"' ?>
|
||||
<?= $field->hasAttribute('maxlength') ? '' : 'maxlength="255"' ?>
|
||||
<?= $field->getAttributes() ?>
|
||||
/>
|
||||
<?php endif ?>
|
||||
9
modules/backend/widgets/form/partials/_field_partial.php
Normal file
9
modules/backend/widgets/form/partials/_field_partial.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?= $this->controller->makePartial($field->path ?: $field->fieldName, [
|
||||
'formWidget' => $this,
|
||||
'formModel' => $formModel,
|
||||
'formField' => $field,
|
||||
'formValue' => $field->value,
|
||||
'model' => $formModel,
|
||||
'field' => $field,
|
||||
'value' => $field->value
|
||||
]) ?>
|
||||
16
modules/backend/widgets/form/partials/_field_password.php
Normal file
16
modules/backend/widgets/form/partials/_field_password.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<!-- Password -->
|
||||
<?php if ($this->previewMode): ?>
|
||||
<div class="form-control">********</div>
|
||||
<?php else: ?>
|
||||
<input
|
||||
type="password"
|
||||
name="<?= $field->getName() ?>"
|
||||
id="<?= $field->getId() ?>"
|
||||
value=""
|
||||
placeholder="<?= e(trans($field->placeholder)) ?>"
|
||||
class="form-control"
|
||||
<?= $field->hasAttribute('autocomplete') ? '' : 'autocomplete="new-password"' ?>
|
||||
<?= $field->hasAttribute('maxlength') ? '' : 'maxlength="255"' ?>
|
||||
<?= $field->getAttributes() ?>
|
||||
/>
|
||||
<?php endif ?>
|
||||
43
modules/backend/widgets/form/partials/_field_radio.php
Normal file
43
modules/backend/widgets/form/partials/_field_radio.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
$fieldOptions = $field->options();
|
||||
?>
|
||||
<!-- Radio List -->
|
||||
<?php if (count($fieldOptions)): ?>
|
||||
|
||||
<?php $index = 0; foreach ($fieldOptions as $value => $option): ?>
|
||||
<?php
|
||||
$index++;
|
||||
if (is_string($option)) {
|
||||
$option = array($option);
|
||||
}
|
||||
|
||||
$fieldId = md5(uniqid($field->getId($index), true));
|
||||
?>
|
||||
<div class="radio custom-radio">
|
||||
|
||||
<input
|
||||
id="<?= $fieldId ?>"
|
||||
name="<?= $field->getName() ?>"
|
||||
value="<?= e($value) ?>"
|
||||
type="radio"
|
||||
<?= $field->isSelected($value) ? 'checked="checked"' : '' ?>
|
||||
<?= $this->previewMode ? 'disabled="disabled"' : '' ?>
|
||||
<?= $field->getAttributes() ?>>
|
||||
|
||||
<label for="<?= $fieldId ?>">
|
||||
<?= e(trans($option[0])) ?>
|
||||
</label>
|
||||
<?php if (isset($option[1])): ?>
|
||||
<p class="help-block"><?= e(trans($option[1])) ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<!-- No options specified -->
|
||||
<?php if ($field->placeholder): ?>
|
||||
<p><?= e(trans($field->placeholder)) ?></p>
|
||||
<?php endif ?>
|
||||
|
||||
<?php endif ?>
|
||||
40
modules/backend/widgets/form/partials/_field_range.php
Normal file
40
modules/backend/widgets/form/partials/_field_range.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<!-- Range -->
|
||||
<?php if ($this->previewMode): ?>
|
||||
<span class="form-control"><?= isset($field->value) ? e($field->value) : ' ' ?></span>
|
||||
<?php else: ?>
|
||||
<?php
|
||||
$min = $field->config['min'] ?? 0;
|
||||
$max = $field->config['max'] ?? 100;
|
||||
$step = $field->config['step'] ?? 1;
|
||||
$value = $field->value;
|
||||
if ($min > $max) {
|
||||
$min = $max - $step;
|
||||
}
|
||||
if (is_null($value)) {
|
||||
$value = ($min + $max) / 2;
|
||||
}
|
||||
?>
|
||||
|
||||
<input
|
||||
type="range"
|
||||
step="<?= $step ?>"
|
||||
name="<?= $field->getName() ?>"
|
||||
id="<?= $field->getId() ?>"
|
||||
value="<?= e($value) ?>"
|
||||
min="<?= $min ?>"
|
||||
max="<?= $max ?>"
|
||||
<?= $field->getAttributes() ?>
|
||||
/>
|
||||
<span style="position: absolute; transform: translateX(-50%)"></span>
|
||||
<script>
|
||||
(() => {
|
||||
const input = document.getElementById("<?= $field->getId() ?>");
|
||||
input.addEventListener("input", function () {
|
||||
this.nextElementSibling.innerHTML = this.value;
|
||||
var pos = ((this.value - <?= $min ?>) / (<?= $max ?> - <?= $min ?>) * 100);
|
||||
this.nextElementSibling.style.left = `calc(${pos}% + ${8 - pos * 0.15}px)`;
|
||||
});
|
||||
input.dispatchEvent(new Event('input'));
|
||||
})();
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
10
modules/backend/widgets/form/partials/_field_section.php
Normal file
10
modules/backend/widgets/form/partials/_field_section.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<!-- Section -->
|
||||
<div class="field-section">
|
||||
<?php if ($field->label): ?>
|
||||
<h4><?= e(trans($field->label)) ?></h4>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($field->comment): ?>
|
||||
<p class="help-block"><?= $field->commentHtml ? trans($field->comment) : e(trans($field->comment)) ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
38
modules/backend/widgets/form/partials/_field_switch.php
Normal file
38
modules/backend/widgets/form/partials/_field_switch.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
$previewMode = false;
|
||||
if ($this->previewMode || $field->readOnly) {
|
||||
$previewMode = true;
|
||||
}
|
||||
|
||||
$on = isset($field->config['on']) ? $field->config['on'] : 'backend::lang.form.field_on';
|
||||
$off = isset($field->config['off']) ? $field->config['off'] : 'backend::lang.form.field_off';
|
||||
?>
|
||||
|
||||
<!-- Switch -->
|
||||
<div class="<?= $previewMode ? 'disabled' : '' ?>">
|
||||
<div class="field-switch">
|
||||
<label for="<?= $field->getId() ?>"><?= e(trans($field->label)) ?></label>
|
||||
<?php if ($field->comment): ?>
|
||||
<p class="help-block"><?= $field->commentHtml ? trans($field->comment) : e(trans($field->comment)) ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="<?= $field->getName() ?>"
|
||||
value="0"
|
||||
<?= $previewMode ? 'disabled="disabled"' : '' ?>>
|
||||
|
||||
<label class="custom-switch" <?= $previewMode ? 'onclick="return false"' : '' ?>>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="<?= $field->getId() ?>"
|
||||
name="<?= $field->getName() ?>"
|
||||
value="1"
|
||||
<?= $previewMode ? 'readonly="readonly"' : '' ?>
|
||||
<?= $field->value == 1 ? 'checked="checked"' : '' ?>
|
||||
<?= $field->getAttributes() ?>>
|
||||
<span><span><?= e(trans($on)) ?></span><span><?= e(trans($off)) ?></span></span>
|
||||
<a class="slide-button"></a>
|
||||
</label>
|
||||
</div>
|
||||
49
modules/backend/widgets/form/partials/_field_tel.php
Normal file
49
modules/backend/widgets/form/partials/_field_tel.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<!-- Tel (Phone) -->
|
||||
<?php
|
||||
$fieldOptions = $field->options();
|
||||
$hasOptions = is_array($fieldOptions) && count($fieldOptions);
|
||||
$listId = $hasOptions ? $field->getId() . '-list' : null;
|
||||
?>
|
||||
<div class="input-group static">
|
||||
<span class="input-group-addon">
|
||||
<i class="empty wn-icon-phone"></i>
|
||||
</span>
|
||||
<?php if ($this->previewMode): ?>
|
||||
<?php if ($field->value): ?>
|
||||
<a
|
||||
href="tel:<?= e($field->value) ?>"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="form-control"
|
||||
>
|
||||
<?= e($field->value) ?>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span class="form-control"> </span>
|
||||
<?php endif ?>
|
||||
<?php else: ?>
|
||||
<input
|
||||
type="tel"
|
||||
id="<?= $field->getId() ?>"
|
||||
name="<?= $field->getName() ?>"
|
||||
value="<?= e($field->value) ?>"
|
||||
class="form-control"
|
||||
<?= isset($field->autocomplete) && is_string($field->autocomplete) ? 'autocomplete="' . e($field->autocomplete) . '"' : '' ?>
|
||||
<?= isset($field->maxlength) && is_numeric($field->maxlength) ? 'maxlength="' . e($field->maxlength) . '"' : '' ?>
|
||||
<?= isset($field->minlength) && is_numeric($field->minlength) ? 'minlength="' . e($field->minlength) . '"' : '' ?>
|
||||
<?= isset($field->pattern) && is_string($field->pattern) ? 'pattern="' . e($field->pattern) . '"' : '' ?>
|
||||
<?= isset($field->placeholder) && is_string($field->placeholder) ? 'placeholder="' . e(trans($field->placeholder)) . '"' : '' ?>
|
||||
<?= isset($field->size) && is_numeric($field->size) ? 'size="' . e($field->size) . '"' : '' ?>
|
||||
<?= $field->getAttributes() ?>
|
||||
<?= $listId ? 'list="' . e($listId) . '"' : '' ?>
|
||||
/>
|
||||
<?php if ($hasOptions): ?>
|
||||
<datalist id="<?= e($listId) ?>">
|
||||
<?php foreach ($fieldOptions as $value => $label): ?>
|
||||
<?php $value = is_int($value) ? $label : $value ?>
|
||||
<option value="<?= e($value) ?>"<?= $value !== $label ? ' label="' . e(trans($label)) . '"' : '' ?>></option>
|
||||
<?php endforeach ?>
|
||||
</datalist>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
15
modules/backend/widgets/form/partials/_field_text.php
Normal file
15
modules/backend/widgets/form/partials/_field_text.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<!-- Text -->
|
||||
<?php if ($this->previewMode): ?>
|
||||
<span class="form-control"><?= $field->value ? e($field->value) : ' ' ?></span>
|
||||
<?php else: ?>
|
||||
<input
|
||||
type="text"
|
||||
name="<?= $field->getName() ?>"
|
||||
id="<?= $field->getId() ?>"
|
||||
value="<?= e($field->value) ?>"
|
||||
placeholder="<?= e(trans($field->placeholder)) ?>"
|
||||
class="form-control"
|
||||
autocomplete="off"
|
||||
<?= $field->getAttributes() ?>
|
||||
/>
|
||||
<?php endif ?>
|
||||
12
modules/backend/widgets/form/partials/_field_textarea.php
Normal file
12
modules/backend/widgets/form/partials/_field_textarea.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<!-- Textarea -->
|
||||
<?php if ($this->previewMode): ?>
|
||||
<div class="form-control"><?= nl2br(e($field->value)) ?></div>
|
||||
<?php else: ?>
|
||||
<textarea
|
||||
name="<?= $field->getName() ?>"
|
||||
id="<?= $field->getId() ?>"
|
||||
autocomplete="off"
|
||||
class="form-control field-textarea size-<?= $field->size ?>"
|
||||
placeholder="<?= e(trans($field->placeholder)) ?>"
|
||||
<?= $field->getAttributes() ?>><?= e($field->value) ?></textarea>
|
||||
<?php endif?>
|
||||
49
modules/backend/widgets/form/partials/_field_url.php
Normal file
49
modules/backend/widgets/form/partials/_field_url.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<!-- URL -->
|
||||
<?php
|
||||
$fieldOptions = $field->options();
|
||||
$hasOptions = is_array($fieldOptions) && count($fieldOptions);
|
||||
$listId = $hasOptions ? $field->getId() . '-list' : null;
|
||||
?>
|
||||
<div class="input-group static">
|
||||
<span class="input-group-addon">
|
||||
<i class="empty wn-icon-link"></i>
|
||||
</span>
|
||||
<?php if ($this->previewMode): ?>
|
||||
<?php if ($field->value): ?>
|
||||
<a
|
||||
href="<?= e($field->value) ?>"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="form-control"
|
||||
>
|
||||
<?= e($field->value) ?>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span class="form-control"> </span>
|
||||
<?php endif ?>
|
||||
<?php else: ?>
|
||||
<input
|
||||
type="url"
|
||||
name="<?= $field->getName() ?>"
|
||||
id="<?= $field->getId() ?>"
|
||||
value="<?= e($field->value) ?>"
|
||||
class="form-control"
|
||||
<?= isset($field->autocomplete) && is_string($field->autocomplete) ? 'autocomplete="' . e($field->autocomplete) . '"' : '' ?>
|
||||
<?= isset($field->maxlength) && is_numeric($field->maxlength) ? 'maxlength="' . e($field->maxlength) . '"' : '' ?>
|
||||
<?= isset($field->minlength) && is_numeric($field->minlength) ? 'minlength="' . e($field->minlength) . '"' : '' ?>
|
||||
<?= isset($field->pattern) && is_string($field->pattern) ? 'pattern="' . e($field->pattern) . '"' : '' ?>
|
||||
<?= isset($field->placeholder) && is_string($field->placeholder) ? 'placeholder="' . e(trans($field->placeholder)) . '"' : '' ?>
|
||||
<?= isset($field->size) && is_numeric($field->size) ? 'size="' . e($field->size) . '"' : '' ?>
|
||||
<?= $field->getAttributes() ?>
|
||||
<?= $listId ? 'list="' . e($listId) . '"' : '' ?>
|
||||
/>
|
||||
<?php if ($hasOptions): ?>
|
||||
<datalist id="<?= e($listId) ?>">
|
||||
<?php foreach ($fieldOptions as $value => $label): ?>
|
||||
<?php $value = is_int($value) ? $label : $value ?>
|
||||
<option value="<?= e($value) ?>"<?= $value !== $label ? ' label="' . e(trans($label)) . '"' : '' ?>></option>
|
||||
<?php endforeach ?>
|
||||
</datalist>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
5
modules/backend/widgets/form/partials/_field_widget.php
Normal file
5
modules/backend/widgets/form/partials/_field_widget.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<!-- Widget -->
|
||||
<?php
|
||||
$widget = $this->makeFormFieldWidget($field);
|
||||
?>
|
||||
<?= $widget->render() ?>
|
||||
10
modules/backend/widgets/form/partials/_form-container.php
Normal file
10
modules/backend/widgets/form/partials/_form-container.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<div
|
||||
data-control="formwidget"
|
||||
data-refresh-handler="<?= $this->getEventHandler('onRefresh') ?>"
|
||||
class="form-widget form-elements layout"
|
||||
role="form"
|
||||
id="<?= $this->getId() ?>">
|
||||
|
||||
<?= $this->makePartial('form') ?>
|
||||
|
||||
</div>
|
||||
12
modules/backend/widgets/form/partials/_form.php
Normal file
12
modules/backend/widgets/form/partials/_form.php
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
<?php if ($outsideTabs->hasFields()): ?>
|
||||
<?= $this->makePartial('section', ['tabs' => $outsideTabs]) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($primaryTabs->hasFields()): ?>
|
||||
<?= $this->makePartial('section', ['tabs' => $primaryTabs]) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($secondaryTabs->hasFields()): ?>
|
||||
<?= $this->makePartial('section', ['tabs' => $secondaryTabs]) ?>
|
||||
<?php endif ?>
|
||||
3
modules/backend/widgets/form/partials/_form_fields.php
Normal file
3
modules/backend/widgets/form/partials/_form_fields.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php foreach ($fields as $field): ?>
|
||||
<?= $this->makePartial('field-container', ['field' => $field]) ?>
|
||||
<?php endforeach ?>
|
||||
62
modules/backend/widgets/form/partials/_form_tabs.php
Normal file
62
modules/backend/widgets/form/partials/_form_tabs.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
$type = $tabs->section;
|
||||
|
||||
$navCss = '';
|
||||
$contentCss = '';
|
||||
$paneCss = '';
|
||||
|
||||
if ($tabs->stretch) {
|
||||
$navCss = 'layout-row min-size';
|
||||
$contentCss = 'layout-row';
|
||||
$paneCss = 'layout-cell';
|
||||
}
|
||||
?>
|
||||
<div class="<?= $navCss ?>">
|
||||
<ul class="nav nav-tabs" <?= $tabs->linkable ? 'data-linkable' : '' ?>>
|
||||
<?php
|
||||
$index = 0;
|
||||
foreach ($tabs as $name => $fields):
|
||||
$lazy = in_array($name, $tabs->lazy);
|
||||
?>
|
||||
|
||||
<li class="<?= ($index++ === 0) ? 'active' : '' ?> <?= $lazy ? 'tab-lazy' : '' ?>">
|
||||
<a
|
||||
href="#<?= $type . 'tab-' . ($tabs->linkable ? str_slug($name) : $index) ?>"
|
||||
<?php if ($lazy): ?>
|
||||
data-tab-name="<?= e($name) ?>"
|
||||
data-tab-section="<?= $type ?>"
|
||||
data-tab-lazy-handler="<?= $this->getEventHandler('onLazyLoadTab') ?>"
|
||||
<?php endif ?>
|
||||
>
|
||||
<span class="title">
|
||||
<span>
|
||||
<?php if ($tabs->getIcon($name)): ?>
|
||||
<span class="<?= $tabs->getIcon($name) ?>"></span>
|
||||
<?php endif; ?>
|
||||
<?= e(trans($name)) ?>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="tab-content <?= $contentCss ?>">
|
||||
<?php
|
||||
$index = 0;
|
||||
foreach ($tabs as $name => $fields):
|
||||
$lazy = in_array($name, $tabs->lazy);
|
||||
?>
|
||||
|
||||
<div
|
||||
class="tab-pane <?= $lazy ? 'lazy' : '' ?> <?= e($tabs->getPaneCssClass($index, $name)) ?> <?= ($index++ === 0) ? 'active' : '' ?> <?= $paneCss ?>"
|
||||
id="<?= $type . 'tab-' . $index ?>">
|
||||
<?php if ($lazy): ?>
|
||||
<?= $this->makePartial('form_tabs_lazy', ['fields' => $fields]) ?>
|
||||
<?php else: ?>
|
||||
<?= $this->makePartial('form_fields', ['fields' => $fields]) ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
37
modules/backend/widgets/form/partials/_form_tabs_lazy.php
Normal file
37
modules/backend/widgets/form/partials/_form_tabs_lazy.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<div class="loading-indicator-container m-t">
|
||||
<div class="loading-indicator indicator-center">
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
// Do not create a hidden field for these field types since
|
||||
// they don't contain any form data.
|
||||
$ignoredTypes = ['section', 'partial'];
|
||||
|
||||
foreach ($fields as $field):
|
||||
if (in_array($field->type, $ignoredTypes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isMultiValue = is_array($field->value);
|
||||
foreach (array_wrap($field->value) as $index => $value):
|
||||
// Use array field names if the field has multiple values (repeater, checkboxlist, etc.).
|
||||
$fieldName = $isMultiValue ? sprintf('%s[%s]', $field->getName(), $index) : $field->getName();
|
||||
|
||||
$valueIsArray = is_array($value);
|
||||
foreach (array_wrap($value) as $index => $value):
|
||||
// Set the correct array keys if the value is an array (repeater form fields).
|
||||
$currentFieldName = $valueIsArray ? sprintf('%s[%s]', $fieldName, $index) : $fieldName;
|
||||
?>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="<?= $currentFieldName ?>"
|
||||
id="<?= $this->nameToId($currentFieldName) ?>"
|
||||
value="<?= e($value) ?>"
|
||||
<?= $field->getAttributes() ?>
|
||||
/>
|
||||
|
||||
<?php endforeach ?>
|
||||
<?php endforeach ?>
|
||||
<?php endforeach ?>
|
||||
20
modules/backend/widgets/form/partials/_section-container.php
Normal file
20
modules/backend/widgets/form/partials/_section-container.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<div
|
||||
data-control="formwidget"
|
||||
data-refresh-handler="<?= $this->getEventHandler('onRefresh') ?>"
|
||||
class="layout-row"
|
||||
role="form"
|
||||
id="<?= $this->getId($renderSection.'Container') ?>">
|
||||
|
||||
<?php if ($renderSection == 'outside'): ?>
|
||||
<?= $this->makePartial('section', ['tabs' => $outsideTabs]) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($renderSection == 'primary'): ?>
|
||||
<?= $this->makePartial('section', ['tabs' => $primaryTabs]) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($renderSection == 'secondary'): ?>
|
||||
<?= $this->makePartial('section', ['tabs' => $secondaryTabs]) ?>
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
30
modules/backend/widgets/form/partials/_section.php
Normal file
30
modules/backend/widgets/form/partials/_section.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
$type = $tabs->section;
|
||||
|
||||
$containerCss = 'layout-row min-size';
|
||||
|
||||
if ($tabs->stretch) {
|
||||
$containerCss = 'layout-row';
|
||||
}
|
||||
?>
|
||||
<!-- <?= ucfirst($type) ?> Tabs -->
|
||||
<div class="<?= $containerCss ?>">
|
||||
<?php if ($tabs->suppressTabs): ?>
|
||||
|
||||
<div
|
||||
id="<?= $this->getId($type.'Tabs') ?>"
|
||||
class="form-tabless-fields <?= $tabs->cssClass ?>">
|
||||
<?= $this->makePartial('form_fields', ['fields' => $tabs]) ?>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<div
|
||||
id="<?= $this->getId($type.'Tabs') ?>"
|
||||
class="control-tabs <?= $type ?>-tabs layout <?= $tabs->cssClass ?>"
|
||||
data-control="tab">
|
||||
<?= $this->makePartial('form_tabs', ['tabs' => $tabs]) ?>
|
||||
</div>
|
||||
|
||||
<?php endif ?>
|
||||
</div>
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Drag-and-drop reordering styles for the list widget.
|
||||
*/
|
||||
.control-list .list-sort-handle-column,
|
||||
.control-list .list-cell-sort-handle {
|
||||
width: 28px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* The whole handle cell is the drag target (SortableJS handle is the cell), so
|
||||
the grab affordance and hit area cover the entire cell, not just the icon.
|
||||
The second selector is specific enough to override rowlink's
|
||||
"tr.rowlink td.nolink { cursor: auto }" rule, which would otherwise reset the
|
||||
cursor on this (intentionally) non-link cell when the list rows are clickable. */
|
||||
.control-list .list-cell-sort-handle,
|
||||
.control-list tr.rowlink td.list-cell-sort-handle.nolink {
|
||||
cursor: move;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.control-list .list-cell-sort-handle:active,
|
||||
.control-list tr.rowlink td.list-cell-sort-handle.nolink:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.control-list .list-sort-handle {
|
||||
display: inline-block;
|
||||
color: #666;
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.control-list tr:hover .list-sort-handle {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.control-list .list-sortable-ghost {
|
||||
opacity: 0.5;
|
||||
background: #f0f7fd;
|
||||
}
|
||||
|
||||
.control-list .list-sortable-chosen {
|
||||
background: #f7f9fa;
|
||||
}
|
||||
|
||||
.control-list[data-sortable="true"] tbody tr {
|
||||
/* Avoid text selection while dragging rows */
|
||||
user-select: none;
|
||||
}
|
||||
1
modules/backend/widgets/lists/assets/js/dist/winter.list.sortable.js
vendored
Normal file
1
modules/backend/widgets/lists/assets/js/dist/winter.list.sortable.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,99 @@
|
||||
import Sortable from 'sortablejs';
|
||||
|
||||
/*
|
||||
* List widget drag-and-drop reordering.
|
||||
*
|
||||
* Additive enhancement on top of the existing list widget. When a list is rendered with
|
||||
* `data-sortable="true"`, SortableJS is initialised on its <tbody> using the per-row drag
|
||||
* handle cell. On drop, the record ids in their new DOM order are posted to the list's reorder
|
||||
* handler, which assigns the sort order values server-side (by position) and re-renders.
|
||||
*/
|
||||
(function ($) {
|
||||
"use strict";
|
||||
|
||||
function collectIds(tbody) {
|
||||
return Array.prototype.map.call(
|
||||
tbody.querySelectorAll('tr[data-record-id]'),
|
||||
function (tr) {
|
||||
return tr.getAttribute('data-record-id');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function initList(listEl) {
|
||||
var tbody = listEl.querySelector('tbody');
|
||||
if (!tbody || tbody.wnListSortable) {
|
||||
return;
|
||||
}
|
||||
|
||||
var handler = listEl.getAttribute('data-reorder-handler');
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $list = $(listEl);
|
||||
|
||||
// SortableJS dispatches a native "change" event on the list root (the tbody) while
|
||||
// an item is being dragged. Stop it bubbling to the surrounding form's change monitor
|
||||
// so reordering — which persists immediately — does not flag the form as having
|
||||
// unsaved changes. Real form-field changes (target = input/select/textarea) are left
|
||||
// untouched.
|
||||
tbody.addEventListener('change', function (event) {
|
||||
if (event.target === tbody) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
var reorderInFlight = false;
|
||||
|
||||
tbody.wnListSortable = Sortable.create(tbody, {
|
||||
handle: '.list-cell-sort-handle',
|
||||
draggable: 'tr',
|
||||
filter: '.no-data',
|
||||
animation: 150,
|
||||
ghostClass: 'list-sortable-ghost',
|
||||
chosenClass: 'list-sortable-chosen',
|
||||
onEnd: function (event) {
|
||||
// Nothing changed if the row was dropped back in its original position.
|
||||
if (event.oldIndex === event.newIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore further drops until the current reorder has been persisted and the
|
||||
// list re-rendered, so overlapping requests can't race and persist a stale order.
|
||||
if (reorderInFlight) {
|
||||
return;
|
||||
}
|
||||
reorderInFlight = true;
|
||||
|
||||
// The request is fired programmatically (not from a [data-request] element),
|
||||
// so show the stripe load indicator manually for feedback.
|
||||
var indicator = ($.wn && $.wn.stripeLoadIndicator) || ($.oc && $.oc.stripeLoadIndicator);
|
||||
if (indicator) {
|
||||
indicator.show();
|
||||
}
|
||||
|
||||
// Only the record ids (in their new order) are sent; the server assigns the
|
||||
// sort order values by position.
|
||||
$list.request(handler, {
|
||||
data: { record_ids: collectIds(tbody) }
|
||||
}).always(function () {
|
||||
reorderInFlight = false;
|
||||
if (indicator) {
|
||||
indicator.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initAll() {
|
||||
var lists = document.querySelectorAll('[data-control="listwidget"][data-sortable="true"]');
|
||||
Array.prototype.forEach.call(lists, initList);
|
||||
}
|
||||
|
||||
$(document).ready(initAll);
|
||||
|
||||
// Re-initialise after the list partial is replaced by an AJAX update (e.g. onRefresh).
|
||||
$(document).on('render', initAll);
|
||||
})(window.jQuery);
|
||||
166
modules/backend/widgets/lists/assets/js/winter.list.js
Normal file
166
modules/backend/widgets/lists/assets/js/winter.list.js
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* List Widget
|
||||
*
|
||||
* Dependences:
|
||||
* - Row Link Plugin (system/assets/ui/js/list.rowlink.js)
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
var ListWidget = function (element, options) {
|
||||
|
||||
var $el = this.$el = $(element);
|
||||
|
||||
this.options = options || {};
|
||||
|
||||
var scrollClassContainer = options.scrollClassContainer !== undefined
|
||||
? options.scrollClassContainer
|
||||
: $el.parent()
|
||||
|
||||
$el.dragScroll({
|
||||
scrollClassContainer: scrollClassContainer,
|
||||
scrollSelector: 'thead',
|
||||
dragSelector: 'thead'
|
||||
})
|
||||
|
||||
this.update()
|
||||
}
|
||||
|
||||
ListWidget.DEFAULTS = {
|
||||
}
|
||||
|
||||
ListWidget.prototype.update = function() {
|
||||
var
|
||||
list = this.$el,
|
||||
head = $('thead', list),
|
||||
body = $('tbody', list),
|
||||
foot = $('tfoot', list)
|
||||
|
||||
/*
|
||||
* Bind check boxes
|
||||
*/
|
||||
$('.list-checkbox input[type="checkbox"]', body).each(function(){
|
||||
var $el = $(this)
|
||||
if ($el.is(':checked'))
|
||||
$el.closest('tr').addClass('active')
|
||||
})
|
||||
|
||||
head.on('change', '.list-checkbox input[type="checkbox"]', function(){
|
||||
var $el = $(this),
|
||||
checked = $el.is(':checked')
|
||||
|
||||
$('.list-checkbox input[type="checkbox"]', body).prop('checked', checked)
|
||||
if (checked)
|
||||
$('tr', body).addClass('active')
|
||||
else
|
||||
$('tr', body).removeClass('active')
|
||||
})
|
||||
|
||||
body.on('change', '.list-checkbox input[type="checkbox"]', function(){
|
||||
var $el = $(this),
|
||||
checked = $el.is(':checked')
|
||||
|
||||
if (checked) {
|
||||
$el.closest('tr').addClass('active')
|
||||
}
|
||||
else {
|
||||
$('.list-checkbox input[type="checkbox"]', head).prop('checked', false)
|
||||
$el.closest('tr').removeClass('active')
|
||||
}
|
||||
})
|
||||
|
||||
this.lastChecked = null
|
||||
|
||||
body.on('click', '.list-checkbox input[type="checkbox"]', (e) => {
|
||||
const current = e.currentTarget
|
||||
|
||||
if (this.lastChecked && e.shiftKey) {
|
||||
const checkboxes = $('.list-checkbox input[type="checkbox"]', body)
|
||||
const start = checkboxes.index(current)
|
||||
const end = checkboxes.index(this.lastChecked)
|
||||
|
||||
checkboxes
|
||||
.slice(Math.min(start, end), Math.max(start, end) + 1)
|
||||
.each(function () {
|
||||
$(this).prop('checked', current.checked).trigger('change')
|
||||
})
|
||||
}
|
||||
|
||||
this.lastChecked = current
|
||||
})
|
||||
}
|
||||
|
||||
ListWidget.prototype.getChecked = function() {
|
||||
var
|
||||
list = this.$el,
|
||||
body = $('tbody', list)
|
||||
|
||||
return $('.list-checkbox input[type="checkbox"]', body).map(function(){
|
||||
var $el = $(this)
|
||||
if ($el.is(':checked'))
|
||||
return $el.val()
|
||||
}).get();
|
||||
}
|
||||
|
||||
ListWidget.prototype.toggleChecked = function(el) {
|
||||
var $checkbox = $('.list-checkbox input[type="checkbox"]', $(el).closest('tr'))
|
||||
$checkbox.prop('checked', !$checkbox.is(':checked')).trigger('change')
|
||||
}
|
||||
|
||||
// LIST WIDGET PLUGIN DEFINITION
|
||||
// ============================
|
||||
|
||||
var old = $.fn.listWidget
|
||||
|
||||
$.fn.listWidget = function (option) {
|
||||
var args = Array.prototype.slice.call(arguments, 1), result
|
||||
|
||||
this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('oc.listwidget')
|
||||
var options = $.extend({}, ListWidget.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
if (!data) $this.data('oc.listwidget', (data = new ListWidget(this, options)))
|
||||
if (typeof option == 'string') result = data[option].apply(data, args)
|
||||
if (typeof result != 'undefined') return false
|
||||
})
|
||||
|
||||
return result ? result : this
|
||||
}
|
||||
|
||||
$.fn.listWidget.Constructor = ListWidget
|
||||
|
||||
// LIST WIDGET NO CONFLICT
|
||||
// =================
|
||||
|
||||
$.fn.listWidget.noConflict = function () {
|
||||
$.fn.listWidget = old
|
||||
return this
|
||||
}
|
||||
|
||||
// LIST WIDGET HELPERS
|
||||
// =================
|
||||
|
||||
if ($.wn === undefined)
|
||||
$.wn = {}
|
||||
if ($.oc === undefined)
|
||||
$.oc = $.wn
|
||||
|
||||
$.wn.listToggleChecked = function(el) {
|
||||
$(el)
|
||||
.closest('[data-control="listwidget"]')
|
||||
.listWidget('toggleChecked', el)
|
||||
}
|
||||
|
||||
$.wn.listGetChecked = function(el) {
|
||||
return $(el)
|
||||
.closest('[data-control="listwidget"]')
|
||||
.listWidget('getChecked')
|
||||
}
|
||||
|
||||
// LIST WIDGET DATA-API
|
||||
// ==============
|
||||
|
||||
$(document).render(function(){
|
||||
$('[data-control="listwidget"]').listWidget();
|
||||
})
|
||||
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="list-widget list-scrollable-container <?= $cssClasses ?>" id="<?= $this->getId() ?>">
|
||||
<?= $this->makePartial('list') ?>
|
||||
</div>
|
||||
42
modules/backend/widgets/lists/partials/_list.php
Normal file
42
modules/backend/widgets/lists/partials/_list.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<div class="control-list list-scrollable" data-control="listwidget"
|
||||
<?php if ($sortable): ?>
|
||||
data-sortable="true"
|
||||
data-reorder-handler="<?= e($reorderHandler) ?>"
|
||||
<?php endif ?>
|
||||
>
|
||||
<table class="table data" data-control="rowlink">
|
||||
<thead>
|
||||
<?php if ($showTotals): ?>
|
||||
<?= $this->makePartial('list_totals') ?>
|
||||
<?php endif; ?>
|
||||
<?= $this->makePartial('list_head_row') ?>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (count($records)): ?>
|
||||
<?= $this->makePartial('list_body_rows') ?>
|
||||
<?php else: ?>
|
||||
<tr class="no-data">
|
||||
<td colspan="<?= $columnTotal ?>" class="nolink">
|
||||
<p class="no-data"><?= $noRecordsMessage ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif ?>
|
||||
</tbody>
|
||||
<?php if ($showTotals): ?>
|
||||
<tfoot>
|
||||
<?= $this->makePartial('list_totals') ?>
|
||||
</tfoot>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
<?php if ($showPagination): ?>
|
||||
<div class="list-footer">
|
||||
<div class="list-pagination">
|
||||
<?php if ($showPageNumbers): ?>
|
||||
<?= $this->makePartial('list_pagination') ?>
|
||||
<?php else: ?>
|
||||
<?= $this->makePartial('list_pagination_simple') ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
<td class="list-checkbox nolink">
|
||||
<div class="checkbox custom-checkbox nolabel">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="checked[]"
|
||||
id="<?= $this->getId('checkbox-' . $record->getKey()) ?>"
|
||||
value="<?= $record->getKey() ?>"
|
||||
autocomplete="off"/>
|
||||
<label for="<?= $this->getId('checkbox-' . $record->getKey()) ?>"><?= e(trans('backend::lang.list.check')) ?></label>
|
||||
</div>
|
||||
</td>
|
||||
47
modules/backend/widgets/lists/partials/_list_body_row.php
Normal file
47
modules/backend/widgets/lists/partials/_list_body_row.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
$expanded = $showTree ? $this->isTreeNodeExpanded($record) : null;
|
||||
$childRecords = $showTree ? $record->getChildren() : null;
|
||||
$treeLevelClass = $showTree ? 'list-tree-level-'.$treeLevel : '';
|
||||
?>
|
||||
<tr class="<?= $treeLevelClass ?> <?= $this->getRowClass($record) ?>"
|
||||
data-record-id="<?= e($record->getKey()) ?>"
|
||||
>
|
||||
<?php if ($showCheckboxes): ?>
|
||||
<?= $this->makePartial('list_body_checkbox', ['record' => $record]) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($showTree): ?>
|
||||
<?= $this->makePartial('list_body_tree', [
|
||||
'record' => $record,
|
||||
'expanded' => $expanded,
|
||||
'childCount' => $record->getChildCount()
|
||||
]) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (!empty($sortable)): ?>
|
||||
<td class="list-cell-sort-handle nolink">
|
||||
<span class="list-sort-handle" title="<?= e(trans('backend::lang.list.sort_drag_title')) ?>"><i class="icon-bars"></i></span>
|
||||
</td>
|
||||
<?php endif ?>
|
||||
|
||||
<?php $index = $url = 0; foreach ($columns as $key => $column): ?>
|
||||
<?php $index++; ?>
|
||||
<td class="list-cell-index-<?= $index ?> list-cell-name-<?= $column->getName() ?> list-cell-type-<?= $column->type ?> <?= $column->clickable ? '' : 'nolink' ?> <?= $column->getAlignClass() ?> <?= $column->cssClass ?>">
|
||||
<?php if ($column->clickable && !$url && ($url = $this->getRecordUrl($record))): ?>
|
||||
<a <?= $this->getRecordOnClick($record) ?> href="<?= $url ?>">
|
||||
<?= $this->getColumnValue($record, $column) ?>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<?= $this->getColumnValue($record, $column) ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php if ($showSetup): ?>
|
||||
<td class="list-setup"> </td>
|
||||
<?php endif ?>
|
||||
</tr>
|
||||
|
||||
<?php if ($showTree && $expanded): ?>
|
||||
<?= $this->makePartial('list_body_rows', ['records' => $childRecords, 'treeLevel' => $treeLevel+1]) ?>
|
||||
<?php endif ?>
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php foreach ($records as $record): ?>
|
||||
<?= $this->makePartial('list_body_row', ['record' => $record, 'treeLevel' => $treeLevel]) ?>
|
||||
<?php endforeach ?>
|
||||
16
modules/backend/widgets/lists/partials/_list_body_tree.php
Normal file
16
modules/backend/widgets/lists/partials/_list_body_tree.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<td class="list-tree nolink">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="list-expand-collapse"
|
||||
data-request="<?= $this->getEventHandler('onToggleTreeNode') ?>"
|
||||
data-stripe-load-indicator
|
||||
data-request-data="node_id: '<?= $record->getKey() ?>', status: <?= $expanded ? 1 : 0 ?>">
|
||||
<?php if (!$childCount): ?>
|
||||
<i class="icon-square-o"></i>
|
||||
<?php elseif ($expanded): ?>
|
||||
<i class="icon-minus-square-o"></i>
|
||||
<?php else: ?>
|
||||
<i class="icon-plus-square-o"></i>
|
||||
<?php endif ?>
|
||||
</a>
|
||||
</td>
|
||||
58
modules/backend/widgets/lists/partials/_list_head_row.php
Normal file
58
modules/backend/widgets/lists/partials/_list_head_row.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<tr>
|
||||
<?php if ($showCheckboxes && count($records)): ?>
|
||||
<th class="list-checkbox">
|
||||
<div class="checkbox custom-checkbox nolabel">
|
||||
<input type="checkbox" id="<?= $this->getId('checkboxAll') ?>" />
|
||||
<label for="<?= $this->getId('checkboxAll') ?>"></label>
|
||||
</div>
|
||||
</th>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($showTree): ?>
|
||||
<th class="list-tree">
|
||||
<span></span>
|
||||
</th>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (!empty($sortable)): ?>
|
||||
<th class="list-sort-handle-column"><span></span></th>
|
||||
<?php endif ?>
|
||||
|
||||
<?php foreach ($columns as $key => $column): ?>
|
||||
<?php if ($showSorting && $column->sortable): ?>
|
||||
<th
|
||||
<?php if ($column->width): ?>
|
||||
style="width: <?= $column->width ?>"
|
||||
<?php endif ?>
|
||||
class="sortable <?= $this->sortColumn==$column->columnName?'sort-'.$this->sortDirection.' active':'' ?> list-cell-name-<?= $column->getName() ?> list-cell-type-<?= $column->type ?> <?= $column->getAlignClass() ?> <?= $column->headCssClass ?>"
|
||||
>
|
||||
<a
|
||||
href="javascript:;"
|
||||
data-request="<?= $this->getEventHandler('onSort') ?>"
|
||||
data-stripe-load-indicator
|
||||
data-request-data="sortColumn: '<?= $column->columnName ?>', page: <?= $pageCurrent ?>">
|
||||
<?= $this->getHeaderValue($column) ?>
|
||||
</a>
|
||||
</th>
|
||||
<?php else: ?>
|
||||
<th
|
||||
<?php if ($column->width): ?>
|
||||
style="width: <?= $column->width ?>"
|
||||
<?php endif ?>
|
||||
class="list-cell-name-<?= $column->getName() ?> list-cell-type-<?= $column->type ?> <?= $column->getAlignClass() ?> <?= $column->headCssClass ?>"
|
||||
>
|
||||
<span><?= $this->getHeaderValue($column) ?></span>
|
||||
</th>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
|
||||
<?php if ($showSetup): ?>
|
||||
<th class="list-setup">
|
||||
<a href="javascript:;"
|
||||
id="<?= $this->getId('setupButton') ?>"
|
||||
title="<?= e(trans('backend::lang.list.setup_title')) ?>"
|
||||
data-control="popup"
|
||||
data-handler="<?= $this->getEventHandler('onLoadSetup') ?>"></a>
|
||||
</th>
|
||||
<?php endif ?>
|
||||
</tr>
|
||||
74
modules/backend/widgets/lists/partials/_list_pagination.php
Normal file
74
modules/backend/widgets/lists/partials/_list_pagination.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<div class="loading-indicator-container size-small pull-right">
|
||||
<div class="control-pagination loading-indicator-container">
|
||||
<span class="page-iteration">
|
||||
<?= e(trans('backend::lang.list.pagination', ['from' => $pageFrom, 'to' => $pageTo, 'total' => $recordTotal])) ?>
|
||||
</span>
|
||||
<?php if ($pageLast > 1): ?>
|
||||
<?php if ($pageCurrent > 1): ?>
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="page-first"
|
||||
data-request="<?= $this->getEventHandler('onPaginate') ?>"
|
||||
data-request-data="page: 1"
|
||||
data-load-indicator="<?= e(trans('backend::lang.list.loading')) ?>"
|
||||
title="<?= e(trans('backend::lang.list.first_page')) ?>"></a>
|
||||
<?php else: ?>
|
||||
<span
|
||||
class="page-first"
|
||||
title="<?= e(trans('backend::lang.list.first_page')) ?>"></span>
|
||||
<?php endif ?>
|
||||
<?php if ($pageCurrent > 1): ?>
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="page-back"
|
||||
data-request="<?= $this->getEventHandler('onPaginate') ?>"
|
||||
data-request-data="page: <?= $pageCurrent-1 ?>"
|
||||
data-load-indicator="<?= e(trans('backend::lang.list.loading')) ?>"
|
||||
title="<?= e(trans('backend::lang.list.prev_page')) ?>"></a>
|
||||
<?php else: ?>
|
||||
<span
|
||||
class="page-back"
|
||||
title="<?= e(trans('backend::lang.list.prev_page')) ?>"></span>
|
||||
<?php endif ?>
|
||||
<input
|
||||
type="number"
|
||||
name="page"
|
||||
value="<?= $pageCurrent ?>"
|
||||
min="1"
|
||||
step="1"
|
||||
max="<?= $pageLast ?>"
|
||||
class="form-control input-sm"
|
||||
data-request="<?= $this->getEventHandler('onPaginate') ?>"
|
||||
data-track-input
|
||||
data-load-indicator="<?= e(trans('backend::lang.list.loading')) ?>"
|
||||
autocomplete="off"
|
||||
style="width: auto; padding-left: 5px; padding-right: 0; display: inline; text-align: center;" />
|
||||
<?php if ($pageLast > $pageCurrent): ?>
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="page-next"
|
||||
data-request-data="page: <?= $pageCurrent+1 ?>"
|
||||
data-request="<?= $this->getEventHandler('onPaginate') ?>"
|
||||
data-load-indicator="<?= e(trans('backend::lang.list.loading')) ?>"
|
||||
title="<?= e(trans('backend::lang.list.next_page')) ?>"></a>
|
||||
<?php else: ?>
|
||||
<span
|
||||
class="page-next"
|
||||
title="<?= e(trans('backend::lang.list.next_page')) ?>"></span>
|
||||
<?php endif ?>
|
||||
<?php if ($pageLast > $pageCurrent): ?>
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="page-last"
|
||||
data-request-data="page: <?= $pageLast ?>"
|
||||
data-request="<?= $this->getEventHandler('onPaginate') ?>"
|
||||
data-load-indicator="<?= e(trans('backend::lang.list.loading')) ?>"
|
||||
title="<?= e(trans('backend::lang.list.last_page')) ?>"></a>
|
||||
<?php else: ?>
|
||||
<span
|
||||
class="page-last"
|
||||
title="<?= e(trans('backend::lang.list.last_page')) ?>"></span>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,50 @@
|
||||
<div class="loading-indicator-container size-small pull-right">
|
||||
<div class="control-pagination">
|
||||
<?php if ($pageCurrent > 1): ?>
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="page-first"
|
||||
data-request="<?= $this->getEventHandler('onPaginate') ?>"
|
||||
data-request-data="page: 1"
|
||||
data-load-indicator="<?= e(trans('backend::lang.list.loading')) ?>"
|
||||
title="<?= e(trans('backend::lang.list.first_page')) ?>"></a>
|
||||
<?php else: ?>
|
||||
<span
|
||||
class="page-first"
|
||||
title="<?= e(trans('backend::lang.list.first_page')) ?>"></span>
|
||||
<?php endif ?>
|
||||
<?php if ($pageCurrent > 1): ?>
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="page-back"
|
||||
data-request="<?= $this->getEventHandler('onPaginate') ?>"
|
||||
data-request-data="page: <?= $pageCurrent-1 ?>"
|
||||
data-load-indicator="<?= e(trans('backend::lang.list.loading')) ?>"
|
||||
title="<?= e(trans('backend::lang.list.prev_page')) ?>"></a>
|
||||
<?php else: ?>
|
||||
<span
|
||||
class="page-back"
|
||||
title="<?= e(trans('backend::lang.list.prev_page')) ?>"></span>
|
||||
<?php endif ?>
|
||||
<select
|
||||
disabled
|
||||
name="page"
|
||||
class="form-control input-sm custom-select select-no-search"
|
||||
autocomplete="off">
|
||||
<option value="<?= $pageCurrent ?>" selected><?= $pageCurrent ?></option>
|
||||
</select>
|
||||
<?php if ($hasMorePages): ?>
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="page-next"
|
||||
data-request-data="page: <?= $pageCurrent+1 ?>"
|
||||
data-request="<?= $this->getEventHandler('onPaginate') ?>"
|
||||
data-load-indicator="<?= e(trans('backend::lang.list.loading')) ?>"
|
||||
title="<?= e(trans('backend::lang.list.next_page')) ?>"></a>
|
||||
<?php else: ?>
|
||||
<span
|
||||
class="page-next"
|
||||
title="<?= e(trans('backend::lang.list.next_page')) ?>"></span>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
26
modules/backend/widgets/lists/partials/_list_totals.php
Normal file
26
modules/backend/widgets/lists/partials/_list_totals.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<tr class="table-totals">
|
||||
<?php if ($showCheckboxes): ?>
|
||||
<td></td>
|
||||
<?php endif ?>
|
||||
<?php if ($showTree): ?>
|
||||
<td class="list-tree">
|
||||
<span></span>
|
||||
</td>
|
||||
<?php endif ?>
|
||||
<?php foreach ($columns as $column): ?>
|
||||
<td>
|
||||
<?php if ($column->type == 'number' && $column->summable): ?>
|
||||
<?php $item = $sums[$column->columnName]; ?>
|
||||
<span>
|
||||
<?= $item['format'] ? sprintf($item['format'], $item['sum']) : number_format($item['sum'], 0, '.', ',') ?>
|
||||
<?php if (!is_null($item['total'])): ?>
|
||||
(<?= $item['format'] ? sprintf($item['format'], $item['total']) : number_format($item['total'], 0, '.', ',') ?>)
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
<?php endforeach ?>
|
||||
<?php if ($showSetup): ?>
|
||||
<td></td>
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
74
modules/backend/widgets/lists/partials/_setup_form.php
Normal file
74
modules/backend/widgets/lists/partials/_setup_form.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?= Form::open(['data-request-parent' => '#' . $this->getId('setupButton')]) ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('backend::lang.list.setup_title')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="help-block before-field"><?= e(trans('backend::lang.list.setup_help')) ?></p>
|
||||
|
||||
<div class="control-simplelist with-checkboxes is-sortable" data-control="simplelist">
|
||||
<ul>
|
||||
<?php foreach ($columns as $key => $column): ?>
|
||||
<li>
|
||||
<div class="checkbox custom-checkbox">
|
||||
<input
|
||||
type="hidden"
|
||||
name="column_order[]"
|
||||
value="<?= e($column->columnName) ?>" />
|
||||
<input
|
||||
id="<?= $this->getId('setupCheckbox-'.$column->columnName) ?>"
|
||||
name="visible_columns[]"
|
||||
value="<?= e($column->columnName) ?>"
|
||||
<?= $column->invisible ? '' : 'checked="checked"' ?>
|
||||
type="checkbox" />
|
||||
<label
|
||||
class="choice"
|
||||
for="<?= $this->getId('setupCheckbox-'.$column->columnName) ?>">
|
||||
<?= e(trans($column->label)) ?>
|
||||
</label>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php if ($this->showPagination): ?>
|
||||
<div class="form-group">
|
||||
<label><?= e(trans('backend::lang.list.records_per_page')) ?></label>
|
||||
<p class="help-block before-field">
|
||||
<?= e(trans('backend::lang.list.records_per_page_help')) ?>
|
||||
</p>
|
||||
<select class="form-control custom-select select-no-search" name="records_per_page">
|
||||
<?php foreach ($perPageOptions as $optionValue): ?>
|
||||
<option value="<?= $optionValue ?>" <?= $optionValue == $recordsPerPage ? 'selected="selected"' : '' ?>><?= $optionValue ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-link pull-left"
|
||||
data-request="<?= $this->getEventHandler('onResetSetup') ?>"
|
||||
data-dismiss="popup"
|
||||
data-stripe-load-indicator>
|
||||
<?= e(trans('backend::lang.form.reset_default')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
data-request="<?= $this->getEventHandler('onApplySetup') ?>"
|
||||
data-dismiss="popup"
|
||||
data-stripe-load-indicator>
|
||||
<?= e(trans('backend::lang.form.apply')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
<?= Form::close() ?>
|
||||
169
modules/backend/widgets/mediamanager/assets/css/mediamanager.css
Normal file
169
modules/backend/widgets/mediamanager/assets/css/mediamanager.css
Normal file
@@ -0,0 +1,169 @@
|
||||
div[data-control="media-manager"]:focus{outline:none}
|
||||
div[data-control="media-manager"] audio,
|
||||
div[data-control="media-manager"] video{width:100%}
|
||||
div[data-control="media-manager"] video{background:#ecf0f1;max-height:225px}
|
||||
div[data-control="media-manager"] .file-icon{fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;display:inline-block}
|
||||
div[data-control="media-manager"] .file-icon-extension{font-family:'ArialMT','Arial',sans-serif;font-size:4em;font-weight:900;fill:#fff}
|
||||
div[data-control="media-manager"] .file-icon-label{fill:#576D7E;fill-rule:nonzero}
|
||||
div[data-control="media-manager"] .file-icon-css,
|
||||
div[data-control="media-manager"] .file-icon-less,
|
||||
div[data-control="media-manager"] .file-icon-scss{fill:#B73FD9}
|
||||
div[data-control="media-manager"] .file-icon-html,
|
||||
div[data-control="media-manager"] .file-icon-xml{fill:#EA9B47}
|
||||
div[data-control="media-manager"] .file-icon-js,
|
||||
div[data-control="media-manager"] .file-icon-json{fill:#A9A9A9}
|
||||
div[data-control="media-manager"] .file-icon-pdf{fill:#E30713}
|
||||
div[data-control="media-manager"] .file-icon-txt{fill:#248BD0}
|
||||
div[data-control="media-manager"] .file-icon-ai{fill:#F29200}
|
||||
div[data-control="media-manager"] .file-icon-eps{fill:#F9B234}
|
||||
div[data-control="media-manager"] .file-icon-psd{fill:#2DAAE2}
|
||||
div[data-control="media-manager"] .file-icon-ttf,
|
||||
div[data-control="media-manager"] .file-icon-otf,
|
||||
div[data-control="media-manager"] .file-icon-woff,
|
||||
div[data-control="media-manager"] .file-icon-woff2{fill:#C4CA10}
|
||||
div[data-control="media-manager"] .file-icon-doc,
|
||||
div[data-control="media-manager"] .file-icon-docx,
|
||||
div[data-control="media-manager"] .file-icon-rtf,
|
||||
div[data-control="media-manager"] .file-icon-odt{fill:#0F70B7}
|
||||
div[data-control="media-manager"] .file-icon-csv,
|
||||
div[data-control="media-manager"] .file-icon-ods,
|
||||
div[data-control="media-manager"] .file-icon-xls,
|
||||
div[data-control="media-manager"] .file-icon-xlsx{fill:#3BAA34}
|
||||
div[data-control="media-manager"] .file-icon-odp,
|
||||
div[data-control="media-manager"] .file-icon-ppt,
|
||||
div[data-control="media-manager"] .file-icon-pptx{fill:#D04526}
|
||||
div[data-control="media-manager"] .file-icon-rar,
|
||||
div[data-control="media-manager"] .file-icon-tar,
|
||||
div[data-control="media-manager"] .file-icon-zip{fill:#363A56}
|
||||
div[data-control="media-manager"] .media-player-fallback{font-size:13px;color:#95a5a6;background:#ecf0f1;line-height:180%}
|
||||
div[data-control="media-manager"] .media-player-fallback.panel-embedded{padding:20px;margin:-20px -20px 0 -20px}
|
||||
div[data-control="media-manager"] .empty-library{padding:20px;text-align:center}
|
||||
div[data-control="media-manager"] p.thumbnail-error-message{font-size:12px;margin:10px;line-height:160%;color:#bdc3c7}
|
||||
div[data-control="media-manager"] .media-list{padding:0 0 0 20px;margin:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
div[data-control="media-manager"] .media-list li{display:inline-block;vertical-align:top;margin:0 20px 20px 0;overflow:hidden;cursor:pointer;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}
|
||||
div[data-control="media-manager"] .media-list li:focus{outline:none}
|
||||
div[data-control="media-manager"] .media-list li .icon-container{display:table}
|
||||
div[data-control="media-manager"] .media-list li .icon-container i{color:#95a5a6;display:inline-block}
|
||||
div[data-control="media-manager"] .media-list li .icon-container div{display:table-cell;text-align:center;vertical-align:middle}
|
||||
div[data-control="media-manager"] .media-list li .icon-container.image>div.icon-wrapper{display:none}
|
||||
div[data-control="media-manager"] .media-list li h4{font-weight:600;font-size:13px;color:#2b3e50;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:150%;margin:15px 0 5px 0;padding-right:0;-webkit-transition:padding 0.1s;transition:padding 0.1s;position:relative}
|
||||
div[data-control="media-manager"] .media-list li h4 a{position:absolute;right:0;top:0;font-size:15px;color:#2b3e50;display:none}
|
||||
div[data-control="media-manager"] .media-list li h4 a:hover{color:#2da7c7;text-decoration:none}
|
||||
div[data-control="media-manager"] .media-list li p.size{font-size:12px;color:#95a5a6}
|
||||
div[data-control="media-manager"] .media-list li .image-placeholder{position:relative}
|
||||
div[data-control="media-manager"] .media-list li .image-placeholder i{padding-top:0;padding-left:2px}
|
||||
div[data-control="media-manager"] .media-list li .image-placeholder[data-loading] i{display:none}
|
||||
div[data-control="media-manager"] .media-list li .image-placeholder[data-loading]:after{background-image:url('../../../../../../modules/system/assets/ui/images/loader-transparent.svg');background-position:50% 50%;content:' ';-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite;background-size:28px 28px;position:absolute;width:28px;height:28px;top:50%;left:50%;margin-top:-14px;margin-left:-14px}
|
||||
div[data-control="media-manager"] .media-list li i.icon-chain-broken{padding:0;color:#bdc3c7}
|
||||
div[data-control="media-manager"] .media-list li[data-item-type=folder] i{color:#48b2ce}
|
||||
div[data-control="media-manager"] .media-list.list li{height:75px;width:260px;border:1px solid #ecf0f1;background:#f6f8f9;box-sizing:content-box}
|
||||
div[data-control="media-manager"] .media-list.list li .icon-container{border-right:1px solid #f6f8f9;width:75px;height:75px;float:left}
|
||||
div[data-control="media-manager"] .media-list.list li .icon-container img{max-height:75px}
|
||||
div[data-control="media-manager"] .media-list.list li .icon-container i{font-size:35px}
|
||||
div[data-control="media-manager"] .media-list.list li .icon-container svg{max-height:44px}
|
||||
div[data-control="media-manager"] .media-list.list li .icon-container.image{border-right:1px solid #ecf0f1 !important}
|
||||
div[data-control="media-manager"] .media-list.list li .icon-container p.thumbnail-error-message{display:none}
|
||||
div[data-control="media-manager"] .media-list.list .icon-wrapper{width:75px}
|
||||
div[data-control="media-manager"] .media-list.list li .info{margin-left:90px}
|
||||
div[data-control="media-manager"] .media-list.list li .image-placeholder{width:75px;height:75px}
|
||||
div[data-control="media-manager"] .media-list.list li[data-root] h4{margin-top:27px}
|
||||
div[data-control="media-manager"] .media-list.list li.selected{background:#48b2ce !important}
|
||||
div[data-control="media-manager"] .media-list.list li.selected i,
|
||||
div[data-control="media-manager"] .media-list.list li.selected p.size{color:#ecf0f1}
|
||||
div[data-control="media-manager"] .media-list.list li.selected h4{color:white}
|
||||
div[data-control="media-manager"] .media-list.list li.selected .icon-container{border-right-color:#48b2ce !important}
|
||||
div[data-control="media-manager"] .media-list.list h4{padding-right:15px}
|
||||
div[data-control="media-manager"] .media-list.list h4 a{right:15px}
|
||||
div[data-control="media-manager"] .media-list.tiles li{width:167px;margin-bottom:25px}
|
||||
div[data-control="media-manager"] .media-list.tiles .icon-wrapper{width:167px}
|
||||
div[data-control="media-manager"] .media-list.tiles li .image-placeholder{width:165px;height:165px}
|
||||
div[data-control="media-manager"] .media-list.tiles li .image-placeholder[data-loading]:after{background-image:url('../../../../../../modules/system/assets/ui/images/loader-transparent.svg');background-position:50% 50%;content:' ';-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite;background-size:55px 55px;position:absolute;width:55px;height:55px;top:50%;left:50%;margin-top:-27.5px;margin-left:-27.5px}
|
||||
div[data-control="media-manager"] .media-list.tiles li .icon-container{width:165px;height:165px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;border:1px solid #ecf0f1;overflow:hidden;background:#f6f8f9;box-sizing:content-box}
|
||||
div[data-control="media-manager"] .media-list.tiles li .icon-container img{max-height:165px}
|
||||
div[data-control="media-manager"] .media-list.tiles li .icon-container i{font-size:55px}
|
||||
div[data-control="media-manager"] .media-list.tiles li .icon-container svg{max-height:65px}
|
||||
div[data-control="media-manager"] .media-list.tiles li .icon-container p{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"}
|
||||
div[data-control="media-manager"] .media-list.tiles li.selected .icon-container{background:#48b2ce !important;border-color:#2581b8}
|
||||
div[data-control="media-manager"] .media-list.tiles li.selected .icon-container i,
|
||||
div[data-control="media-manager"] .media-list.tiles li.selected .icon-container p{color:#ecf0f1}
|
||||
div[data-control="media-manager"] .media-list.tiles li.selected h4{color:#2581b8}
|
||||
div[data-control="media-manager"] .media-list.tiles i.icon-chain-broken{margin-top:47px}
|
||||
div[data-control="media-manager"] .media-list.tiles p.size{margin-bottom:0}
|
||||
div[data-control="media-manager"] [data-control="sidebar-labels"]{word-wrap:break-word}
|
||||
div[data-control="media-manager"] .sidebar-group{margin-bottom:20px}
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder-container,
|
||||
div[data-control="media-manager"] .sidebar-document-placeholder-container{display:table;width:100%}
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder,
|
||||
div[data-control="media-manager"] .sidebar-document-placeholder{display:table-cell;position:relative;vertical-align:middle;text-align:center;border-bottom:1px solid #ecf0f1;box-sizing:content-box}
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder{height:225px}
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder[data-loading]{background:#ecf0f1}
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder[data-loading]:after{background-image:url('../../../../../../modules/system/assets/ui/images/loader-transparent.svg');background-position:50% 50%;content:' ';-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite;background-size:62px 62px;position:absolute;width:62px;height:62px;top:50%;left:50%;margin-top:-31px;margin-left:-31px}
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder i.icon-chain-broken,
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder i.icon-crop,
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder i.icon-asterisk,
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder i.icon-level-up{color:#bdc3c7;font-size:55px}
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder.no-border{border-bottom:none}
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder p{font-size:12px;margin:10px;line-height:160%;color:#bdc3c7;margin-top:25px}
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder img{max-width:100%;max-height:225px}
|
||||
div[data-control="media-manager"] .sidebar-document-placeholder{height:155px}
|
||||
div[data-control="media-manager"] .sidebar-document-placeholder svg{width:100px;height:100px}
|
||||
div[data-control="media-manager"] .list-container{position:relative;z-index:100}
|
||||
div[data-control="media-manager"] .list-container .no-data{font-size:13px}
|
||||
div[data-control="media-manager"] .list-container p.no-data{padding:0 20px 20px 20px}
|
||||
div[data-control="media-manager"] .list-container li.no-data{padding-top:20px;display:block !important;width:100% !important;border:none !important;background:transparent !important;cursor:default !important}
|
||||
div[data-control="media-manager"] .list-container table.table.data tbody tr:not(.no-data):active td{background:#48b2ce !important}
|
||||
div[data-control="media-manager"] [data-control="item-list"]{position:relative;display:table-cell}
|
||||
div[data-control="media-manager"] .control-scrollpad{position:absolute;left:0;top:0;min-height:300px}
|
||||
div[data-control="media-manager"] .scroll-wrapper{position:relative}
|
||||
div[data-control="media-manager"] table.table{table-layout:fixed;margin-bottom:0;white-space:nowrap}
|
||||
div[data-control="media-manager"] table.table div.no-wrap-text{overflow:hidden;text-overflow:ellipsis}
|
||||
div[data-control="media-manager"] table.table div.item-title{position:relative;padding-right:0;-webkit-transition:padding 0.1s;transition:padding 0.1s}
|
||||
div[data-control="media-manager"] table.table div.item-title a{position:absolute;right:0;top:0;display:none}
|
||||
div[data-control="media-manager"] table.table tr:hover div.item-title{padding-right:25px}
|
||||
div[data-control="media-manager"] table.table tr:hover div.item-title a{display:block}
|
||||
div[data-control="media-manager"] table.table tr[data-item-type=folder] i.icon-folder{color:#48b2ce}
|
||||
div[data-control="media-manager"] table.table tr:focus{outline:none}
|
||||
div[data-control="media-manager"] div[data-control="selection-marker"]{position:absolute;z-index:250;border:1px dashed #95a5a6;background:rgba(0,0,0,0.1)}
|
||||
div[data-control="media-manager"] .upload-progress{background:#f9f9f9;padding:0 20px}
|
||||
div[data-control="media-manager"] .upload-progress h5{margin:0 0 10px 0;font-size:13px;color:#2b3e50;font-weight:600}
|
||||
div[data-control="media-manager"] .upload-progress h5 span{display:inline-block;margin-left:10px;color:#95a5a6;font-size:15px}
|
||||
div[data-control="media-manager"] .upload-progress .progress-controls{padding-right:30px;position:relative}
|
||||
div[data-control="media-manager"] .upload-progress .progress-controls .controls{position:absolute;right:0;bottom:0}
|
||||
div[data-control="media-manager"] .upload-progress .progress-controls .controls a{display:block;position:relative;top:7px;right:3px;color:#95a5a6;font-size:16px;cursor:pointer!important}
|
||||
div[data-control="media-manager"] .upload-progress .progress-controls .controls a:hover{text-decoration:none;color:#2da7c7}
|
||||
div[data-control="media-manager"] .dz-preview{display:none}
|
||||
div[data-control="media-manager"] button[data-command="toggle-sidebar"].sidebar-hidden{-webkit-transform:rotate(180deg) translate(0,0);-ms-transform:rotate(180deg) translate(0,0);transform:rotate(180deg) translate(0,0)}
|
||||
[data-control="media-manager-crop-tool"] .image_area{position:absolute;width:100%;height:100%;overflow:auto}
|
||||
[data-control="media-manager-crop-tool"] .image_area .jcrop-holder{background-color:transparent!important}
|
||||
[data-control="media-manager-crop-tool"] img{cursor:crosshair;display:block}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler-container .layout-relative{overflow:hidden}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler-container.horizontal .layout-cell{height:20px}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler-container.horizontal .layout-relative{width:100%}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler-container.vertical{width:20px}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler-container.vertical .layout-relative{height:100%}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler{position:absolute;height:20px;margin-left:-3px;background:#555}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler ul{margin:0;padding:0;white-space:nowrap;font-size:0}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler li{margin:0;padding:0 0 0 40px;list-style:none;display:inline-block;width:24px;margin:0 -10px 0 -14px;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;text-align:left;position:relative;font-size:10px;line-height:20px;color:#ecf0f1;font-family:Arial,sans-serif}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler li:before,
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler li:after{content:' ';position:absolute;border-left:1px solid #8e8e8e}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler li:before{height:20px;top:0;left:-3px}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler li:after{height:3px;bottom:0;left:20px}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler li:first-child:after{display:none}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler[data-control=v-ruler]{-webkit-transform:rotateZ(90deg);-ms-transform:rotateZ(90deg);transform:rotateZ(90deg);-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;transform-origin:left top;left:23px;top:-23px}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler[data-control=v-ruler] li:after{top:0;left:auto}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hover .icon-container{background:#48b2ce !important;border-color:#2581b8}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hover .icon-container i,
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hover .icon-container p{color:#ecf0f1}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hover h4{color:#2581b8}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hover h4{padding-right:20px !important}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover{background:#48b2ce !important}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover i,
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover p.size{color:#ecf0f1}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover h4{color:white}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover .icon-container{border-right-color:#48b2ce !important}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover h4{padding-right:35px !important}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list li:hover h4 a{display:block}
|
||||
@media (max-width:1280px){div[data-control="media-manager"] .media-list.list li{width:230px}}
|
||||
@media (max-width:1024px){div[data-control="media-manager"] .media-list.list li{display:block;width:auto}}
|
||||
@media (max-width:768px){div[data-control="media-manager"] [data-control="preview-sidebar"],div[data-control="media-manager"] [data-command="toggle-sidebar"]{display:none!important}div[data-control="media-manager"] .media-list.list{padding:0}div[data-control="media-manager"] .media-list.list li{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;margin:0;border-right:none;border-left:none;border-bottom:none}}
|
||||
@media (max-width:480px){div[data-control="media-manager"] [data-control="left-sidebar"]{display:none!important}}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
543
modules/backend/widgets/mediamanager/assets/js/mediamanager-browser-min.js
vendored
Normal file
543
modules/backend/widgets/mediamanager/assets/js/mediamanager-browser-min.js
vendored
Normal file
@@ -0,0 +1,543 @@
|
||||
+function($){"use strict";if($.wn.mediaManager===undefined)$.wn.mediaManager={}
|
||||
var Base=$.wn.foundation.base,BaseProto=Base.prototype
|
||||
var MediaManager=function(element,options){this.$el=$(element)
|
||||
this.$form=this.$el.closest('form')
|
||||
this.options=options
|
||||
Base.call(this)
|
||||
this.selectTimer=null
|
||||
this.sidebarPreviewElement=null
|
||||
this.itemListElement=null
|
||||
this.scrollContentElement=null
|
||||
this.thumbnailQueue=[]
|
||||
this.activeThumbnailQueueLength=0
|
||||
this.sidebarThumbnailAjax=null
|
||||
this.selectionMarker=null
|
||||
this.dropzone=null
|
||||
this.searchTrackInputTimer=null
|
||||
this.navigationAjax=null
|
||||
this.dblTouchTimer=null
|
||||
this.dblTouchFlag=null
|
||||
this.itemListPosition=null
|
||||
this.init()}
|
||||
MediaManager.prototype=Object.create(BaseProto)
|
||||
MediaManager.prototype.constructor=MediaManager
|
||||
MediaManager.prototype.dispose=function(){this.unregisterHandlers()
|
||||
this.clearSelectTimer()
|
||||
this.destroyUploader()
|
||||
this.clearSearchTrackInputTimer()
|
||||
this.releaseNavigationAjax()
|
||||
this.clearDblTouchTimer()
|
||||
this.removeAttachedControls()
|
||||
this.removeScroll()
|
||||
this.$el.removeData('oc.mediaManager')
|
||||
this.$el=null
|
||||
this.$form=null
|
||||
this.sidebarPreviewElement=null
|
||||
this.itemListElement=null
|
||||
this.scrollContentElement=null
|
||||
this.sidebarThumbnailAjax=null
|
||||
this.selectionMarker=null
|
||||
this.thumbnailQueue=[]
|
||||
this.navigationAjax=null
|
||||
BaseProto.dispose.call(this)}
|
||||
MediaManager.prototype.getSelectedItems=function(returnNotProcessed,allowRootItem){var items=this.$el.get(0).querySelectorAll('[data-type="media-item"].selected'),result=[]
|
||||
if(!allowRootItem){var filteredItems=[]
|
||||
for(var i=0,len=items.length;i<len;i++){var item=items[i]
|
||||
if(!item.hasAttribute('data-root'))filteredItems.push(item)}items=filteredItems}if(returnNotProcessed===true)return items
|
||||
for(var i=0,len=items.length;i<len;i++){var item=items[i],itemDetails={itemType:item.getAttribute('data-item-type'),path:item.getAttribute('data-path'),title:item.getAttribute('data-title'),sizeBytes:item.getAttribute('data-size-bytes'),lastModified:item.getAttribute('data-last-modified-ts'),documentType:item.getAttribute('data-document-type'),folder:item.getAttribute('data-folder'),publicUrl:item.getAttribute('data-public-url')}
|
||||
result.push(itemDetails)}return result}
|
||||
MediaManager.prototype.init=function(){this.itemListElement=this.$el.find('[data-control="item-list"]').get(0)
|
||||
this.scrollContentElement=this.itemListElement.querySelector('.scroll-wrapper')
|
||||
if(this.options.bottomToolbar){this.$el.find('[data-control="bottom-toolbar"]').removeClass('hide')
|
||||
if(this.options.cropAndInsertButton)this.$el.find('[data-popup-command="crop-and-insert"]').removeClass('hide')}this.registerHandlers()
|
||||
this.updateSidebarPreview()
|
||||
this.generateThumbnails()
|
||||
this.initUploader()
|
||||
this.initScroll()}
|
||||
MediaManager.prototype.registerHandlers=function(){this.$el.on('dblclick',this.proxy(this.onNavigate))
|
||||
this.$el.on('click.tree-path','ul.tree-path, [data-control="sidebar-labels"]',this.proxy(this.onNavigate))
|
||||
this.$el.on('click.command','[data-command]',this.proxy(this.onCommandClick))
|
||||
this.$el.on('click.item','[data-type="media-item"]',this.proxy(this.onItemClick))
|
||||
this.$el.on('touchend','[data-type="media-item"]',this.proxy(this.onItemTouch))
|
||||
this.$el.on('change','[data-control="sorting"]',this.proxy(this.onSortingChanged))
|
||||
this.$el.on('input','[data-control="search"]',this.proxy(this.onSearchChanged))
|
||||
this.$el.on('mediarefresh',this.proxy(this.refresh))
|
||||
this.$el.on('shown.oc.popup','[data-command="create-folder"]',this.proxy(this.onFolderPopupShown))
|
||||
this.$el.on('hidden.oc.popup','[data-command="create-folder"]',this.proxy(this.onFolderPopupHidden))
|
||||
this.$el.on('shown.oc.popup','[data-command="move"]',this.proxy(this.onMovePopupShown))
|
||||
this.$el.on('hidden.oc.popup','[data-command="move"]',this.proxy(this.onMovePopupHidden))
|
||||
this.$el.on('keydown',this.proxy(this.onKeyDown))
|
||||
if(this.itemListElement)this.itemListElement.addEventListener('mousedown',this.proxy(this.onListMouseDown))}
|
||||
MediaManager.prototype.unregisterHandlers=function(){this.$el.off('dblclick',this.proxy(this.onNavigate))
|
||||
this.$el.off('click.tree-path',this.proxy(this.onNavigate))
|
||||
this.$el.off('click.command',this.proxy(this.onCommandClick))
|
||||
this.$el.off('click.item',this.proxy(this.onItemClick))
|
||||
this.$el.off('touchend','[data-type="media-item"]',this.proxy(this.onItemTouch))
|
||||
this.$el.off('change','[data-control="sorting"]',this.proxy(this.onSortingChanged))
|
||||
this.$el.off('keyup','[data-control="search"]',this.proxy(this.onSearchChanged))
|
||||
this.$el.off('shown.oc.popup','[data-command="create-folder"]',this.proxy(this.onFolderPopupShown))
|
||||
this.$el.off('hidden.oc.popup','[data-command="create-folder"]',this.proxy(this.onFolderPopupHidden))
|
||||
this.$el.off('shown.oc.popup','[data-command="move"]',this.proxy(this.onMovePopupShown))
|
||||
this.$el.off('hidden.oc.popup','[data-command="move"]',this.proxy(this.onMovePopupHidden))
|
||||
this.$el.off('keydown',this.proxy(this.onKeyDown))
|
||||
if(this.itemListElement){this.itemListElement.removeEventListener('mousedown',this.proxy(this.onListMouseDown))
|
||||
this.itemListElement.removeEventListener('mousemove',this.proxy(this.onListMouseMove))}document.removeEventListener('mouseup',this.proxy(this.onListMouseUp))}
|
||||
MediaManager.prototype.changeView=function(view){var data={view:view,path:this.$el.find('[data-type="current-folder"]').val()}
|
||||
this.execNavigationRequest('onChangeView',data)}
|
||||
MediaManager.prototype.setFilter=function(filter){var data={filter:filter,path:this.$el.find('[data-type="current-folder"]').val()}
|
||||
this.execNavigationRequest('onSetFilter',data)}
|
||||
MediaManager.prototype.isSearchMode=function(){return this.$el.find('[data-type="search-mode"]').val()=='true'}
|
||||
MediaManager.prototype.initScroll=function(){this.$el.find('.control-scrollpad').scrollpad()}
|
||||
MediaManager.prototype.updateScroll=function(){this.$el.find('.control-scrollpad').scrollpad('update')}
|
||||
MediaManager.prototype.removeScroll=function(){this.$el.find('.control-scrollpad').scrollpad('dispose')}
|
||||
MediaManager.prototype.scrollToTop=function(){this.$el.find('.control-scrollpad').scrollpad('scrollToStart')}
|
||||
MediaManager.prototype.removeAttachedControls=function(){this.$el.find('[data-control=toolbar]').toolbar('dispose')
|
||||
this.$el.find('[data-control=sorting]').select2('destroy')}
|
||||
MediaManager.prototype.clearSelectTimer=function(){if(this.selectTimer===null)return
|
||||
clearTimeout(this.selectTimer)
|
||||
this.selectTimer=null}
|
||||
MediaManager.prototype.selectItem=function(node,expandSelection){if(!expandSelection){var items=this.$el.get(0).querySelectorAll('[data-type="media-item"].selected')
|
||||
for(var i=0,len=items.length;i<len;i++){items[i].setAttribute('class','')}node.setAttribute('class','selected')}else{if(node.getAttribute('class')=='selected')node.setAttribute('class','')
|
||||
else node.setAttribute('class','selected')}node.focus()
|
||||
this.clearSelectTimer()
|
||||
if(this.isPreviewSidebarVisible()){this.selectTimer=setTimeout(this.proxy(this.updateSidebarPreview),100)}if(node.hasAttribute('data-root')&&!expandSelection){this.toggleMoveAndDelete(true)}else{this.toggleMoveAndDelete(false)}if(expandSelection){this.unselectRoot()}}
|
||||
MediaManager.prototype.toggleMoveAndDelete=function(value){$('[data-command=delete]',this.$el).prop('disabled',value)
|
||||
$('[data-command=move]',this.$el).prop('disabled',value)}
|
||||
MediaManager.prototype.unselectRoot=function(){var rootItem=this.$el.get(0).querySelector('[data-type="media-item"][data-root].selected')
|
||||
if(rootItem)rootItem.setAttribute('class','')}
|
||||
MediaManager.prototype.clearDblTouchTimer=function(){if(this.dblTouchTimer===null)return
|
||||
clearTimeout(this.dblTouchTimer)
|
||||
this.dblTouchTimer=null}
|
||||
MediaManager.prototype.clearDblTouchFlag=function(){this.dblTouchFlag=false}
|
||||
MediaManager.prototype.selectFirstItem=function(){var firstItem=this.itemListElement.querySelector('[data-type="media-item"]:first-child')
|
||||
if(firstItem){this.selectItem(firstItem)}}
|
||||
MediaManager.prototype.selectRelative=function(next,expandSelection){var currentSelection=this.getSelectedItems(true,true)
|
||||
if(currentSelection.length==0){this.selectFirstItem()
|
||||
return}var itemToSelect=null
|
||||
if(next){var lastItem=currentSelection[currentSelection.length-1]
|
||||
if(lastItem)itemToSelect=lastItem.nextElementSibling}else{var firstItem=currentSelection[0]
|
||||
if(firstItem)itemToSelect=firstItem.previousElementSibling}if(itemToSelect)this.selectItem(itemToSelect,expandSelection)}
|
||||
MediaManager.prototype.gotoFolder=function(path,resetSearch){var data={path:path,resetSearch:resetSearch!==undefined?1:0}
|
||||
this.execNavigationRequest('onGoToFolder',data)}
|
||||
MediaManager.prototype.afterNavigate=function(){this.scrollToTop()
|
||||
this.generateThumbnails()
|
||||
this.updateSidebarPreview(true)
|
||||
this.selectFirstItem()
|
||||
this.updateScroll()}
|
||||
MediaManager.prototype.refresh=function(){var data={path:this.$el.find('[data-type="current-folder"]').val(),clearCache:true}
|
||||
this.execNavigationRequest('onGoToFolder',data)}
|
||||
MediaManager.prototype.execNavigationRequest=function(handler,data,element){if(element===undefined)element=this.$form
|
||||
if(this.navigationAjax!==null){try{this.navigationAjax.abort()}catch(e){}this.releaseNavigationAjax()}$.wn.stripeLoadIndicator.show()
|
||||
this.navigationAjax=element.request(this.options.alias+'::'+handler,{data:data}).always(function(){$.wn.stripeLoadIndicator.hide()}).done(this.proxy(this.afterNavigate)).always(this.proxy(this.releaseNavigationAjax))}
|
||||
MediaManager.prototype.releaseNavigationAjax=function(){this.navigationAjax=null}
|
||||
MediaManager.prototype.navigateToItem=function($item){if(!$item.length||!$item.data('path').length)return
|
||||
if($item.data('item-type')=='folder'){if(!$item.data('clear-search'))this.gotoFolder($item.data('path'))
|
||||
else{this.resetSearch()
|
||||
this.gotoFolder($item.data('path'),true)}}else if($item.data('item-type')=='file'){this.$el.trigger('popupcommand',['insert'])}}
|
||||
MediaManager.prototype.isPreviewSidebarVisible=function(){return!this.$el.find('[data-control="preview-sidebar"]').hasClass('hide')}
|
||||
MediaManager.prototype.toggleSidebar=function(ev){var isVisible=this.isPreviewSidebarVisible(),$sidebar=this.$el.find('[data-control="preview-sidebar"]'),$button=$(ev.target)
|
||||
if(!isVisible){$sidebar.removeClass('hide')
|
||||
this.updateSidebarPreview()
|
||||
$button.removeClass('sidebar-hidden')}else{$sidebar.addClass('hide')
|
||||
$button.addClass('sidebar-hidden')}this.$form.request(this.options.alias+'::onSetSidebarVisible',{data:{visible:(isVisible?0:1)}})}
|
||||
MediaManager.prototype.updateSidebarMediaPreview=function(items){var previewPanel=this.sidebarPreviewElement,previewContainer=previewPanel.querySelector('[data-control="media-preview-container"]'),template=''
|
||||
for(var i=0,len=previewContainer.children.length;i<len;i++){previewContainer.removeChild(previewContainer.children[i])}if(items.length==1&&!items[0].hasAttribute('data-root')){var item=items[0],documentType=item.getAttribute('data-document-type')
|
||||
switch(documentType){case'audio':template=previewPanel.querySelector('[data-control="audio-template"]').innerHTML
|
||||
break;case'video':template=previewPanel.querySelector('[data-control="video-template"]').innerHTML
|
||||
break;case'image':template=previewPanel.querySelector('[data-control="image-template"]').innerHTML
|
||||
break;case'document':template=previewPanel.querySelector('[data-control="document-template"]').innerHTML
|
||||
break;}previewContainer.innerHTML=template.replace('{src}',item.getAttribute('data-public-url')).replace('{path}',item.getAttribute('data-path')).replace('{last-modified}',item.getAttribute('data-last-modified-ts'))
|
||||
if(documentType=='document')this.loadSidebarDocumentIcon(item)
|
||||
if(documentType=='image')this.loadSidebarThumbnail()}else if(items.length==1&&items[0].hasAttribute('data-root')){template=previewPanel.querySelector('[data-control="go-up"]').innerHTML
|
||||
previewContainer.innerHTML=template}else if(items.length==0){template=previewPanel.querySelector('[data-control="no-selection-template"]').innerHTML
|
||||
previewContainer.innerHTML=template}else{template=previewPanel.querySelector('[data-control="multi-selection-template"]').innerHTML
|
||||
previewContainer.innerHTML=template}}
|
||||
MediaManager.prototype.updateSidebarPreview=function(resetSidebar){if(!this.sidebarPreviewElement)this.sidebarPreviewElement=this.$el.get(0).querySelector('[data-control="preview-sidebar"]')
|
||||
var items=resetSidebar===undefined?this.$el.get(0).querySelectorAll('[data-type="media-item"].selected'):[],previewPanel=this.sidebarPreviewElement
|
||||
if(items.length==0){this.sidebarPreviewElement.querySelector('[data-control="sidebar-labels"]').setAttribute('class','hide')}else if(items.length==1&&!items[0].hasAttribute('data-root')){this.sidebarPreviewElement.querySelector('[data-control="sidebar-labels"]').setAttribute('class','panel')
|
||||
var item=items[0],lastModified=item.getAttribute('data-last-modified')
|
||||
previewPanel.querySelector('[data-label="size"]').textContent=item.getAttribute('data-size')
|
||||
previewPanel.querySelector('[data-label="title"]').textContent=item.getAttribute('data-title')
|
||||
previewPanel.querySelector('[data-label="last-modified"]').textContent=lastModified
|
||||
previewPanel.querySelector('[data-label="public-url"]').setAttribute('href',item.getAttribute('data-public-url'))
|
||||
if(lastModified)previewPanel.querySelector('[data-control="last-modified"]').setAttribute('class','')
|
||||
else previewPanel.querySelector('[data-control="last-modified"]').setAttribute('class','hide')
|
||||
if(this.isSearchMode()){previewPanel.querySelector('[data-control="item-folder"]').setAttribute('class','')
|
||||
var folderNode=previewPanel.querySelector('[data-label="folder"]')
|
||||
folderNode.textContent=item.getAttribute('data-folder')
|
||||
folderNode.setAttribute('data-path',item.getAttribute('data-folder'))}else{previewPanel.querySelector('[data-control="item-folder"]').setAttribute('class','hide')}}else{this.sidebarPreviewElement.querySelector('[data-control="sidebar-labels"]').setAttribute('class','hide')}this.updateSidebarMediaPreview(items)}
|
||||
MediaManager.prototype.loadSidebarDocumentIcon=function(item){var sidebarDocument=this.sidebarPreviewElement.querySelector('[data-control="sidebar-document"]'),svg=item.querySelector('svg')
|
||||
sidebarDocument.innerHTML=svg.outerHTML}
|
||||
MediaManager.prototype.loadSidebarThumbnail=function(){if(this.sidebarThumbnailAjax){try{this.sidebarThumbnailAjax.abort()}catch(e){}this.sidebarThumbnailAjax=null}var sidebarThumbnail=this.sidebarPreviewElement.querySelector('[data-control="sidebar-thumbnail"]')
|
||||
if(!sidebarThumbnail)return
|
||||
var data={path:sidebarThumbnail.getAttribute('data-path'),lastModified:sidebarThumbnail.getAttribute('data-last-modified')}
|
||||
this.sidebarThumbnailAjax=this.$form.request(this.options.alias+'::onGetSidebarThumbnail',{data:data}).done(this.proxy(this.replaceSidebarPlaceholder)).always(this.proxy(this.releaseSidebarThumbnailAjax))}
|
||||
MediaManager.prototype.replaceSidebarPlaceholder=function(response){if(!this.sidebarPreviewElement)return
|
||||
var sidebarThumbnail=this.sidebarPreviewElement.querySelector('[data-control="sidebar-thumbnail"]')
|
||||
if(!sidebarThumbnail)return
|
||||
if(!response.markup)return
|
||||
sidebarThumbnail.innerHTML=response.markup
|
||||
sidebarThumbnail.removeAttribute('data-loading')}
|
||||
MediaManager.prototype.releaseSidebarThumbnailAjax=function(){this.sidebarThumbnailAjax=null}
|
||||
MediaManager.prototype.generateThumbnails=function(){this.thumbnailQueue=[]
|
||||
var placeholders=this.itemListElement.querySelectorAll('[data-type="media-item"] div.image-placeholder')
|
||||
for(var i=(placeholders.length-1);i>=0;i--)this.thumbnailQueue.push({id:placeholders[i].getAttribute('id'),width:placeholders[i].getAttribute('data-width'),height:placeholders[i].getAttribute('data-height'),path:placeholders[i].getAttribute('data-path'),lastModified:placeholders[i].getAttribute('data-last-modified')})
|
||||
this.handleThumbnailQueue()}
|
||||
MediaManager.prototype.handleThumbnailQueue=function(){var maxThumbnailQueueLength=2,maxThumbnailBatchLength=3
|
||||
if(this.activeThumbnailQueueLength>=maxThumbnailQueueLength)return
|
||||
for(var i=this.activeThumbnailQueueLength;i<maxThumbnailQueueLength&&this.thumbnailQueue.length>0;i++){var batch=[]
|
||||
for(var j=0;j<maxThumbnailBatchLength&&this.thumbnailQueue.length>0;j++)batch.push(this.thumbnailQueue.pop())
|
||||
this.activeThumbnailQueueLength++
|
||||
this.handleThumbnailBatch(batch).always(this.proxy(this.placeholdersUpdated))}}
|
||||
MediaManager.prototype.handleThumbnailBatch=function(batch){var data={batch:batch}
|
||||
for(var i=0,len=batch.length;i<len;i++){var placeholder=document.getElementById(batch[i].id)
|
||||
if(placeholder)placeholder.setAttribute('data-loading','true')}var promise=this.$form.request(this.options.alias+'::onGenerateThumbnails',{data:data})
|
||||
promise.done(this.proxy(this.replacePlaceholder))
|
||||
return promise}
|
||||
MediaManager.prototype.replacePlaceholder=function(response){if(!response.generatedThumbnails)return
|
||||
for(var i=0,len=response.generatedThumbnails.length;i<len;i++){var thumbnailInfo=response.generatedThumbnails[i]
|
||||
if(!thumbnailInfo.id||!thumbnailInfo.markup)continue
|
||||
var node=document.getElementById(thumbnailInfo.id)
|
||||
if(!node)continue
|
||||
var placeholderContainer=node.parentNode
|
||||
if(placeholderContainer)placeholderContainer.innerHTML=thumbnailInfo.markup}}
|
||||
MediaManager.prototype.placeholdersUpdated=function(){this.activeThumbnailQueueLength--
|
||||
this.handleThumbnailQueue()}
|
||||
MediaManager.prototype.getRelativePosition=function(element,pageX,pageY,startPosition){var absolutePosition=startPosition!==undefined?startPosition:$.wn.foundation.element.absolutePosition(element,true)
|
||||
return{x:(pageX-absolutePosition.left),y:(pageY-absolutePosition.top+this.scrollContentElement.scrollTop)}}
|
||||
MediaManager.prototype.createSelectionMarker=function(){if(this.selectionMarker)return
|
||||
this.selectionMarker=document.createElement('div')
|
||||
this.selectionMarker.setAttribute('data-control','selection-marker')
|
||||
this.scrollContentElement.insertBefore(this.selectionMarker,this.scrollContentElement.firstChild)}
|
||||
MediaManager.prototype.doObjectsCollide=function(aTop,aLeft,aWidth,aHeight,bTop,bLeft,bWidth,bHeight){return!(((aTop+aHeight)<(bTop))||(aTop>(bTop+bHeight))||((aLeft+aWidth)<bLeft)||(aLeft>(bLeft+bWidth)))}
|
||||
MediaManager.prototype.initUploader=function(){if(!this.itemListElement||this.options.readOnly)return
|
||||
var uploaderOptions={clickable:this.$el.find('[data-control="upload"]').get(0),url:this.options.url,paramName:'file_data',timeout:0,headers:{},createImageThumbnails:false}
|
||||
var token=$('meta[name="csrf-token"]').attr('content')
|
||||
if(token){uploaderOptions.headers['X-CSRF-TOKEN']=token}this.dropzone=new Dropzone(this.$el.get(0),uploaderOptions)
|
||||
this.dropzone.on('addedfile',this.proxy(this.uploadFileAdded))
|
||||
this.dropzone.on('totaluploadprogress',this.proxy(this.uploadUpdateTotalProgress))
|
||||
this.dropzone.on('queuecomplete',this.proxy(this.uploadQueueComplete))
|
||||
this.dropzone.on('sending',this.proxy(this.uploadSending))
|
||||
this.dropzone.on('error',this.proxy(this.uploadError))
|
||||
this.dropzone.on('success',this.proxy(this.uploadSuccess))
|
||||
Snowboard.globalEvent("widgets.mediamanager.initUploader",this);}
|
||||
MediaManager.prototype.destroyUploader=function(){if(!this.dropzone)return
|
||||
this.dropzone.destroy()
|
||||
this.dropzone=null}
|
||||
MediaManager.prototype.uploadFileAdded=function(){this.showUploadUi()
|
||||
this.setUploadProgress(0)
|
||||
this.$el.find('[data-command="cancel-uploading"]').removeClass('hide')
|
||||
this.$el.find('[data-command="close-uploader"]').addClass('hide')}
|
||||
MediaManager.prototype.showUploadUi=function(){this.$el.find('[data-control="upload-ui"]').removeClass('hide')}
|
||||
MediaManager.prototype.hideUploadUi=function(){this.$el.find('[data-control="upload-ui"]').addClass('hide')}
|
||||
MediaManager.prototype.uploadUpdateTotalProgress=function(uploadProgress,totalBytes,totalBytesSent){this.setUploadProgress(uploadProgress)
|
||||
var fileNumberLabel=this.$el.get(0).querySelector('[data-label="file-number-and-progress"]'),messageTemplate=fileNumberLabel.getAttribute('data-message-template'),fileNumber=this.dropzone.getUploadingFiles().length+this.dropzone.getQueuedFiles().length
|
||||
if(uploadProgress>=100){uploadProgress=99}fileNumberLabel.innerHTML=messageTemplate.replace(':number',fileNumber).replace(':percents',Math.round(uploadProgress)+'%')}
|
||||
MediaManager.prototype.setUploadProgress=function(value){var progressBar=this.$el.get(0).querySelector('[data-control="upload-progress-bar"]')
|
||||
progressBar.setAttribute('style','width: '+value+'%')
|
||||
progressBar.setAttribute('class','progress-bar')}
|
||||
MediaManager.prototype.uploadQueueComplete=function(){this.$el.find('[data-command="cancel-uploading"]').addClass('hide')
|
||||
this.$el.find('[data-command="close-uploader"]').removeClass('hide')
|
||||
this.refresh()}
|
||||
MediaManager.prototype.uploadSending=function(file,xhr,formData){formData.append('path',this.$el.find('[data-type="current-folder"]').val())
|
||||
xhr.setRequestHeader('X-WINTER-REQUEST-HANDLER',this.options.uploadHandler)}
|
||||
MediaManager.prototype.uploadCancelAll=function(){this.dropzone.removeAllFiles(true)
|
||||
this.hideUploadUi()}
|
||||
MediaManager.prototype.updateUploadBar=function(templateName,classNames){var fileNumberLabel=this.$el.get(0).querySelector('[data-label="file-number-and-progress"]'),successTemplate=fileNumberLabel.getAttribute('data-'+templateName+'-template'),progressBar=this.$el.get(0).querySelector('[data-control="upload-progress-bar"]')
|
||||
fileNumberLabel.innerHTML=successTemplate;progressBar.setAttribute('class',classNames)}
|
||||
MediaManager.prototype.uploadSuccess=function(){this.updateUploadBar('success','progress-bar progress-bar-success');}
|
||||
MediaManager.prototype.uploadError=function(file,message){this.updateUploadBar('error','progress-bar progress-bar-danger');if(file.xhr.status===413){message='Server rejected the file because it was too large, try increasing post_max_size';}if(!message){message='Error uploading file'}$.wn.alert(message)}
|
||||
MediaManager.prototype.cropSelectedImage=function(callback){var selectedItems=this.getSelectedItems(true)
|
||||
if(selectedItems.length!=1){alert(this.options.selectSingleImage)
|
||||
return}if(selectedItems[0].getAttribute('data-document-type')!=='image'){alert(this.options.selectionNotImage)
|
||||
return}var path=selectedItems[0].getAttribute('data-path')
|
||||
new $.wn.mediaManager.imageCropPopup(path,{alias:this.options.alias,onDone:callback})}
|
||||
MediaManager.prototype.onImageCropped=function(result){this.$el.trigger('popupcommand',['insert-cropped',result])}
|
||||
MediaManager.prototype.clearSearchTrackInputTimer=function(){if(this.searchTrackInputTimer===null)return
|
||||
clearTimeout(this.searchTrackInputTimer)
|
||||
this.searchTrackInputTimer=null}
|
||||
MediaManager.prototype.updateSearchResults=function(){var $searchField=this.$el.find('[data-control="search"]'),data={search:$searchField.val()}
|
||||
this.execNavigationRequest('onSearch',data,$searchField)}
|
||||
MediaManager.prototype.resetSearch=function(){this.$el.find('[data-control="search"]').val('')}
|
||||
MediaManager.prototype.onSearchChanged=function(ev){var value=ev.currentTarget.value
|
||||
if(this.lastSearchValue!==undefined&&this.lastSearchValue==value)return
|
||||
this.lastSearchValue=value
|
||||
this.clearSearchTrackInputTimer()
|
||||
this.searchTrackInputTimer=window.setTimeout(this.proxy(this.updateSearchResults),300)}
|
||||
MediaManager.prototype.deleteItems=function(){var items=this.$el.get(0).querySelectorAll('[data-type="media-item"].selected')
|
||||
if(!items.length){$.wn.alert(this.options.deleteEmpty)
|
||||
return}$.wn.confirm(this.options.deleteConfirm,this.proxy(this.deleteConfirmation))}
|
||||
MediaManager.prototype.deleteConfirmation=function(confirmed){if(!confirmed)return
|
||||
var items=this.$el.get(0).querySelectorAll('[data-type="media-item"].selected'),paths=[]
|
||||
for(var i=0,len=items.length;i<len;i++){if(items[i].hasAttribute('data-root')){continue;}paths.push({'path':items[i].getAttribute('data-path'),'type':items[i].getAttribute('data-item-type')})}var data={paths:paths}
|
||||
$.wn.stripeLoadIndicator.show()
|
||||
this.$form.request(this.options.alias+'::onDeleteItem',{data:data}).always(function(){$.wn.stripeLoadIndicator.hide()}).done(this.proxy(this.afterNavigate))}
|
||||
MediaManager.prototype.createFolder=function(ev){$(ev.target).popup({content:this.$el.find('[data-control="new-folder-template"]').html(),zIndex:1200})}
|
||||
MediaManager.prototype.onFolderPopupShown=function(ev,button,popup){$(popup).find('input[name=name]').focus()
|
||||
$(popup).on('submit.media','form',this.proxy(this.onNewFolderSubmit))}
|
||||
MediaManager.prototype.onFolderPopupHidden=function(ev,button,popup){$(popup).off('.media','form')}
|
||||
MediaManager.prototype.onNewFolderSubmit=function(ev){var data={name:$(ev.target).find('input[name=name]').val(),path:this.$el.find('[data-type="current-folder"]').val()}
|
||||
$.wn.stripeLoadIndicator.show()
|
||||
this.$form.request(this.options.alias+'::onCreateFolder',{data:data}).always(function(){$.wn.stripeLoadIndicator.hide()}).done(this.proxy(this.folderCreated))
|
||||
ev.preventDefault()
|
||||
return false}
|
||||
MediaManager.prototype.folderCreated=function(){this.$el.find('button[data-command="create-folder"]').popup('hide')
|
||||
this.afterNavigate()}
|
||||
MediaManager.prototype.moveItems=function(ev){var items=this.$el.get(0).querySelectorAll('[data-type="media-item"].selected')
|
||||
if(!items.length){$.wn.alert(this.options.moveEmpty)
|
||||
return}var data={exclude:[],path:this.$el.find('[data-type="current-folder"]').val()}
|
||||
for(var i=0,len=items.length;i<len;i++){var item=items[i],path=item.getAttribute('data-path')
|
||||
if(item.getAttribute('data-item-type')=='folder')data.exclude.push(path)}$(ev.target).popup({handler:this.options.alias+'::onLoadMovePopup',extraData:data,zIndex:1200})}
|
||||
MediaManager.prototype.onMovePopupShown=function(ev,button,popup){$(popup).on('submit.media','form',this.proxy(this.onMoveItemsSubmit))}
|
||||
MediaManager.prototype.onMoveItemsSubmit=function(ev){var items=this.$el.get(0).querySelectorAll('[data-type="media-item"].selected'),data={dest:$(ev.target).find('select[name=dest]').val(),originalPath:$(ev.target).find('input[name=originalPath]').val(),files:[],folders:[]}
|
||||
for(var i=0,len=items.length;i<len;i++){var item=items[i],path=item.getAttribute('data-path')
|
||||
if(item.getAttribute('data-item-type')=='folder')data.folders.push(path)
|
||||
else data.files.push(path)}$.wn.stripeLoadIndicator.show()
|
||||
this.$form.request(this.options.alias+'::onMoveItems',{data:data}).always(function(){$.wn.stripeLoadIndicator.hide()}).done(this.proxy(this.itemsMoved))
|
||||
ev.preventDefault()
|
||||
return false}
|
||||
MediaManager.prototype.onMovePopupHidden=function(ev,button,popup){$(popup).off('.media','form')}
|
||||
MediaManager.prototype.itemsMoved=function(){this.$el.find('button[data-command="move"]').popup('hide')
|
||||
this.afterNavigate()}
|
||||
MediaManager.prototype.onNavigate=function(ev){var $item=$(ev.target).closest('[data-type="media-item"]')
|
||||
this.navigateToItem($item)
|
||||
if($(ev.target).data('label')!='public-url')return false}
|
||||
MediaManager.prototype.onCommandClick=function(ev){var command=$(ev.currentTarget).data('command')
|
||||
switch(command){case'refresh':this.refresh()
|
||||
break;case'change-view':this.changeView($(ev.currentTarget).data('view'))
|
||||
break;case'cancel-uploading':this.uploadCancelAll()
|
||||
break;case'close-uploader':this.hideUploadUi()
|
||||
break;case'set-filter':this.setFilter($(ev.currentTarget).data('filter'))
|
||||
break;case'delete':this.deleteItems()
|
||||
break;case'create-folder':this.createFolder(ev)
|
||||
break;case'move':this.moveItems(ev)
|
||||
break;case'toggle-sidebar':this.toggleSidebar(ev)
|
||||
break;case'popup-command':var popupCommand=$(ev.currentTarget).data('popup-command')
|
||||
if(popupCommand!=='crop-and-insert')this.$el.trigger('popupcommand',[popupCommand])
|
||||
else this.cropSelectedImage(this.proxy(this.onImageCropped))
|
||||
break;}return false}
|
||||
MediaManager.prototype.onItemClick=function(ev){if(ev.target.tagName=='I'&&ev.target.hasAttribute('data-rename-control'))return
|
||||
this.selectItem(ev.currentTarget,ev.shiftKey)}
|
||||
MediaManager.prototype.onItemTouch=function(ev){ev.preventDefault()
|
||||
ev.stopPropagation()
|
||||
if(this.dblTouchFlag){this.onNavigate(ev)
|
||||
this.dblTouchFlag=null}else{this.onItemClick(ev)
|
||||
this.dblTouchFlag=true}this.clearDblTouchTimer()
|
||||
this.dblTouchTimer=setTimeout(this.proxy(this.clearDblTouchFlag),300)}
|
||||
MediaManager.prototype.onListMouseDown=function(ev){this.itemListElement.addEventListener('mousemove',this.proxy(this.onListMouseMove))
|
||||
document.addEventListener('mouseup',this.proxy(this.onListMouseUp))
|
||||
this.itemListPosition=$.wn.foundation.element.absolutePosition(this.itemListElement,true)
|
||||
var pagePosition=$.wn.foundation.event.pageCoordinates(ev),relativePosition=this.getRelativePosition(this.itemListElement,pagePosition.x,pagePosition.y,this.itemListPosition)
|
||||
this.selectionStartPoint=relativePosition
|
||||
this.selectionStarted=false}
|
||||
MediaManager.prototype.onListMouseUp=function(ev){this.itemListElement.removeEventListener('mousemove',this.proxy(this.onListMouseMove))
|
||||
document.removeEventListener('mouseup',this.proxy(this.onListMouseUp))
|
||||
$(document.body).removeClass('no-select')
|
||||
if(this.selectionStarted){this.unselectRoot()
|
||||
var items=this.itemListElement.querySelectorAll('[data-type="media-item"]:not([data-root])'),selectionPosition=$.wn.foundation.element.absolutePosition(this.selectionMarker,true)
|
||||
for(var index=0,len=items.length;index<len;index++){var item=items[index],itemPosition=$.wn.foundation.element.absolutePosition(item,true)
|
||||
if(this.doObjectsCollide(selectionPosition.top,selectionPosition.left,this.selectionMarker.offsetWidth,this.selectionMarker.offsetHeight,itemPosition.top,itemPosition.left,item.offsetWidth,item.offsetHeight)){if(!ev.shiftKey)item.setAttribute('class','selected')
|
||||
else{if(item.getAttribute('class')=='selected')item.setAttribute('class','')
|
||||
else item.setAttribute('class','selected')}}else if(!ev.shiftKey)item.setAttribute('class','')}this.updateSidebarPreview()
|
||||
this.selectionMarker.setAttribute('class','hide')}this.selectionStarted=false}
|
||||
MediaManager.prototype.onListMouseMove=function(ev){var pagePosition=$.wn.foundation.event.pageCoordinates(ev),relativePosition=this.getRelativePosition(this.itemListElement,pagePosition.x,pagePosition.y,this.itemListPosition)
|
||||
var deltaX=relativePosition.x-this.selectionStartPoint.x,deltaY=relativePosition.y-this.selectionStartPoint.y
|
||||
if(!this.selectionStarted&&(Math.abs(deltaX)>2||Math.abs(deltaY)>2)){this.createSelectionMarker()
|
||||
this.selectionMarker.setAttribute('class','')
|
||||
this.selectionStarted=true
|
||||
$(document.body).addClass('no-select')}if(this.selectionStarted){if(deltaX>=0){this.selectionMarker.style.left=this.selectionStartPoint.x+'px'
|
||||
this.selectionMarker.style.width=deltaX+'px'}else{this.selectionMarker.style.left=relativePosition.x+'px'
|
||||
this.selectionMarker.style.width=Math.abs(deltaX)+'px'}if(deltaY>=0){this.selectionMarker.style.height=deltaY+'px'
|
||||
this.selectionMarker.style.top=this.selectionStartPoint.y+'px'}else{this.selectionMarker.style.top=relativePosition.y+'px'
|
||||
this.selectionMarker.style.height=Math.abs(deltaY)+'px'}}}
|
||||
MediaManager.prototype.onSortingChanged=function(ev){var $target=$(ev.target),data={path:this.$el.find('[data-type="current-folder"]').val()}
|
||||
if($target.data('sort')=='by'){data.sortBy=$target.val();}else if($target.data('sort')=='direction'){data.sortDirection=$target.val()}this.execNavigationRequest('onSetSorting',data)}
|
||||
MediaManager.prototype.onKeyDown=function(ev){var eventHandled=false
|
||||
switch(ev.key){case'Enter':var items=this.getSelectedItems(true,true)
|
||||
if(items.length>0)this.navigateToItem($(items[0]))
|
||||
eventHandled=true
|
||||
break;case'ArrowRight':case'ArrowDown':this.selectRelative(true,ev.shiftKey)
|
||||
eventHandled=true
|
||||
break;case'ArrowLeft':case'ArrowUp':this.selectRelative(false,ev.shiftKey)
|
||||
eventHandled=true
|
||||
break;}if(eventHandled){ev.preventDefault()
|
||||
ev.stopPropagation()}}
|
||||
MediaManager.DEFAULTS={url:window.location,uploadHandler:null,alias:'',deleteEmpty:'Please select files to delete.',deleteConfirm:'Delete the selected file(s)?',moveEmpty:'Please select files to move.',selectSingleImage:'Please select a single image.',selectionNotImage:'The selected item is not an image.',bottomToolbar:false,cropAndInsertButton:false}
|
||||
var old=$.fn.mediaManager
|
||||
$.fn.mediaManager=function(option){var args=Array.prototype.slice.call(arguments,1),result=undefined
|
||||
this.each(function(){var $this=$(this)
|
||||
var data=$this.data('oc.mediaManager')
|
||||
var options=$.extend({},MediaManager.DEFAULTS,$this.data(),typeof option=='object'&&option)
|
||||
if(!data)$this.data('oc.mediaManager',(data=new MediaManager(this,options)))
|
||||
if(typeof option=='string')result=data[option].apply(data,args)
|
||||
if(typeof result!='undefined')return false})
|
||||
return result?result:this}
|
||||
$.fn.mediaManager.Constructor=MediaManager
|
||||
$.fn.mediaManager.noConflict=function(){$.fn.mediaManager=old
|
||||
return this}
|
||||
$(document).on('render',function(){$('div[data-control=media-manager]').mediaManager()})}(window.jQuery);+function($){"use strict";if($.wn.mediaManager===undefined)$.wn.mediaManager={}
|
||||
var Base=$.wn.foundation.base,BaseProto=Base.prototype
|
||||
var MediaManagerImageCropPopup=function(path,options){this.$popupRootElement=null
|
||||
this.$popupElement=null
|
||||
this.selectionSizeLabel=null
|
||||
this.imageArea=null
|
||||
this.hRulerHolder=null
|
||||
this.vRulerHolder=null
|
||||
this.rulersVisible=false
|
||||
this.prevScrollTop=0
|
||||
this.prevScrollLeft=0
|
||||
this.jCrop=null
|
||||
this.options=$.extend({},MediaManagerImageCropPopup.DEFAULTS,options)
|
||||
this.path=path
|
||||
Base.call(this)
|
||||
this.init()
|
||||
this.show()}
|
||||
MediaManagerImageCropPopup.prototype=Object.create(BaseProto)
|
||||
MediaManagerImageCropPopup.prototype.constructor=MediaManagerImageCropPopup
|
||||
MediaManagerImageCropPopup.prototype.dispose=function(){this.unregisterHandlers()
|
||||
this.removeAttachedControls()
|
||||
this.$popupRootElement.remove()
|
||||
this.$popupRootElement=null
|
||||
this.$popupElement=null
|
||||
this.selectionSizeLabel=null
|
||||
this.imageArea=null
|
||||
this.hRulerHolder=null
|
||||
this.vRulerHolder=null
|
||||
BaseProto.dispose.call(this)}
|
||||
MediaManagerImageCropPopup.prototype.init=function(){if(this.options.alias===undefined)throw new Error('Media Manager image crop popup option "alias" is not set.')
|
||||
this.$popupRootElement=$('<div/>')
|
||||
this.registerHandlers()}
|
||||
MediaManagerImageCropPopup.prototype.show=function(){var data={path:this.path}
|
||||
this.$popupRootElement.popup({extraData:data,size:'adaptive',adaptiveHeight:true,handler:this.options.alias+'::onLoadImageCropPopup',zIndex:1200})}
|
||||
MediaManagerImageCropPopup.prototype.registerHandlers=function(){this.$popupRootElement.one('hide.oc.popup',this.proxy(this.onPopupHidden))
|
||||
this.$popupRootElement.one('shown.oc.popup',this.proxy(this.onPopupShown))}
|
||||
MediaManagerImageCropPopup.prototype.unregisterHandlers=function(){this.$popupElement.off('change','[data-control="selection-mode"]',this.proxy(this.onSelectionModeChanged))
|
||||
this.$popupElement.off('click','[data-command]',this.proxy(this.onCommandClick))
|
||||
this.$popupElement.off('shown.oc.popup','button[data-command=resize]',this.proxy(this.onResizePopupShown))
|
||||
this.$popupElement.off('hidden.oc.popup','button[data-command=resize]',this.proxy(this.onResizePopupHidden))
|
||||
if(this.rulersVisible){var $cropToolRoot=this.$popupElement.find('[data-control=media-manager-crop-tool]')
|
||||
this.imageArea.removeEventListener('scroll',this.proxy(this.onImageScroll))}this.getWidthInput().off('change',this.proxy(this.onSizeInputChange))
|
||||
this.getHeightInput().off('change',this.proxy(this.onSizeInputChange))}
|
||||
MediaManagerImageCropPopup.prototype.removeAttachedControls=function(){if(this.$popupElement){this.$popupElement.find('[data-control="selection-mode"]').select2('destroy').remove()
|
||||
this.$popupElement.find('[data-control=toolbar]').toolbar('dispose').remove()
|
||||
this.jCrop.destroy()}this.jCrop=null}
|
||||
MediaManagerImageCropPopup.prototype.hide=function(){if(this.$popupElement)this.$popupElement.trigger('close.oc.popup')}
|
||||
MediaManagerImageCropPopup.prototype.getSelectionMode=function(){return this.$popupElement.find('[data-control="selection-mode"]').val()}
|
||||
MediaManagerImageCropPopup.prototype.initRulers=function(){if(!Modernizr.csstransforms)return
|
||||
var $cropToolRoot=this.$popupElement.find('[data-control=media-manager-crop-tool]'),width=$cropToolRoot.data('image-width'),height=$cropToolRoot.data('image-height')
|
||||
if(!width||!height)return
|
||||
if($cropToolRoot.width()>width)width=$(window).width()
|
||||
if($cropToolRoot.height()>height)height=$(window).height()
|
||||
$cropToolRoot.find('.ruler-container').removeClass('hide')
|
||||
$cropToolRoot.addClass('has-rulers')
|
||||
var $hRuler=$cropToolRoot.find('[data-control=h-ruler]'),$vRuler=$cropToolRoot.find('[data-control=v-ruler]'),hTicks=width/40+1,vTicks=height/40+1
|
||||
this.createRulerTicks($hRuler,hTicks)
|
||||
this.createRulerTicks($vRuler,vTicks)
|
||||
this.rulersVisible=true
|
||||
this.imageArea.addEventListener('scroll',this.proxy(this.onImageScroll))
|
||||
this.hRulerHolder=$cropToolRoot.find('.ruler-container.horizontal .layout-relative').get(0)
|
||||
this.vRulerHolder=$cropToolRoot.find('.ruler-container.vertical .layout-relative').get(0)}
|
||||
MediaManagerImageCropPopup.prototype.createRulerTicks=function($rulerElement,count){var list=document.createElement('ul')
|
||||
for(var i=0;i<=count;i++){var li=document.createElement('li')
|
||||
li.textContent=i*40
|
||||
list.appendChild(li)}$rulerElement.append(list)}
|
||||
MediaManagerImageCropPopup.prototype.initJCrop=function(){this.jCrop=$.Jcrop($(this.imageArea).find('img').get(0),{shade:true,onChange:this.proxy(this.onSelectionChanged)})}
|
||||
MediaManagerImageCropPopup.prototype.fixDimensionValue=function(value){var result=value.replace(/[^0-9]+/,'')
|
||||
if(!result.length)result=200
|
||||
if(result=='0')result=1
|
||||
return result}
|
||||
MediaManagerImageCropPopup.prototype.getWidthInput=function(){return this.$popupElement.find('[data-control="crop-width-input"]')}
|
||||
MediaManagerImageCropPopup.prototype.getHeightInput=function(){return this.$popupElement.find('[data-control="crop-height-input"]')}
|
||||
MediaManagerImageCropPopup.prototype.applySelectionMode=function(){if(!this.jCrop)return
|
||||
var $widthInput=this.getWidthInput(),$heightInput=this.getHeightInput(),width=this.fixDimensionValue($widthInput.val()),height=this.fixDimensionValue($heightInput.val()),mode=this.getSelectionMode()
|
||||
switch(mode){case'fixed-ratio':this.jCrop.setOptions({aspectRatio:width/height,minSize:[0,0],maxSize:[0,0],allowResize:true})
|
||||
break
|
||||
case'fixed-size':this.jCrop.setOptions({aspectRatio:0,minSize:[width,height],maxSize:[width,height],allowResize:false})
|
||||
break
|
||||
case'normal':this.jCrop.setOptions({aspectRatio:0,minSize:[0,0],maxSize:[0,0],allowResize:true})
|
||||
break}}
|
||||
MediaManagerImageCropPopup.prototype.cropAndInsert=function(){var data={img:$(this.imageArea).find('img').attr('src'),selection:this.jCrop.tellSelect()}
|
||||
$.wn.stripeLoadIndicator.show()
|
||||
this.$popupElement.find('form').request(this.options.alias+'::onCropImage',{data:data}).always(function(){$.wn.stripeLoadIndicator.hide()}).done(this.proxy(this.onImageCropped))}
|
||||
MediaManagerImageCropPopup.prototype.onImageCropped=function(response){this.hide()
|
||||
if(this.options.onDone!==undefined){this.options.onDone(response)}}
|
||||
MediaManagerImageCropPopup.prototype.showResizePopup=function(){this.$popupElement.find('button[data-command=resize]').popup({content:this.$popupElement.find('[data-control="resize-template"]').html(),zIndex:1220})}
|
||||
MediaManagerImageCropPopup.prototype.onResizePopupShown=function(ev,button,popup){var $popup=$(popup),$widthControl=$popup.find('input[name=width]'),$heightControl=$popup.find('input[name=height]'),imageWidth=this.fixDimensionValue(this.$popupElement.find('input[data-control=dimension-width]').val()),imageHeight=this.fixDimensionValue(this.$popupElement.find('input[data-control=dimension-height]').val())
|
||||
$widthControl.val(imageWidth)
|
||||
$heightControl.val(imageHeight)
|
||||
$widthControl.focus()
|
||||
$popup.on('submit.media','form',this.proxy(this.onResizeSubmit))
|
||||
$widthControl.on('keyup.media',this.proxy(this.onResizeDimensionKeyUp))
|
||||
$heightControl.on('keyup.media',this.proxy(this.onResizeDimensionKeyUp))
|
||||
$widthControl.on('change.media',this.proxy(this.onResizeDimensionChanged))
|
||||
$heightControl.on('change.media',this.proxy(this.onResizeDimensionChanged))}
|
||||
MediaManagerImageCropPopup.prototype.onResizePopupHidden=function(ev,button,popup){var $popup=$(popup),$widthControl=$popup.find('input[name=width]'),$heightControl=$popup.find('input[name=height]')
|
||||
$popup.off('.media','form')
|
||||
$widthControl.off('.media')
|
||||
$heightControl.off('.media')}
|
||||
MediaManagerImageCropPopup.prototype.onResizeDimensionKeyUp=function(ev){var $target=$(ev.target),targetValue=this.fixDimensionValue($target.val()),otherDimensionName=$target.attr('name')=='width'?'height':'width',$otherInput=$target.closest('form').find('input[name='+otherDimensionName+']'),ratio=this.$popupElement.find('[data-control=original-ratio]').val(),value=otherDimensionName=='height'?targetValue/ratio:targetValue*ratio
|
||||
$otherInput.val(Math.round(value))}
|
||||
MediaManagerImageCropPopup.prototype.onResizeDimensionChanged=function(ev){var $target=$(ev.target)
|
||||
$target.val(this.fixDimensionValue($target.val()))}
|
||||
MediaManagerImageCropPopup.prototype.onResizeSubmit=function(ev){var data={cropSessionKey:this.$popupElement.find('input[name=cropSessionKey]').val(),path:this.$popupElement.find('input[name=path]').val()}
|
||||
$.wn.stripeLoadIndicator.show()
|
||||
$(ev.target).request(this.options.alias+'::onResizeImage',{data:data}).always(function(){$.wn.stripeLoadIndicator.hide()}).done(this.proxy(this.imageResized))
|
||||
ev.preventDefault()
|
||||
return false}
|
||||
MediaManagerImageCropPopup.prototype.imageResized=function(response){this.$popupElement.find('button[data-command=resize]').popup('hide')
|
||||
this.updateImage(response.url,response.dimensions[0],response.dimensions[1])}
|
||||
MediaManagerImageCropPopup.prototype.updateImage=function(url,width,hegiht){this.jCrop.destroy()
|
||||
this.$popupElement.find('span[data-label=width]').text(width)
|
||||
this.$popupElement.find('span[data-label=height]').text(hegiht)
|
||||
this.$popupElement.find('input[data-control=dimension-width]').val(width)
|
||||
this.$popupElement.find('input[data-control=dimension-height]').val(hegiht)
|
||||
var $imageArea=$(this.imageArea)
|
||||
$imageArea.find('img').remove()
|
||||
var $img=$('<img>').attr('src',url)
|
||||
$img.one('load',this.proxy(this.initJCrop))
|
||||
$imageArea.append($img)
|
||||
this.imageArea.scrollTop=0
|
||||
this.imageArea.scrollLeft=0
|
||||
this.onImageScroll()}
|
||||
MediaManagerImageCropPopup.prototype.undoResizing=function(){this.updateImage(this.$popupElement.find('input[data-control=original-url]').val(),this.$popupElement.find('input[data-control=original-width]').val(),this.$popupElement.find('input[data-control=original-height]').val())}
|
||||
MediaManagerImageCropPopup.prototype.updateSelectionSizeLabel=function(width,height){if(width==0&&height==0){this.selectionSizeLabel.setAttribute('class','hide')
|
||||
return}this.selectionSizeLabel.setAttribute('class','')
|
||||
this.selectionSizeLabel.querySelector('[data-label=selection-width]').textContent=parseInt(width)
|
||||
this.selectionSizeLabel.querySelector('[data-label=selection-height]').textContent=parseInt(height)}
|
||||
MediaManagerImageCropPopup.prototype.onPopupHidden=function(event,element,popup){$(document).trigger('mousedown')
|
||||
this.dispose()}
|
||||
MediaManagerImageCropPopup.prototype.onPopupShown=function(event,element,popup){this.$popupElement=popup
|
||||
this.$popupElement.on('change','[data-control="selection-mode"]',this.proxy(this.onSelectionModeChanged))
|
||||
this.$popupElement.on('click','[data-command]',this.proxy(this.onCommandClick))
|
||||
this.$popupElement.on('shown.oc.popup','button[data-command=resize]',this.proxy(this.onResizePopupShown))
|
||||
this.$popupElement.on('hidden.oc.popup','button[data-command=resize]',this.proxy(this.onResizePopupHidden))
|
||||
this.imageArea=popup.find('[data-control=media-manager-crop-tool]').get(0).querySelector('.image_area')
|
||||
this.selectionSizeLabel=popup.find('[data-label="selection-size"]').get(0)
|
||||
this.getWidthInput().on('change',this.proxy(this.onSizeInputChange))
|
||||
this.getHeightInput().on('change',this.proxy(this.onSizeInputChange))
|
||||
this.initRulers()
|
||||
this.initJCrop()
|
||||
this.applySelectionMode()}
|
||||
MediaManagerImageCropPopup.prototype.onSelectionModeChanged=function(){var mode=this.getSelectionMode(),$widthInput=this.getWidthInput(),$heightInput=this.getHeightInput()
|
||||
if(mode==='normal'){$widthInput.attr('disabled','disabled')
|
||||
$heightInput.attr('disabled','disabled')}else{$widthInput.removeAttr('disabled')
|
||||
$heightInput.removeAttr('disabled')
|
||||
$widthInput.val(this.fixDimensionValue($widthInput.val()))
|
||||
$heightInput.val(this.fixDimensionValue($heightInput.val()))}this.applySelectionMode()}
|
||||
MediaManagerImageCropPopup.prototype.onImageScroll=function(){var scrollTop=this.imageArea.scrollTop,scrollLeft=this.imageArea.scrollLeft
|
||||
if(this.prevScrollTop!=scrollTop){this.prevScrollTop=scrollTop
|
||||
this.vRulerHolder.scrollTop=scrollTop}if(this.prevScrollLeft!=scrollLeft){this.prevScrollLeft=scrollLeft
|
||||
this.hRulerHolder.scrollLeft=scrollLeft}}
|
||||
MediaManagerImageCropPopup.prototype.onSizeInputChange=function(ev){var $target=$(ev.target)
|
||||
$target.val(this.fixDimensionValue($target.val()))
|
||||
this.applySelectionMode()}
|
||||
MediaManagerImageCropPopup.prototype.onCommandClick=function(ev){var command=$(ev.currentTarget).data('command')
|
||||
switch(command){case'insert':this.cropAndInsert()
|
||||
break
|
||||
case'resize':this.showResizePopup()
|
||||
break
|
||||
case'undo-resizing':this.undoResizing()
|
||||
break}}
|
||||
MediaManagerImageCropPopup.prototype.onSelectionChanged=function(c){this.updateSelectionSizeLabel(c.w,c.h)}
|
||||
MediaManagerImageCropPopup.DEFAULTS={alias:undefined,onDone:undefined}
|
||||
$.wn.mediaManager.imageCropPopup=MediaManagerImageCropPopup}(window.jQuery);
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* This is a bundle file, you can compile this by running
|
||||
*
|
||||
* php artisan winter:util compile assets
|
||||
*
|
||||
* @see build-min.js
|
||||
*
|
||||
|
||||
=require mediamanager.js
|
||||
=require mediamanager.imagecroppopup.js
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
These scripts will be included globally as part of the backend.
|
||||
|
||||
=require mediamanager.popup.js
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* Media manager image editor popup
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
if ($.wn.mediaManager === undefined)
|
||||
$.wn.mediaManager = {}
|
||||
|
||||
var Base = $.wn.foundation.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var MediaManagerImageCropPopup = function(path, options) {
|
||||
this.$popupRootElement = null
|
||||
this.$popupElement = null
|
||||
this.selectionSizeLabel = null
|
||||
this.imageArea = null
|
||||
this.hRulerHolder = null
|
||||
this.vRulerHolder = null
|
||||
|
||||
this.rulersVisible = false
|
||||
this.prevScrollTop = 0
|
||||
this.prevScrollLeft = 0
|
||||
|
||||
this.jCrop = null
|
||||
|
||||
this.options = $.extend({}, MediaManagerImageCropPopup.DEFAULTS, options)
|
||||
this.path = path
|
||||
|
||||
Base.call(this)
|
||||
|
||||
this.init()
|
||||
this.show()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype = Object.create(BaseProto)
|
||||
MediaManagerImageCropPopup.prototype.constructor = MediaManagerImageCropPopup
|
||||
|
||||
MediaManagerImageCropPopup.prototype.dispose = function() {
|
||||
this.unregisterHandlers()
|
||||
this.removeAttachedControls()
|
||||
|
||||
this.$popupRootElement.remove()
|
||||
this.$popupRootElement = null
|
||||
this.$popupElement = null
|
||||
this.selectionSizeLabel = null
|
||||
this.imageArea = null
|
||||
this.hRulerHolder = null
|
||||
this.vRulerHolder = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.init = function() {
|
||||
if (this.options.alias === undefined)
|
||||
throw new Error('Media Manager image crop popup option "alias" is not set.')
|
||||
|
||||
this.$popupRootElement = $('<div/>')
|
||||
this.registerHandlers()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.show = function() {
|
||||
var data = {
|
||||
path: this.path
|
||||
}
|
||||
|
||||
this.$popupRootElement.popup({
|
||||
extraData: data,
|
||||
size: 'adaptive',
|
||||
adaptiveHeight: true,
|
||||
handler: this.options.alias + '::onLoadImageCropPopup',
|
||||
zIndex: 1200 // Media Manager can be opened in a popup, so this new popup should have a higher z-index
|
||||
})
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.registerHandlers = function() {
|
||||
this.$popupRootElement.one('hide.oc.popup', this.proxy(this.onPopupHidden))
|
||||
this.$popupRootElement.one('shown.oc.popup', this.proxy(this.onPopupShown))
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.unregisterHandlers = function() {
|
||||
this.$popupElement.off('change', '[data-control="selection-mode"]', this.proxy(this.onSelectionModeChanged))
|
||||
this.$popupElement.off('click', '[data-command]', this.proxy(this.onCommandClick))
|
||||
this.$popupElement.off('shown.oc.popup', 'button[data-command=resize]', this.proxy(this.onResizePopupShown))
|
||||
this.$popupElement.off('hidden.oc.popup', 'button[data-command=resize]', this.proxy(this.onResizePopupHidden))
|
||||
|
||||
if (this.rulersVisible) {
|
||||
var $cropToolRoot = this.$popupElement.find('[data-control=media-manager-crop-tool]')
|
||||
this.imageArea.removeEventListener('scroll', this.proxy(this.onImageScroll))
|
||||
}
|
||||
|
||||
this.getWidthInput().off('change', this.proxy(this.onSizeInputChange))
|
||||
this.getHeightInput().off('change', this.proxy(this.onSizeInputChange))
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.removeAttachedControls = function() {
|
||||
if (this.$popupElement) {
|
||||
// Note - the controls are destroyed and removed from DOM. If they're just destroyed,
|
||||
// the JS plugins could be re-attached to them on window.onresize. -ab
|
||||
this.$popupElement.find('[data-control="selection-mode"]').select2('destroy').remove()
|
||||
this.$popupElement.find('[data-control=toolbar]').toolbar('dispose').remove()
|
||||
|
||||
this.jCrop.destroy()
|
||||
}
|
||||
|
||||
this.jCrop = null
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.hide = function() {
|
||||
if (this.$popupElement)
|
||||
this.$popupElement.trigger('close.oc.popup')
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.getSelectionMode = function() {
|
||||
return this.$popupElement.find('[data-control="selection-mode"]').val()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.initRulers = function() {
|
||||
if (!Modernizr.csstransforms)
|
||||
return
|
||||
|
||||
var $cropToolRoot = this.$popupElement.find('[data-control=media-manager-crop-tool]'),
|
||||
width = $cropToolRoot.data('image-width'),
|
||||
height = $cropToolRoot.data('image-height')
|
||||
|
||||
if (!width || !height)
|
||||
return
|
||||
|
||||
if ($cropToolRoot.width() > width)
|
||||
width = $(window).width()
|
||||
|
||||
if ($cropToolRoot.height() > height)
|
||||
height = $(window).height()
|
||||
|
||||
$cropToolRoot.find('.ruler-container').removeClass('hide')
|
||||
|
||||
$cropToolRoot.addClass('has-rulers')
|
||||
|
||||
var $hRuler = $cropToolRoot.find('[data-control=h-ruler]'),
|
||||
$vRuler = $cropToolRoot.find('[data-control=v-ruler]'),
|
||||
hTicks = width / 40 + 1,
|
||||
vTicks = height / 40 + 1
|
||||
|
||||
this.createRulerTicks($hRuler, hTicks)
|
||||
this.createRulerTicks($vRuler, vTicks)
|
||||
|
||||
this.rulersVisible = true
|
||||
|
||||
this.imageArea.addEventListener('scroll', this.proxy(this.onImageScroll))
|
||||
|
||||
this.hRulerHolder = $cropToolRoot.find('.ruler-container.horizontal .layout-relative').get(0)
|
||||
this.vRulerHolder = $cropToolRoot.find('.ruler-container.vertical .layout-relative').get(0)
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.createRulerTicks = function($rulerElement, count) {
|
||||
var list = document.createElement('ul')
|
||||
|
||||
for (var i=0; i <= count; i++) {
|
||||
var li = document.createElement('li')
|
||||
li.textContent = i*40
|
||||
|
||||
list.appendChild(li)
|
||||
}
|
||||
|
||||
$rulerElement.append(list)
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.initJCrop = function() {
|
||||
this.jCrop = $.Jcrop($(this.imageArea).find('img').get(0), {
|
||||
shade: true,
|
||||
onChange: this.proxy(this.onSelectionChanged)
|
||||
})
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.fixDimensionValue = function(value) {
|
||||
var result = value.replace(/[^0-9]+/, '')
|
||||
|
||||
if (!result.length)
|
||||
result = 200
|
||||
|
||||
if (result == '0')
|
||||
result = 1
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.getWidthInput = function() {
|
||||
return this.$popupElement.find('[data-control="crop-width-input"]')
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.getHeightInput = function() {
|
||||
return this.$popupElement.find('[data-control="crop-height-input"]')
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.applySelectionMode = function() {
|
||||
if (!this.jCrop)
|
||||
return
|
||||
|
||||
var $widthInput = this.getWidthInput(),
|
||||
$heightInput = this.getHeightInput(),
|
||||
width = this.fixDimensionValue($widthInput.val()),
|
||||
height = this.fixDimensionValue($heightInput.val()),
|
||||
mode = this.getSelectionMode()
|
||||
|
||||
switch (mode) {
|
||||
case 'fixed-ratio' :
|
||||
this.jCrop.setOptions({
|
||||
aspectRatio: width/height,
|
||||
minSize: [0, 0],
|
||||
maxSize: [0, 0],
|
||||
allowResize: true
|
||||
})
|
||||
break
|
||||
case 'fixed-size' :
|
||||
this.jCrop.setOptions({
|
||||
aspectRatio: 0,
|
||||
minSize: [width, height],
|
||||
maxSize: [width, height],
|
||||
allowResize: false
|
||||
})
|
||||
break
|
||||
case 'normal' :
|
||||
this.jCrop.setOptions({
|
||||
aspectRatio: 0,
|
||||
minSize: [0, 0],
|
||||
maxSize: [0, 0],
|
||||
allowResize: true
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.cropAndInsert = function() {
|
||||
var data = {
|
||||
img: $(this.imageArea).find('img').attr('src'),
|
||||
selection: this.jCrop.tellSelect()
|
||||
}
|
||||
|
||||
$.wn.stripeLoadIndicator.show()
|
||||
|
||||
this.$popupElement
|
||||
.find('form')
|
||||
.request(this.options.alias+'::onCropImage', {
|
||||
data: data
|
||||
})
|
||||
.always(function() {
|
||||
$.wn.stripeLoadIndicator.hide()
|
||||
})
|
||||
.done(this.proxy(this.onImageCropped))
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onImageCropped = function(response) {
|
||||
this.hide()
|
||||
|
||||
if (this.options.onDone !== undefined) {
|
||||
this.options.onDone(response)
|
||||
}
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.showResizePopup = function() {
|
||||
this.$popupElement.find('button[data-command=resize]').popup({
|
||||
content: this.$popupElement.find('[data-control="resize-template"]').html(),
|
||||
zIndex: 1220
|
||||
})
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onResizePopupShown = function(ev, button, popup) {
|
||||
var $popup = $(popup),
|
||||
$widthControl = $popup.find('input[name=width]'),
|
||||
$heightControl = $popup.find('input[name=height]'),
|
||||
imageWidth = this.fixDimensionValue(this.$popupElement.find('input[data-control=dimension-width]').val()),
|
||||
imageHeight = this.fixDimensionValue(this.$popupElement.find('input[data-control=dimension-height]').val())
|
||||
|
||||
$widthControl.val(imageWidth)
|
||||
$heightControl.val(imageHeight)
|
||||
|
||||
$widthControl.focus()
|
||||
|
||||
$popup.on('submit.media', 'form', this.proxy(this.onResizeSubmit))
|
||||
$widthControl.on('keyup.media', this.proxy(this.onResizeDimensionKeyUp))
|
||||
$heightControl.on('keyup.media', this.proxy(this.onResizeDimensionKeyUp))
|
||||
|
||||
$widthControl.on('change.media', this.proxy(this.onResizeDimensionChanged))
|
||||
$heightControl.on('change.media', this.proxy(this.onResizeDimensionChanged))
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onResizePopupHidden = function(ev, button, popup) {
|
||||
var $popup = $(popup),
|
||||
$widthControl = $popup.find('input[name=width]'),
|
||||
$heightControl = $popup.find('input[name=height]')
|
||||
|
||||
$popup.off('.media', 'form')
|
||||
$widthControl.off('.media')
|
||||
$heightControl.off('.media')
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onResizeDimensionKeyUp = function(ev) {
|
||||
var $target = $(ev.target),
|
||||
targetValue = this.fixDimensionValue($target.val()),
|
||||
otherDimensionName = $target.attr('name') == 'width' ? 'height' : 'width',
|
||||
$otherInput = $target.closest('form').find('input[name='+otherDimensionName+']'),
|
||||
ratio = this.$popupElement.find('[data-control=original-ratio]').val(),
|
||||
value = otherDimensionName == 'height' ? targetValue / ratio : targetValue * ratio
|
||||
|
||||
$otherInput.val(Math.round(value))
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onResizeDimensionChanged = function(ev) {
|
||||
var $target = $(ev.target)
|
||||
|
||||
$target.val(this.fixDimensionValue($target.val()))
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onResizeSubmit = function(ev) {
|
||||
var data = {
|
||||
cropSessionKey: this.$popupElement.find('input[name=cropSessionKey]').val(),
|
||||
path: this.$popupElement.find('input[name=path]').val()
|
||||
}
|
||||
|
||||
$.wn.stripeLoadIndicator.show()
|
||||
$(ev.target).request(this.options.alias+'::onResizeImage', {
|
||||
data: data
|
||||
}).always(function() {
|
||||
$.wn.stripeLoadIndicator.hide()
|
||||
}).done(this.proxy(this.imageResized))
|
||||
|
||||
ev.preventDefault()
|
||||
return false
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.imageResized = function(response) {
|
||||
this.$popupElement.find('button[data-command=resize]').popup('hide')
|
||||
|
||||
this.updateImage(response.url, response.dimensions[0], response.dimensions[1])
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.updateImage = function(url, width, hegiht) {
|
||||
this.jCrop.destroy()
|
||||
|
||||
this.$popupElement.find('span[data-label=width]').text(width)
|
||||
this.$popupElement.find('span[data-label=height]').text(hegiht)
|
||||
|
||||
this.$popupElement.find('input[data-control=dimension-width]').val(width)
|
||||
this.$popupElement.find('input[data-control=dimension-height]').val(hegiht)
|
||||
|
||||
var $imageArea = $(this.imageArea)
|
||||
$imageArea.find('img').remove()
|
||||
|
||||
var $img = $('<img>').attr('src', url)
|
||||
$img.one('load', this.proxy(this.initJCrop))
|
||||
|
||||
$imageArea.append($img)
|
||||
|
||||
this.imageArea.scrollTop = 0
|
||||
this.imageArea.scrollLeft = 0
|
||||
this.onImageScroll()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.undoResizing = function() {
|
||||
this.updateImage(
|
||||
this.$popupElement.find('input[data-control=original-url]').val(),
|
||||
this.$popupElement.find('input[data-control=original-width]').val(),
|
||||
this.$popupElement.find('input[data-control=original-height]').val()
|
||||
)
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.updateSelectionSizeLabel = function(width, height) {
|
||||
if (width == 0 && height == 0) {
|
||||
this.selectionSizeLabel.setAttribute('class', 'hide')
|
||||
return
|
||||
}
|
||||
|
||||
this.selectionSizeLabel.setAttribute('class', '')
|
||||
this.selectionSizeLabel.querySelector('[data-label=selection-width]').textContent = parseInt(width)
|
||||
this.selectionSizeLabel.querySelector('[data-label=selection-height]').textContent = parseInt(height)
|
||||
}
|
||||
|
||||
// EVENT HANDLERS
|
||||
// ============================
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onPopupHidden = function(event, element, popup) {
|
||||
// Release clickedElement reference inside redactor.js
|
||||
// If we don't do it, the image editor popup DOM elements
|
||||
// won't be removed from the memory.
|
||||
$(document).trigger('mousedown')
|
||||
|
||||
this.dispose()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onPopupShown = function(event, element, popup) {
|
||||
this.$popupElement = popup
|
||||
this.$popupElement.on('change', '[data-control="selection-mode"]', this.proxy(this.onSelectionModeChanged))
|
||||
this.$popupElement.on('click', '[data-command]', this.proxy(this.onCommandClick))
|
||||
this.$popupElement.on('shown.oc.popup', 'button[data-command=resize]', this.proxy(this.onResizePopupShown))
|
||||
this.$popupElement.on('hidden.oc.popup', 'button[data-command=resize]', this.proxy(this.onResizePopupHidden))
|
||||
|
||||
this.imageArea = popup.find('[data-control=media-manager-crop-tool]').get(0).querySelector('.image_area')
|
||||
this.selectionSizeLabel = popup.find('[data-label="selection-size"]').get(0)
|
||||
|
||||
this.getWidthInput().on('change', this.proxy(this.onSizeInputChange))
|
||||
this.getHeightInput().on('change', this.proxy(this.onSizeInputChange))
|
||||
|
||||
this.initRulers()
|
||||
this.initJCrop()
|
||||
this.applySelectionMode()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onSelectionModeChanged = function() {
|
||||
var mode = this.getSelectionMode(),
|
||||
$widthInput = this.getWidthInput(),
|
||||
$heightInput = this.getHeightInput()
|
||||
|
||||
if (mode === 'normal') {
|
||||
$widthInput.attr('disabled', 'disabled')
|
||||
$heightInput.attr('disabled', 'disabled')
|
||||
}
|
||||
else {
|
||||
$widthInput.removeAttr('disabled')
|
||||
$heightInput.removeAttr('disabled')
|
||||
|
||||
$widthInput.val(this.fixDimensionValue($widthInput.val()))
|
||||
$heightInput.val(this.fixDimensionValue($heightInput.val()))
|
||||
}
|
||||
|
||||
this.applySelectionMode()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onImageScroll = function() {
|
||||
var scrollTop = this.imageArea.scrollTop,
|
||||
scrollLeft = this.imageArea.scrollLeft
|
||||
|
||||
if (this.prevScrollTop != scrollTop) {
|
||||
this.prevScrollTop = scrollTop
|
||||
|
||||
this.vRulerHolder.scrollTop = scrollTop
|
||||
}
|
||||
|
||||
if (this.prevScrollLeft != scrollLeft) {
|
||||
this.prevScrollLeft = scrollLeft
|
||||
|
||||
this.hRulerHolder.scrollLeft = scrollLeft
|
||||
}
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onSizeInputChange = function(ev) {
|
||||
var $target = $(ev.target)
|
||||
|
||||
$target.val(this.fixDimensionValue($target.val()))
|
||||
|
||||
this.applySelectionMode()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onCommandClick = function(ev) {
|
||||
var command = $(ev.currentTarget).data('command')
|
||||
|
||||
switch (command) {
|
||||
case 'insert':
|
||||
this.cropAndInsert()
|
||||
break
|
||||
case 'resize':
|
||||
this.showResizePopup()
|
||||
break
|
||||
case 'undo-resizing':
|
||||
this.undoResizing()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onSelectionChanged = function(c) {
|
||||
this.updateSelectionSizeLabel(c.w, c.h)
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.DEFAULTS = {
|
||||
alias: undefined,
|
||||
onDone: undefined
|
||||
}
|
||||
|
||||
$.wn.mediaManager.imageCropPopup = MediaManagerImageCropPopup
|
||||
}(window.jQuery);
|
||||
1350
modules/backend/widgets/mediamanager/assets/js/mediamanager.js
Normal file
1350
modules/backend/widgets/mediamanager/assets/js/mediamanager.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Media manager popup
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
if ($.wn.mediaManager === undefined)
|
||||
$.wn.mediaManager = {}
|
||||
|
||||
var Base = $.wn.foundation.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var MediaManagerPopup = function(options) {
|
||||
this.$popupRootElement = null
|
||||
|
||||
this.options = $.extend({}, MediaManagerPopup.DEFAULTS, options)
|
||||
|
||||
Base.call(this)
|
||||
|
||||
this.init()
|
||||
this.show()
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype = Object.create(BaseProto)
|
||||
MediaManagerPopup.prototype.constructor = MediaManagerPopup
|
||||
|
||||
MediaManagerPopup.prototype.dispose = function() {
|
||||
this.unregisterHandlers()
|
||||
|
||||
this.$popupRootElement.remove()
|
||||
this.$popupRootElement = null
|
||||
this.$popupElement = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype.init = function() {
|
||||
if (this.options.alias === undefined)
|
||||
throw new Error('Media Manager popup option "alias" is not set.')
|
||||
|
||||
this.$popupRootElement = $('<div/>')
|
||||
this.registerHandlers()
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype.registerHandlers = function() {
|
||||
this.$popupRootElement.one('hide.oc.popup', this.proxy(this.onPopupHidden))
|
||||
this.$popupRootElement.one('shown.oc.popup', this.proxy(this.onPopupShown))
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype.unregisterHandlers = function() {
|
||||
this.$popupElement.off('popupcommand', this.proxy(this.onPopupCommand))
|
||||
this.$popupRootElement.off('popupcommand', this.proxy(this.onPopupCommand))
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype.show = function() {
|
||||
var data = {
|
||||
bottomToolbar: this.options.bottomToolbar ? 1 : 0,
|
||||
cropAndInsertButton: this.options.cropAndInsertButton ? 1 : 0,
|
||||
mode: this.options.mode || 'all',
|
||||
}
|
||||
|
||||
this.$popupRootElement.popup({
|
||||
extraData: data,
|
||||
size: 'adaptive',
|
||||
adaptiveHeight: true,
|
||||
handler: this.options.alias + '::onLoadPopup'
|
||||
})
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype.hide = function() {
|
||||
if (this.$popupElement)
|
||||
this.$popupElement.trigger('close.oc.popup')
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype.getMediaManagerElement = function() {
|
||||
return this.$popupElement.find('[data-control="media-manager"]')
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype.insertMedia = function() {
|
||||
var items = this.getMediaManagerElement().mediaManager('getSelectedItems')
|
||||
|
||||
if (this.options.onInsert !== undefined)
|
||||
this.options.onInsert.call(this, items)
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype.insertCroppedImage = function(imageItem) {
|
||||
if (this.options.onInsert !== undefined)
|
||||
this.options.onInsert.call(this, [imageItem])
|
||||
}
|
||||
|
||||
// EVENT HANDLERS
|
||||
// ============================
|
||||
|
||||
MediaManagerPopup.prototype.onPopupHidden = function(event, element, popup) {
|
||||
var mediaManager = this.getMediaManagerElement()
|
||||
|
||||
mediaManager.mediaManager('dispose')
|
||||
mediaManager.remove()
|
||||
|
||||
// Release clickedElement reference inside redactor.js
|
||||
// If we don't do it, the Media Manager popup DOM elements
|
||||
// won't be removed from the memory.
|
||||
$(document).trigger('mousedown')
|
||||
|
||||
this.dispose()
|
||||
|
||||
if (this.options.onClose !== undefined)
|
||||
this.options.onClose.call(this)
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype.onPopupShown = function(event, element, popup) {
|
||||
this.$popupElement = popup
|
||||
this.$popupElement.on('popupcommand', this.proxy(this.onPopupCommand))
|
||||
|
||||
// Unfocus the Redactor field, otherwise all keyboard commands
|
||||
// in the Media Manager popup translate to Redactor.
|
||||
this.getMediaManagerElement().mediaManager('selectFirstItem')
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype.onPopupCommand = function(ev, command, param) {
|
||||
switch (command) {
|
||||
case 'insert' :
|
||||
this.insertMedia()
|
||||
break;
|
||||
case 'insert-cropped' :
|
||||
this.insertCroppedImage(param)
|
||||
break;
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
MediaManagerPopup.DEFAULTS = {
|
||||
alias: undefined,
|
||||
bottomToolbar: true,
|
||||
cropAndInsertButton: false,
|
||||
onInsert: undefined,
|
||||
onClose: undefined
|
||||
}
|
||||
|
||||
$.wn.mediaManager.popup = MediaManagerPopup
|
||||
}(window.jQuery);
|
||||
@@ -0,0 +1,803 @@
|
||||
@import "../../../../../backend/assets/less/core/boot.less";
|
||||
|
||||
@color-media-list-hover-bg: mix(white, @brand-secondary, 13%);
|
||||
|
||||
.media-selected-tiles() {
|
||||
.icon-container {
|
||||
background: @color-media-list-hover-bg !important;
|
||||
border-color: #2581b8;
|
||||
|
||||
i, p {
|
||||
color: #ecf0f1;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
color: #2581b8;
|
||||
}
|
||||
}
|
||||
|
||||
.media-selected-list() {
|
||||
background: @color-media-list-hover-bg !important;
|
||||
|
||||
i, p.size {
|
||||
color: #ecf0f1;
|
||||
}
|
||||
|
||||
h4 {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
border-right-color: @color-media-list-hover-bg !important;
|
||||
}
|
||||
}
|
||||
|
||||
div[data-control="media-manager"] {
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.loading-indicator-pseudo-absolute(@size) {
|
||||
background-image: url('../../../../../../modules/system/assets/ui/images/loader-transparent.svg');
|
||||
background-position: 50% 50%;
|
||||
content: ' ';
|
||||
.animation(spin 1s linear infinite);
|
||||
|
||||
background-size: @size @size;
|
||||
position: absolute;
|
||||
width: @size;
|
||||
height: @size;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -(@size / 2);
|
||||
margin-left: -(@size / 2);
|
||||
}
|
||||
|
||||
audio, video {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
video {
|
||||
background: #ecf0f1;
|
||||
max-height: 225px;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
fill-rule: evenodd;
|
||||
clip-rule: evenodd;
|
||||
stroke-linejoin: round;
|
||||
stroke-miterlimit: 2;
|
||||
display: inline-block;
|
||||
|
||||
&-extension {
|
||||
font-family: 'ArialMT','Arial',sans-serif;
|
||||
font-size: 4em;
|
||||
font-weight: 900;
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
&-label {
|
||||
fill: #576D7E; /* Default color */
|
||||
fill-rule: nonzero;
|
||||
}
|
||||
|
||||
&-css,
|
||||
&-less,
|
||||
&-scss {
|
||||
fill: #B73FD9;
|
||||
}
|
||||
&-html,
|
||||
&-xml {
|
||||
fill: #EA9B47;
|
||||
}
|
||||
&-js,
|
||||
&-json {
|
||||
fill: #A9A9A9;
|
||||
}
|
||||
&-pdf {
|
||||
fill: #E30713;
|
||||
}
|
||||
&-txt {
|
||||
fill: #248BD0;
|
||||
}
|
||||
&-ai {
|
||||
fill: #F29200;
|
||||
}
|
||||
&-eps {
|
||||
fill: #F9B234;
|
||||
}
|
||||
&-psd {
|
||||
fill: #2DAAE2;
|
||||
}
|
||||
&-ttf,
|
||||
&-otf,
|
||||
&-woff,
|
||||
&-woff2 {
|
||||
fill: #C4CA10;
|
||||
}
|
||||
&-doc,
|
||||
&-docx,
|
||||
&-rtf,
|
||||
&-odt {
|
||||
fill: #0F70B7;
|
||||
}
|
||||
&-csv,
|
||||
&-ods,
|
||||
&-xls,
|
||||
&-xlsx {
|
||||
fill: #3BAA34;
|
||||
}
|
||||
&-odp,
|
||||
&-ppt,
|
||||
&-pptx {
|
||||
fill: #D04526;
|
||||
}
|
||||
&-rar,
|
||||
&-tar,
|
||||
&-zip {
|
||||
fill: #363A56;
|
||||
}
|
||||
}
|
||||
|
||||
.media-player-fallback {
|
||||
font-size: 13px;
|
||||
color: #95a5a6;
|
||||
background: #ecf0f1;
|
||||
line-height: 180%;
|
||||
|
||||
&.panel-embedded {
|
||||
padding: 20px;
|
||||
margin: -20px -20px 0 -20px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-message() {
|
||||
font-size: 12px;
|
||||
margin: 10px;
|
||||
line-height: 160%;
|
||||
color: #bdc3c7;
|
||||
}
|
||||
|
||||
.empty-library {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p.thumbnail-error-message {
|
||||
.icon-message();
|
||||
}
|
||||
|
||||
.media-list {
|
||||
padding: 0 0 0 20px;
|
||||
margin: 0;
|
||||
.user-select(none);
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin: 0 20px 20px 0;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
.border-radius(3px);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
display: table;
|
||||
|
||||
i {
|
||||
color: #95a5a6;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div {
|
||||
display: table-cell;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-container.image {
|
||||
> div.icon-wrapper {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
color: #2b3e50;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 150%;
|
||||
margin: 15px 0 5px 0;
|
||||
padding-right: 0;
|
||||
.transition(padding 0.1s);
|
||||
|
||||
position: relative;
|
||||
|
||||
a {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
font-size: 15px;
|
||||
color: #2b3e50;
|
||||
display: none;
|
||||
|
||||
&:hover {
|
||||
color: @link-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.size {
|
||||
font-size: 12px;
|
||||
color: #95a5a6;
|
||||
}
|
||||
|
||||
.image-placeholder {
|
||||
position: relative;
|
||||
|
||||
i {
|
||||
padding-top: 0;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
&[data-loading] {
|
||||
i {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-loading]:after {
|
||||
.loading-indicator-pseudo-absolute(28px);
|
||||
}
|
||||
}
|
||||
|
||||
i.icon-chain-broken {
|
||||
padding: 0;
|
||||
color: #bdc3c7;
|
||||
}
|
||||
|
||||
&[data-item-type=folder] i {
|
||||
color: @color-media-list-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&.list {
|
||||
li {
|
||||
height: 75px;
|
||||
width: 260px;
|
||||
border: 1px solid #ecf0f1;
|
||||
background: #f6f8f9;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
li .icon-container {
|
||||
border-right: 1px solid #f6f8f9;
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
float: left;
|
||||
|
||||
img {
|
||||
max-height: 75px;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 35px;
|
||||
}
|
||||
|
||||
svg {
|
||||
max-height: 44px;
|
||||
}
|
||||
|
||||
&.image {
|
||||
border-right: 1px solid #ecf0f1!important;
|
||||
}
|
||||
|
||||
p.thumbnail-error-message {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
li .info {
|
||||
margin-left: 90px;
|
||||
}
|
||||
|
||||
li .image-placeholder {
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
}
|
||||
|
||||
li[data-root] h4 {
|
||||
margin-top: 27px;
|
||||
}
|
||||
|
||||
li.selected {
|
||||
.media-selected-list();
|
||||
}
|
||||
|
||||
h4 {
|
||||
padding-right: 15px;
|
||||
|
||||
a {
|
||||
right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.tiles {
|
||||
li {
|
||||
width: 167px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
width: 167px;
|
||||
}
|
||||
|
||||
li .image-placeholder {
|
||||
width: 165px;
|
||||
height: 165px;
|
||||
|
||||
&[data-loading]:after {
|
||||
.loading-indicator-pseudo-absolute(55px);
|
||||
}
|
||||
}
|
||||
|
||||
li .icon-container {
|
||||
width: 165px;
|
||||
height: 165px;
|
||||
.border-radius(3px);
|
||||
border: 1px solid #ecf0f1;
|
||||
overflow: hidden;
|
||||
background: #f6f8f9;
|
||||
box-sizing: content-box;
|
||||
|
||||
img {
|
||||
max-height: 165px;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 55px;
|
||||
}
|
||||
|
||||
svg {
|
||||
max-height: 65px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: @font-family-base;
|
||||
}
|
||||
}
|
||||
|
||||
li.selected {
|
||||
.media-selected-tiles();
|
||||
}
|
||||
|
||||
i.icon-chain-broken {
|
||||
margin-top: 47px;
|
||||
}
|
||||
|
||||
p.size {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-control="sidebar-labels"] {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.sidebar-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.sidebar-image-placeholder-container,
|
||||
.sidebar-document-placeholder-container {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar-image-placeholder,
|
||||
.sidebar-document-placeholder {
|
||||
display: table-cell;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #ecf0f1;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.sidebar-image-placeholder {
|
||||
height: 225px;
|
||||
|
||||
&[data-loading] {
|
||||
background: #ecf0f1;
|
||||
&:after {
|
||||
.loading-indicator-pseudo-absolute(62px);
|
||||
}
|
||||
}
|
||||
|
||||
i.icon-chain-broken, i.icon-crop, i.icon-asterisk, i.icon-level-up {
|
||||
color: #bdc3c7;
|
||||
font-size: 55px;
|
||||
}
|
||||
|
||||
&.no-border {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
p {
|
||||
.icon-message();
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 225px;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-document-placeholder {
|
||||
height: 155px;
|
||||
|
||||
svg {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.list-container {
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
|
||||
.no-data {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
p.no-data {
|
||||
padding: 0 20px 20px 20px;
|
||||
}
|
||||
|
||||
li.no-data {
|
||||
padding-top: 20px;
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
table.table.data {
|
||||
tbody tr:not(.no-data):active td {
|
||||
background: @color-list-hover-bg !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-control="item-list"] {
|
||||
position: relative;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.control-scrollpad {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
// Prevents erratic rendering issues when the height is
|
||||
// sometimes calculated as 0 then repeatedly redrawn
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.scroll-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
table.table {
|
||||
table-layout: fixed;
|
||||
margin-bottom: 0;
|
||||
white-space: nowrap;
|
||||
|
||||
div.no-wrap-text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
div.item-title {
|
||||
position: relative;
|
||||
padding-right: 0;
|
||||
.transition(padding 0.1s);
|
||||
|
||||
a {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
tr:hover div.item-title {
|
||||
padding-right: 25px;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
tr[data-item-type=folder] i.icon-folder {
|
||||
color: @color-media-list-hover-bg;
|
||||
}
|
||||
|
||||
tr:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
div[data-control="selection-marker"] {
|
||||
position: absolute;
|
||||
z-index: 250;
|
||||
border: 1px dashed #95a5a6;
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.upload-progress {
|
||||
background: @body-bg;
|
||||
padding: 0 20px;
|
||||
|
||||
h5 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 13px;
|
||||
color: #2b3e50;
|
||||
font-weight: 600;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
color: #95a5a6;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-controls {
|
||||
padding-right: 30px;
|
||||
position: relative;
|
||||
|
||||
.controls {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 7px;
|
||||
right: 3px;
|
||||
color: #95a5a6;
|
||||
font-size: 16px;
|
||||
cursor: pointer!important;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: @link-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dz-preview {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button[data-command="toggle-sidebar"] {
|
||||
&.sidebar-hidden {
|
||||
.transform( ~'rotate(180deg) translate(0, 0)' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-control="media-manager-crop-tool"] {
|
||||
.image_area {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
overflow: auto;
|
||||
|
||||
.jcrop-holder {
|
||||
background-color: transparent!important;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
cursor: crosshair;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.has-rulers {
|
||||
.ruler-container {
|
||||
.layout-relative {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.horizontal {
|
||||
.layout-cell {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.layout-relative {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.vertical {
|
||||
width: 20px;
|
||||
.layout-relative {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ruler {
|
||||
position: absolute;
|
||||
height: 20px;
|
||||
margin-left: -3px;
|
||||
background: #555;
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 0 0 0 40px;
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
margin: 0px -10px 0px -14px;
|
||||
.box-sizing(content-box);
|
||||
text-align: left;
|
||||
position: relative;
|
||||
font-size: 10px;
|
||||
line-height: 20px;
|
||||
color: #ecf0f1;
|
||||
font-family: Arial, sans-serif;
|
||||
|
||||
&:before, &:after {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
border-left: 1px solid #8e8e8e;
|
||||
}
|
||||
|
||||
&:before {
|
||||
height: 20px;
|
||||
top: 0;
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
height: 3px;
|
||||
bottom: 0;
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
&:first-child:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ruler[data-control=v-ruler] {
|
||||
.transform(~'rotateZ(90deg)');
|
||||
.transform-origin(~'left top');
|
||||
left: 23px;
|
||||
top: -23px;
|
||||
|
||||
& li:after {
|
||||
top: 0;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
body:not(.no-select) {
|
||||
div[data-control="media-manager"] .media-list {
|
||||
&.tiles {
|
||||
li:hover {
|
||||
.media-selected-tiles();
|
||||
}
|
||||
|
||||
li:hover h4 {
|
||||
padding-right: 20px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.list {
|
||||
li:hover {
|
||||
.media-selected-list();
|
||||
}
|
||||
|
||||
li:hover h4 {
|
||||
padding-right: 35px !important;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
&:hover h4 a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
div[data-control="media-manager"] {
|
||||
.media-list {
|
||||
&.list {
|
||||
li {
|
||||
width: 230px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
div[data-control="media-manager"] {
|
||||
.media-list {
|
||||
&.list {
|
||||
li {
|
||||
display: block;
|
||||
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: @screen-sm) {
|
||||
div[data-control="media-manager"] {
|
||||
[data-control="preview-sidebar"],
|
||||
[data-command="toggle-sidebar"] {
|
||||
display: none!important;
|
||||
}
|
||||
|
||||
.media-list {
|
||||
&.list {
|
||||
padding: 0;
|
||||
li {
|
||||
.border-radius(0);
|
||||
margin: 0;
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
div[data-control="media-manager"] {
|
||||
[data-control="left-sidebar"] {
|
||||
display: none!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
70
modules/backend/widgets/mediamanager/partials/_body.php
Normal file
70
modules/backend/widgets/mediamanager/partials/_body.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<div
|
||||
data-control="media-manager"
|
||||
class="layout"
|
||||
data-alias="<?= $this->alias ?>"
|
||||
data-upload-handler="<?= $this->getEventHandler('onUpload') ?>"
|
||||
data-delete-empty="<?= e(trans('backend::lang.media.delete_empty')) ?>"
|
||||
data-delete-confirm="<?= e(trans('backend::lang.media.delete_confirm')) ?>"
|
||||
data-move-empty="<?= e(trans('backend::lang.media.move_empty')) ?>"
|
||||
data-select-single-image="<?= e(trans('backend::lang.media.select_single_image')) ?>"
|
||||
data-selection-not-image="<?= e(trans('backend::lang.media.selection_not_image')) ?>"
|
||||
data-bottom-toolbar="<?= $this->bottomToolbar ? 'true' : 'false' ?>"
|
||||
data-crop-and-insert-button="<?= $this->cropAndInsertButton ? 'true' : 'false' ?>"
|
||||
data-read-only="<?= $this->readOnly ? 'true' : 'false'; ?>"
|
||||
tabindex="0"
|
||||
>
|
||||
|
||||
<?= $this->makePartial('toolbar') ?>
|
||||
|
||||
<?= (!$this->readOnly) ? $this->makePartial('upload-progress') : '' ?>
|
||||
|
||||
<div class="layout-row whiteboard">
|
||||
<div class="layout">
|
||||
<div class="layout-row">
|
||||
<div class="layout-cell panel w-200 border-right" data-control="left-sidebar">
|
||||
<?= $this->makePartial('left-sidebar') ?>
|
||||
</div>
|
||||
<div class="layout-cell">
|
||||
<div class="layout">
|
||||
|
||||
<div class="layout-row min-size">
|
||||
<?= $this->makePartial('folder-toolbar') ?>
|
||||
</div>
|
||||
<div class="layout-row">
|
||||
<!-- Main area -->
|
||||
<div class="layout">
|
||||
<div class="layout-row">
|
||||
<div class="layout">
|
||||
<!-- Main area - list -->
|
||||
<div data-control="item-list">
|
||||
<div class="control-scrollpad">
|
||||
<div class="scroll-wrapper"> <!-- This element is required for the scrollpad control -->
|
||||
<div id="<?= $this->getId('item-list') ?>" >
|
||||
<?= $this->makePartial('item-list') ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layout-cell w-300 panel border-left no-padding <?= !$sidebarVisible ? 'hide' : null ?>" data-control="preview-sidebar">
|
||||
<!-- Right sidebar -->
|
||||
<?= $this->makePartial('right-sidebar') ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layout-row min-size hide" data-control="bottom-toolbar">
|
||||
<?= $this->makePartial('bottom-toolbar') ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?= $this->makePartial('new-folder-form') ?>
|
||||
</div>
|
||||
@@ -0,0 +1,30 @@
|
||||
<div class="panel no-padding-bottom border-top">
|
||||
<div class="form-buttons">
|
||||
<div class="pull-right">
|
||||
<button
|
||||
type="button"
|
||||
data-command="popup-command"
|
||||
data-popup-command="insert"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.media.insert')) ?>
|
||||
</button>
|
||||
|
||||
<?php if (!$this->readOnly): ?>
|
||||
<button
|
||||
type="button"
|
||||
data-command="popup-command"
|
||||
data-popup-command="crop-and-insert"
|
||||
class="btn btn-primary hide">
|
||||
<?= e(trans('backend::lang.media.crop_and_insert')) ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-dismiss="popup"
|
||||
class="btn btn-default no-margin-right">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,33 @@
|
||||
<div class="layout"
|
||||
data-control="media-manager-crop-tool"
|
||||
<?php if ($dimensions): ?>
|
||||
data-image-width="<?= $dimensions[0] ?>"
|
||||
data-image-height="<?= $dimensions[1] ?>"
|
||||
<?php endif ?>
|
||||
>
|
||||
<div class="layout-row min-size ruler-container horizontal hide">
|
||||
<div class="layout-cell">
|
||||
<div class="layout-relative">
|
||||
<div class="ruler" data-control="h-ruler"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-row">
|
||||
<div class="layout">
|
||||
<div class="layout-row">
|
||||
<div class="layout-cell ruler-container vertical hide">
|
||||
<div class="layout-relative">
|
||||
<div class="ruler" data-control="v-ruler"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-cell">
|
||||
<div class="layout-relative">
|
||||
<div class="image_area">
|
||||
<img src="<?= $imageUrl ?>"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
$selectionModes = [
|
||||
Backend\Widgets\MediaManager::SELECTION_MODE_NORMAL => trans('backend::lang.media.selection_mode_normal'),
|
||||
Backend\Widgets\MediaManager::SELECTION_MODE_FIXED_RATIO => trans('backend::lang.media.selection_mode_fixed_ratio'),
|
||||
Backend\Widgets\MediaManager::SELECTION_MODE_FIXED_SIZE => trans('backend::lang.media.selection_mode_fixed_size')
|
||||
];
|
||||
|
||||
$sizeDisabledAttr = $currentSelectionMode == Backend\Widgets\MediaManager::SELECTION_MODE_NORMAL ? 'disabled="disabled"' : null;
|
||||
?>
|
||||
|
||||
<div class="control-toolbar toolbar-padded">
|
||||
<div class="toolbar-item toolbar-primary">
|
||||
<div data-control="toolbar">
|
||||
<label class="standalone"><?= e(trans('backend::lang.media.image_size')) ?> <span data-label="width"><?= $dimensions[0] ?></span> x <span data-label="height"><?= $dimensions[1] ?></span></label>
|
||||
|
||||
<div class="btn-group offset-right">
|
||||
<button type="button" class="btn btn-primary standalone" data-command="resize"
|
||||
><?= e(trans('backend::lang.media.resize')) ?></button>
|
||||
|
||||
<button type="button" class="btn btn-primary wn-icon-undo empty" data-command="undo-resizing"></button>
|
||||
</div>
|
||||
|
||||
<label for="mmcropimagewidth"><?= e(trans('backend::lang.media.selection_mode')) ?></label>
|
||||
<select name="selectionMode" class="form-control custom-select w-150" data-control="selection-mode">
|
||||
<?php foreach ($selectionModes as $mode => $name): ?>
|
||||
<option <?= $mode == $currentSelectionMode ? 'selected="selected"' : null ?> value="<?= $mode ?>"><?= e($name) ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
|
||||
<label for="mmcropimagewidth"><?= e(trans('backend::lang.media.width')) ?></label>
|
||||
<input id="mmcropimagewidth" type="text" class="form-control w-100" data-control="crop-width-input" name="selectionWidth" value="<?= e($currentSelectionWidth) ?>" <?= $sizeDisabledAttr ?>/>
|
||||
|
||||
<label for="mmcropimageheight"><?= e(trans('backend::lang.media.height')) ?></label>
|
||||
<input id="mmcropimageheight" type="text" class="form-control w-100" data-control="crop-height-input" name="selectionHeight" value="<?= e($currentSelectionHeight) ?>" <?= $sizeDisabledAttr ?>/>
|
||||
|
||||
<label class="standalone hide" data-label="selection-size"><?= e(trans('backend::lang.media.selected_size')) ?> <span data-label="selection-width"></span> x <span data-label="selection-height"></span></label>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
64
modules/backend/widgets/mediamanager/partials/_filters.php
Normal file
64
modules/backend/widgets/mediamanager/partials/_filters.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<h3 class="section"><?= e(trans('backend::lang.media.display')) ?></h3>
|
||||
|
||||
<ul class="nav nav-stacked selector-group">
|
||||
<li
|
||||
role="presentation"
|
||||
<?php if ($currentFilter == Backend\Widgets\MediaManager::FILTER_ALL): ?>
|
||||
class="active"
|
||||
<?php endif ?>
|
||||
>
|
||||
<a href="#" data-command="set-filter" data-filter="<?= Backend\Widgets\MediaManager::FILTER_ALL ?>">
|
||||
<i class="icon-recycle"></i>
|
||||
|
||||
<?= e(trans('backend::lang.media.filter_all')) ?>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
role="presentation"
|
||||
<?php if ($currentFilter == System\Classes\MediaLibraryItem::FILE_TYPE_IMAGE): ?>
|
||||
class="active"
|
||||
<?php endif ?>
|
||||
>
|
||||
<a href="#" data-command="set-filter" data-filter="<?= System\Classes\MediaLibraryItem::FILE_TYPE_IMAGE ?>">
|
||||
<i class="icon-picture-o"></i>
|
||||
|
||||
<?= e(trans('backend::lang.media.filter_images')) ?>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
role="presentation"
|
||||
<?php if ($currentFilter == System\Classes\MediaLibraryItem::FILE_TYPE_VIDEO): ?>
|
||||
class="active"
|
||||
<?php endif ?>
|
||||
>
|
||||
<a href="#" data-command="set-filter" data-filter="<?= System\Classes\MediaLibraryItem::FILE_TYPE_VIDEO ?>">
|
||||
<i class="icon-video-camera"></i>
|
||||
|
||||
<?= e(trans('backend::lang.media.filter_video')) ?>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
role="presentation"
|
||||
<?php if ($currentFilter == System\Classes\MediaLibraryItem::FILE_TYPE_AUDIO): ?>
|
||||
class="active"
|
||||
<?php endif ?>
|
||||
>
|
||||
<a href="#" data-command="set-filter" data-filter="<?= System\Classes\MediaLibraryItem::FILE_TYPE_AUDIO ?>">
|
||||
<i class="icon-volume-up"></i>
|
||||
|
||||
<?= e(trans('backend::lang.media.filter_audio')) ?>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
role="presentation"
|
||||
<?php if ($currentFilter == System\Classes\MediaLibraryItem::FILE_TYPE_DOCUMENT): ?>
|
||||
class="active"
|
||||
<?php endif ?>
|
||||
>
|
||||
<a href="#" data-command="set-filter" data-filter="<?= System\Classes\MediaLibraryItem::FILE_TYPE_DOCUMENT ?>">
|
||||
<i class="icon-file"></i>
|
||||
|
||||
<?= e(trans('backend::lang.media.filter_documents')) ?>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -0,0 +1,13 @@
|
||||
<ul class="tree-path">
|
||||
<li class="root"><a href="javascript:;" data-type="media-item" data-item-type="folder" data-path="/" data-clear-search="true"><?= e(trans('backend::lang.media.library')) ?></a></li>
|
||||
|
||||
<?php if (!$searchMode): ?>
|
||||
<?php foreach ($pathSegments as $folder => $path): ?>
|
||||
<?php if ($path != '/'): ?>
|
||||
<li><a href="javascript:;" data-type="media-item" data-item-type="folder" data-path="<?= e($path) ?>"><?= basename($folder) ?></a></li>
|
||||
<?php endif ?>
|
||||
<?php endforeach?>
|
||||
<?php else: ?>
|
||||
<li><a href="javascript:;" data-type="media-item"><?= e(trans('backend::lang.media.search')) ?></a></li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
@@ -0,0 +1,16 @@
|
||||
<div class="panel padding-less border-bottom triangle-down">
|
||||
<div class="layout">
|
||||
<div class="layout-cell">
|
||||
<div class="layout-row" id="<?= $this->getId('folder-path') ?>">
|
||||
<?= $this->makePartial('folder-path') ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-cell">
|
||||
<button
|
||||
type="button"
|
||||
data-command="toggle-sidebar"
|
||||
class="wn-icon-sign-out btn-icon pull-right larger <?= !$sidebarVisible ? 'sidebar-hidden' : null ?>">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
$listElementId = $this->getId('item-list');
|
||||
?>
|
||||
|
||||
<ul class="media-list <?= $listClass ?>">
|
||||
<?php if (count($items) > 0 || !$isRootFolder): ?>
|
||||
<?php if (!$isRootFolder && !$searchMode): ?>
|
||||
<li tabindex="0" data-type="media-item" data-item-type="folder" data-root data-path="<?= e(dirname($currentFolder)) ?>">
|
||||
<div class="icon-container folder">
|
||||
<div class="icon-wrapper"><i class="icon-arrow-turn-up"></i></div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<h4 title="<?= e(trans('backend::lang.media.return_to_parent')) ?>"><?= e(trans('backend::lang.media.return_to_parent_label')) ?></h4>
|
||||
</div>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
|
||||
<?php foreach ($items as $item): ?>
|
||||
<?php
|
||||
$itemType = $item->getFileType();
|
||||
?>
|
||||
<li data-type="media-item"
|
||||
data-item-type="<?= $item->type ?>"
|
||||
data-path="<?= e($item->path) ?>"
|
||||
data-title="<?= e(basename($item->path)) ?>"
|
||||
data-size="<?= e($item->sizeToString()) ?>"
|
||||
data-size-bytes="<?= $item->size ?>"
|
||||
data-last-modified="<?= e($item->lastModifiedAsString()) ?>"
|
||||
data-last-modified-ts="<?= $item->lastModified ?>"
|
||||
data-public-url="<?= e($item->publicUrl) ?>"
|
||||
data-document-type="<?= e($itemType) ?>"
|
||||
data-folder="<?= e(dirname($item->path)) ?>"
|
||||
tabindex="0"
|
||||
>
|
||||
<?= $this->makePartial('item-icon', ['itemType'=>$itemType, 'item'=>$item]) ?>
|
||||
|
||||
<div class="info">
|
||||
<h4 title="<?= e(basename($item->path)) ?>">
|
||||
<?= e(basename($item->path)) ?>
|
||||
|
||||
<?php if (!$this->readOnly): ?>
|
||||
<a
|
||||
href="#"
|
||||
data-rename
|
||||
data-control="popup"
|
||||
data-z-index="1200"
|
||||
data-request-data="path: '<?= e($item->path) ?>', listId: '<?= $listElementId ?>', type: '<?= $item->type ?>'"
|
||||
data-handler="<?= $this->getEventHandler('onLoadRenamePopup') ?>"
|
||||
><i data-rename-control class="icon-terminal"></i></a>
|
||||
<?php endif; ?>
|
||||
</h4>
|
||||
<p class="size"><?= e($item->sizeToString()) ?></p>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (count($items) == 0 && $searchMode): ?>
|
||||
<li class="no-data">
|
||||
<?= e(trans('backend::lang.media.no_files_found')) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
@@ -0,0 +1,42 @@
|
||||
<?= Form::open(['class'=>'layout', 'onsubmit'=>'return false']) ?>
|
||||
<div class="layout-row min-size">
|
||||
<?= $this->makePartial('crop-toolbar') ?>
|
||||
</div>
|
||||
<div class="layout-row whiteboard">
|
||||
<?= $this->makePartial('crop-tool-image-area') ?>
|
||||
</div>
|
||||
<div class="layout-row min-size whiteboard">
|
||||
<div class="panel no-padding-bottom border-top">
|
||||
<div class="form-buttons">
|
||||
<div class="pull-right">
|
||||
<button
|
||||
type="button"
|
||||
data-command="insert"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.media.crop_and_insert')) ?>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-dismiss="popup"
|
||||
class="btn btn-default no-margin-right">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="path" value="<?= e($path) ?>">
|
||||
|
||||
<input type="hidden" data-control="dimension-width" value="<?= $dimensions[0] ?>">
|
||||
<input type="hidden" data-control="dimension-height" value="<?= $dimensions[1] ?>">
|
||||
|
||||
<input type="hidden" data-control="original-width" value="<?= $dimensions[0] ?>">
|
||||
<input type="hidden" data-control="original-height" value="<?= $dimensions[1] ?>">
|
||||
|
||||
<input type="hidden" data-control="original-ratio" value="<?= $originalRatio ?>">
|
||||
<input type="hidden" data-control="original-url" value="<?= e($imageUrl) ?>">
|
||||
|
||||
<?= $this->makePartial('resize-image-form') ?>
|
||||
<?= Form::close() ?>
|
||||
45
modules/backend/widgets/mediamanager/partials/_item-icon.php
Normal file
45
modules/backend/widgets/mediamanager/partials/_item-icon.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<div class="icon-container <?= $itemType ?>">
|
||||
<div class="icon-wrapper">
|
||||
<?php
|
||||
$itemIconClass = $this->itemTypeToIconClass($item, $itemType);
|
||||
$extension = substr(strtolower(pathinfo($item->path, PATHINFO_EXTENSION)), 0, 4) ?? '???';
|
||||
|
||||
if ($itemIconClass == 'icon-file'): ?>
|
||||
<svg class="file-icon" viewBox="0 0 250 250" xml:space="preserve">
|
||||
<path d="M62.5,0c-8.594,0 -15.625,7.031 -15.625,15.625l0,218.75c0,8.594 7.031,15.625 15.625,15.625l156.25,0c8.594,0 15.625,-7.031 15.625,-15.625l0,-171.875l-62.5,-62.5l-109.375,0Z" style="fill:#e2e5e7;fill-rule:nonzero;"/>
|
||||
<path d="M187.5,62.5l46.875,0l-62.5,-62.5l0,46.875c0,8.594 7.031,15.625 15.625,15.625Z" style="fill:#b0b7bd;fill-rule:nonzero;"/>
|
||||
<path d="M234.375,109.375l-46.875,-46.875l46.875,0l0,46.875Z" style="fill:#cad1d8;fill-rule:nonzero;"/>
|
||||
<path d="M195.313,210.938l-148.438,-0.001l0,7.813l148.438,0c4.296,0 7.812,-3.516 7.812,-7.813l-0,-7.812c-0,4.297 -3.516,7.813 -7.813,7.813Z" style="fill:#cad1d8;fill-rule:nonzero;"/>
|
||||
<!-- Colour label -->
|
||||
<path class="file-icon-label file-icon-<?= $extension ?>" d="M203.125,203.125c0,4.297 -3.516,7.813 -7.813,7.813l-171.875,-0c-4.296,-0 -7.812,-3.516 -7.812,-7.813l-0,-78.125c-0,-4.297 3.516,-7.813 7.812,-7.813l171.875,0.001c4.297,-0.001 7.813,3.515 7.813,7.812l0,78.125Z"/>
|
||||
<!-- Extension text node -->
|
||||
<text class="file-icon-extension" x="43%" y="170px" dominant-baseline="middle" text-anchor="middle"><?= strtoupper($extension) ?></text>
|
||||
</svg>
|
||||
<?php else :?>
|
||||
<i class="<?= $itemIconClass ?>"></i>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php if (
|
||||
$itemType == System\Classes\MediaLibraryItem::FILE_TYPE_IMAGE
|
||||
&& $thumbnailUrl = $this->getResizedImageUrl($item->path, $thumbnailParams)
|
||||
): ?>
|
||||
<div>
|
||||
<?php if (!$thumbnailUrl): ?>
|
||||
<div
|
||||
class="image-placeholder"
|
||||
data-width="<?= $thumbnailParams['width'] ?>"
|
||||
data-height="<?= $thumbnailParams['height'] ?>"
|
||||
data-path="<?= e($item->path) ?>"
|
||||
data-last-modified="<?= $item->lastModified ?>"
|
||||
id="<?= $this->getPlaceholderId($item) ?>"
|
||||
>
|
||||
<div class="icon-wrapper"><i class="<?= $this->itemTypeToIconClass($item, $itemType) ?>"></i></div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?= $this->makePartial('thumbnail-image', [
|
||||
'imageUrl' => $thumbnailUrl,
|
||||
]) ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
17
modules/backend/widgets/mediamanager/partials/_item-list.php
Normal file
17
modules/backend/widgets/mediamanager/partials/_item-list.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<div class="panel no-padding padding-top">
|
||||
<input type="hidden" data-type="current-folder" value="<?= e($currentFolder) ?>"/>
|
||||
<input type="hidden" data-type="search-mode" value="<?= $searchMode ? 'true' : 'false' ?>"/>
|
||||
<div class="list-container">
|
||||
<?php if (count($items) == 0 && $isRootFolder && !$searchMode): ?>
|
||||
<div class="empty-library"><?= e(trans('backend::lang.media.empty_library')) ?></div>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($viewMode == Backend\Widgets\MediaManager::VIEW_MODE_GRID): ?>
|
||||
<?= $this->makePartial('list-grid') ?>
|
||||
<?php elseif ($viewMode == Backend\Widgets\MediaManager::VIEW_MODE_LIST): ?>
|
||||
<?= $this->makePartial('list-list') ?>
|
||||
<?php else: ?>
|
||||
<?= $this->makePartial('list-tiles') ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,50 @@
|
||||
<div data-control="media-preview-container"></div>
|
||||
|
||||
<script type="text/template" data-control="audio-template">
|
||||
<div class="panel no-padding-bottom">
|
||||
<audio src="{src}" controls>
|
||||
<div class="media-player-fallback panel-embedded">Your browser doesn't support HTML5 audio.</div>
|
||||
</audio>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/template" data-control="video-template">
|
||||
<video src="{src}" controls poster="<?= Url::asset('modules/backend/widgets/mediamanager/assets/images/video-poster.png') ?>">
|
||||
<div class="panel media-player-fallback">Your browser doesn't support HTML5 video.</div>
|
||||
</video>
|
||||
</script>
|
||||
|
||||
<script type="text/template" data-control="image-template">
|
||||
<div class="sidebar-image-placeholder-container"><div class="sidebar-image-placeholder" data-path="{path}" data-last-modified="{last-modified}" data-loading="true" data-control="sidebar-thumbnail"></div></div>
|
||||
</script>
|
||||
|
||||
<script type="text/template" data-control="document-template">
|
||||
<div class="sidebar-document-placeholder-container"><div class="sidebar-document-placeholder" data-path="{path}" data-last-modified="{last-modified}" data-control="sidebar-document"></div></div>
|
||||
</script>
|
||||
|
||||
<script type="text/template" data-control="no-selection-template">
|
||||
<div class="sidebar-image-placeholder-container">
|
||||
<div class="sidebar-image-placeholder no-border">
|
||||
<i class="icon-crop"></i>
|
||||
<p><?= e(trans('backend::lang.media.nothing_selected')) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/template" data-control="multi-selection-template">
|
||||
<div class="sidebar-image-placeholder-container">
|
||||
<div class="sidebar-image-placeholder no-border">
|
||||
<i class="icon-asterisk"></i>
|
||||
<p><?= e(trans('backend::lang.media.multiple_selected')) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/template" data-control="go-up">
|
||||
<div class="sidebar-image-placeholder-container">
|
||||
<div class="sidebar-image-placeholder no-border">
|
||||
<i class="icon-level-up"></i>
|
||||
<p><?= e(trans('backend::lang.media.return_to_parent')) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php if ($this->getFilterDisplay()): ?>
|
||||
<div id="<?= $this->getId('filters') ?>">
|
||||
<?= $this->makePartial('filters') ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?= $this->makePartial('sorting') ?>
|
||||
70
modules/backend/widgets/mediamanager/partials/_list-grid.php
Normal file
70
modules/backend/widgets/mediamanager/partials/_list-grid.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
$listElementId = $this->getId('item-list');
|
||||
?>
|
||||
|
||||
<table class="table data">
|
||||
<col />
|
||||
<col width="130px" />
|
||||
<col width="130px" />
|
||||
|
||||
<tbody class="icons clickable">
|
||||
<?php if (count($items) > 0 || !$isRootFolder): ?>
|
||||
<?php if (!$isRootFolder && !$searchMode): ?>
|
||||
<tr data-type="media-item" data-item-type="folder" data-root data-path="<?= e(dirname($currentFolder)) ?>" tabindex="0">
|
||||
<td><i class="icon-arrow-turn-up"></i>..</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<?php endif ?>
|
||||
|
||||
<?php foreach ($items as $item):
|
||||
$itemType = $item->getFileType();
|
||||
?>
|
||||
|
||||
<tr data-type="media-item"
|
||||
data-item-type="<?= $item->type ?>"
|
||||
data-path="<?= e($item->path) ?>"
|
||||
data-title="<?= e(basename($item->path)) ?>"
|
||||
data-size="<?= e($item->sizeToString()) ?>"
|
||||
data-size-bytes="<?= $item->size ?>"
|
||||
data-last-modified="<?= e($item->lastModifiedAsString()) ?>"
|
||||
data-last-modified-ts="<?= $item->lastModified ?>"
|
||||
data-public-url="<?= e($item->publicUrl) ?>"
|
||||
data-document-type="<?= e($itemType) ?>"
|
||||
data-folder="<?= e(dirname($item->path)) ?>"
|
||||
tabindex="0"
|
||||
>
|
||||
<td>
|
||||
<div class="item-title no-wrap-text">
|
||||
<i class="<?= $this->itemTypeToIconClass($item, $itemType) ?>"></i> <?= e(basename($item->path)) ?>
|
||||
|
||||
<?php if (!$this->readOnly): ?>
|
||||
<a
|
||||
href="#"
|
||||
data-rename
|
||||
data-control="popup"
|
||||
data-request-data="path: '<?= e($item->path) ?>', listId: '<?= $listElementId ?>', type: '<?= $item->type ?>'"
|
||||
data-handler="<?= $this->getEventHandler('onLoadRenamePopup') ?>"
|
||||
data-z-index="1200"
|
||||
><i data-rename-control class="icon-terminal"></i></a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
<td><?= e($item->sizeToString()) ?></td>
|
||||
<td><?= e($item->lastModifiedAsString()) ?></td>
|
||||
<?php if ($searchMode): ?>
|
||||
<td title="<?= e(dirname($item->path)) ?>">
|
||||
<div class="no-wrap-text"><?= e(dirname($item->path)) ?></div>
|
||||
</td>
|
||||
<?php endif ?>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
<?php endif ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php if (count($items) == 0 && $searchMode): ?>
|
||||
<p class="no-data">
|
||||
<?= e(trans('backend::lang.media.no_files_found')) ?>
|
||||
</p>
|
||||
<?php endif ?>
|
||||
@@ -0,0 +1 @@
|
||||
<?= $this->makePartial('generic-list', ['listClass' => 'list']) ?>
|
||||
@@ -0,0 +1 @@
|
||||
<?= $this->makePartial('generic-list', ['listClass' => 'tiles']) ?>
|
||||
35
modules/backend/widgets/mediamanager/partials/_move-form.php
Normal file
35
modules/backend/widgets/mediamanager/partials/_move-form.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?= Form::open() ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('backend::lang.media.move_popup_title')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label><?= e(trans('backend::lang.media.move_destination')) ?></label>
|
||||
<select
|
||||
class="form-control custom-select"
|
||||
name="dest"
|
||||
data-placeholder="<?= e(trans('backend::lang.media.move_please_select')) ?>">
|
||||
<option></option>
|
||||
<?php foreach ($folders as $path => $folder): ?>
|
||||
<option value="<?= e($path) ?>"><?= e($folder) ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
|
||||
<input type="hidden" name="originalPath" value="<?= e($originalPath) ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.media.move_button')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,32 @@
|
||||
<script type="text/template" data-control="new-folder-template">
|
||||
<?= Form::open() ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('backend::lang.media.new_folder_title')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label><?= e(trans('backend::lang.media.folder_name')) ?></label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="name"
|
||||
value=""
|
||||
autocomplete="off" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.apply')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
<?= Form::open(['class'=>'layout', 'onsubmit'=>'return false']) ?>
|
||||
<?= $this->render() ?>
|
||||
<?= Form::close() ?>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?= Form::ajax($this->getEventHandler('onApplyName'), [
|
||||
'success' => "\$el.trigger('close.oc.popup'); \$('#".$listId."').trigger('mediarefresh');",
|
||||
'data-stripe-load-indicator' => 1,
|
||||
'id' => 'media-rename-popup-form'
|
||||
]) ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('backend::lang.media.rename_popup_title')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label><?= e(trans('backend::lang.media.rename_new_name')) ?></label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value="<?= e($name) ?>"
|
||||
class="form-control"
|
||||
autocomplete="off"
|
||||
default-focus />
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="originalName" value="<?= e($name) ?>">
|
||||
<input type="hidden" name="type" value="<?= e($type) ?>">
|
||||
<input type="hidden" name="originalPath" value="<?= e($originalPath) ?>" />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.apply')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
<script>
|
||||
setTimeout(
|
||||
function(){ $('#media-rename-popup-form input.form-control').focus() },
|
||||
310
|
||||
)
|
||||
|
||||
$('#media-rename-popup-form').on('oc.beforeRequest', function(ev){
|
||||
var originalName = $('#media-rename-popup-form [name=originalName]').val(),
|
||||
newName = $.trim($('#media-rename-popup-form [name=name]').val())
|
||||
|
||||
if (originalName == newName || newName.length == 0) {
|
||||
alert('Please enter a new name')
|
||||
|
||||
ev.preventDefault()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<?= Form::close() ?>
|
||||
@@ -0,0 +1,31 @@
|
||||
<script type="text/template" data-control="resize-template">
|
||||
<?= Form::open() ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('backend::lang.media.resize_image')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group span-left">
|
||||
<label><?= e(trans('backend::lang.media.width')) ?></label>
|
||||
<input type="text" class="form-control" name="width" value="" />
|
||||
</div>
|
||||
<div class="form-group span-right">
|
||||
<label><?= e(trans('backend::lang.media.height')) ?></label>
|
||||
<input type="text" class="form-control" name="height" value="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.apply')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?= $this->makePartial('item-sidebar-preview') ?>
|
||||
|
||||
<div class="panel hide" data-control="sidebar-labels">
|
||||
<label><?= e(trans('backend::lang.media.title')) ?></label>
|
||||
<p data-label="title"></p>
|
||||
|
||||
<table class="name-value-list">
|
||||
<tr>
|
||||
<th><?= e(trans('backend::lang.media.size')) ?></th>
|
||||
<td data-label="size"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?= e(trans('backend::lang.media.public_url')) ?></th>
|
||||
<td><a href="#" data-label="public-url" target="_blank"><?= e(trans('backend::lang.media.click_here')) ?></a></td>
|
||||
</tr>
|
||||
<tr data-control="last-modified">
|
||||
<th><?= e(trans('backend::lang.media.last_modified')) ?></th>
|
||||
<td data-label="last-modified"></td>
|
||||
</tr>
|
||||
|
||||
<tr data-control="item-folder" class="hide">
|
||||
<th><?= e(trans('backend::lang.media.folder')) ?></th>
|
||||
<td><a href="#" data-type="media-item" data-item-type="folder" data-label="folder" data-clear-search="true"></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
46
modules/backend/widgets/mediamanager/partials/_sorting.php
Normal file
46
modules/backend/widgets/mediamanager/partials/_sorting.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
$sortModes = [
|
||||
System\Classes\MediaLibrary::SORT_BY_TITLE => trans('backend::lang.media.title'),
|
||||
System\Classes\MediaLibrary::SORT_BY_SIZE => trans('backend::lang.media.size'),
|
||||
System\Classes\MediaLibrary::SORT_BY_MODIFIED => trans('backend::lang.media.last_modified')
|
||||
];
|
||||
|
||||
$sortDirections = [
|
||||
System\Classes\MediaLibrary::SORT_DIRECTION_ASC => trans('backend::lang.media.direction_asc'),
|
||||
System\Classes\MediaLibrary::SORT_DIRECTION_DESC => trans('backend::lang.media.direction_desc')
|
||||
];
|
||||
?>
|
||||
|
||||
<div class="sidebar-group">
|
||||
<h3 class="section"><?= e(trans('backend::lang.media.order_by')) ?></h3>
|
||||
|
||||
<select
|
||||
name="sorting"
|
||||
class="form-control custom-select select-no-search"
|
||||
data-control="sorting"
|
||||
data-sort="by">
|
||||
<?php foreach ($sortModes as $code => $title): ?>
|
||||
<option
|
||||
<?= $code == $sortBy ? 'selected="selected"' : '' ?>
|
||||
value="<?= $code ?>"
|
||||
><?= e($title) ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-group">
|
||||
<h3 class="section"><?= e(trans('backend::lang.media.direction')) ?></h3>
|
||||
|
||||
<select
|
||||
name="sorting"
|
||||
class="form-control custom-select select-no-search"
|
||||
data-control="sorting"
|
||||
data-sort="direction">
|
||||
<?php foreach ($sortDirections as $code => $title): ?>
|
||||
<option
|
||||
<?= $code == $sortDirection ? 'selected="selected"' : '' ?>
|
||||
value="<?= $code ?>"
|
||||
><?= e($title) ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php if ($imageUrl): ?>
|
||||
<img src="<?= $imageUrl ?>"/>
|
||||
<?php else: ?>
|
||||
<i class="icon-chain-broken" title="<?= e(trans('backend::lang.media.thumbnail_error')) ?>"></i>
|
||||
<p class="thumbnail-error-message"><?= e(trans('backend::lang.media.thumbnail_error')) ?></p>
|
||||
<?php endif ?>
|
||||
42
modules/backend/widgets/mediamanager/partials/_toolbar.php
Normal file
42
modules/backend/widgets/mediamanager/partials/_toolbar.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<div class="layout-row min-size">
|
||||
<div class="control-toolbar toolbar-padded">
|
||||
<div class="toolbar-item toolbar-primary">
|
||||
<div data-control="toolbar">
|
||||
<?php if (!$this->readOnly): ?>
|
||||
<div class="btn-group offset-right">
|
||||
<button type="button" class="btn btn-primary wn-icon-upload" data-control="upload"><?= e(trans('backend::lang.media.upload')) ?></button>
|
||||
<button type="button" class="btn btn-primary wn-icon-folder" data-command="create-folder"><?= e(trans('backend::lang.media.add_folder')) ?></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<button type="button" class="btn btn-default wn-icon-refresh empty offset-right" data-command="refresh"></button>
|
||||
|
||||
<?php if (!$this->readOnly): ?>
|
||||
<div class="btn-group offset-right">
|
||||
<button type="button" class="btn btn-default wn-icon-reply-all" data-command="move"><?= e(trans('backend::lang.media.move')) ?></button>
|
||||
<button type="button" class="btn btn-default wn-icon-trash" data-command="delete"><?= e(trans('backend::lang.media.delete')) ?></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="btn-group offset-right" id="<?= $this->getId('view-mode-buttons') ?>">
|
||||
<?= $this->makePartial('view-mode-buttons') ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolbar-item" data-calculate-width>
|
||||
<div class="relative loading-indicator-container size-input-text">
|
||||
<input
|
||||
type="text"
|
||||
name="search"
|
||||
value="<?= e($searchTerm) ?>"
|
||||
class="form-control icon search growable"
|
||||
placeholder="<?= e(trans('backend::lang.media.search')) ?>"
|
||||
data-control="search"
|
||||
autocomplete="off"
|
||||
data-load-indicator
|
||||
data-load-indicator-opaque
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
<div class="layout-row min-size hide" data-control="upload-ui">
|
||||
<div class="layout">
|
||||
<div class="upload-progress">
|
||||
<h5
|
||||
data-label="file-number-and-progress"
|
||||
data-message-template="<?= e(trans('backend::lang.media.uploading_file_num')) ?> <span>:percents</span>"
|
||||
data-success-template="<?= e(trans('backend::lang.media.uploading_complete')) ?>"
|
||||
data-error-template="<?= e(trans('backend::lang.media.uploading_error')) ?>"
|
||||
></h5>
|
||||
|
||||
<div class="progress-controls">
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" style="width: 0;" data-control="upload-progress-bar">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<a href="#" data-command="cancel-uploading"><i class="icon-times-circle" title=""></i></a>
|
||||
<a class="hide" href="#" data-command="close-uploader"><i class="icon-check-circle" title=""></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,18 @@
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default wn-icon-align-justify empty <?= $viewMode == Backend\Widgets\MediaManager::VIEW_MODE_GRID ? 'on' : '' ?>"
|
||||
data-command="change-view"
|
||||
data-view="<?= Backend\Widgets\MediaManager::VIEW_MODE_GRID ?>">
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default wn-icon-th empty <?= $viewMode == Backend\Widgets\MediaManager::VIEW_MODE_LIST ? 'on' : '' ?>"
|
||||
data-command="change-view"
|
||||
data-view="<?= Backend\Widgets\MediaManager::VIEW_MODE_LIST ?>">
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default wn-icon-th-large empty <?= $viewMode == Backend\Widgets\MediaManager::VIEW_MODE_TILES ? 'on' : '' ?>"
|
||||
data-command="change-view"
|
||||
data-view="<?= Backend\Widgets\MediaManager::VIEW_MODE_TILES ?>">
|
||||
</button>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user