svg
@ -11,8 +11,14 @@ body {
|
|||||||
|
|
||||||
.avatar{
|
.avatar{
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
width:40px;
|
width:35px;
|
||||||
height:40px;
|
height:35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bigavatar{
|
||||||
|
border-radius: 100%;
|
||||||
|
width:100px;
|
||||||
|
height:100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-brand img {
|
.navbar-brand img {
|
||||||
|
@ -2,3 +2,22 @@ function ModalLoad(idmodal,title,path) {
|
|||||||
$("#"+idmodal+" .modal-header h4").text(title);
|
$("#"+idmodal+" .modal-header h4").text(title);
|
||||||
$("#"+idmodal+" #framemodal").attr("src",path);
|
$("#"+idmodal+" #framemodal").attr("src",path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
|
||||||
|
$("#selectcompany").on("change", function() {
|
||||||
|
url=$(this).data("change");
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: {
|
||||||
|
id: $(this).val()
|
||||||
|
},
|
||||||
|
success: function (result) {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
3
public/lib/fontawesome/FontAwesome-v5.0.9-Free.json
Normal file
BIN
public/lib/imgareaselect/css/border-anim-h.gif
Executable file
After Width: | Height: | Size: 219 B |
BIN
public/lib/imgareaselect/css/border-anim-v.gif
Executable file
After Width: | Height: | Size: 219 B |
BIN
public/lib/imgareaselect/css/border-h.gif
Executable file
After Width: | Height: | Size: 72 B |
BIN
public/lib/imgareaselect/css/border-v.gif
Executable file
After Width: | Height: | Size: 72 B |
41
public/lib/imgareaselect/css/imgareaselect-animated.css
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* imgAreaSelect animated border style
|
||||||
|
*/
|
||||||
|
|
||||||
|
.imgareaselect-border1 {
|
||||||
|
background: url(border-anim-v.gif) repeat-y left top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-border2 {
|
||||||
|
background: url(border-anim-h.gif) repeat-x left top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-border3 {
|
||||||
|
background: url(border-anim-v.gif) repeat-y right top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-border4 {
|
||||||
|
background: url(border-anim-h.gif) repeat-x left bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-border1, .imgareaselect-border2,
|
||||||
|
.imgareaselect-border3, .imgareaselect-border4 {
|
||||||
|
filter: alpha(opacity=50);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-handle {
|
||||||
|
background-color: #fff;
|
||||||
|
border: solid 1px #000;
|
||||||
|
filter: alpha(opacity=50);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-outer {
|
||||||
|
background-color: #000;
|
||||||
|
filter: alpha(opacity=50);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-selection {
|
||||||
|
}
|
41
public/lib/imgareaselect/css/imgareaselect-default.css
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* imgAreaSelect default style
|
||||||
|
*/
|
||||||
|
|
||||||
|
.imgareaselect-border1 {
|
||||||
|
background: url(border-v.gif) repeat-y left top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-border2 {
|
||||||
|
background: url(border-h.gif) repeat-x left top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-border3 {
|
||||||
|
background: url(border-v.gif) repeat-y right top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-border4 {
|
||||||
|
background: url(border-h.gif) repeat-x left bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-border1, .imgareaselect-border2,
|
||||||
|
.imgareaselect-border3, .imgareaselect-border4 {
|
||||||
|
filter: alpha(opacity=50);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-handle {
|
||||||
|
background-color: #fff;
|
||||||
|
border: solid 1px #000;
|
||||||
|
filter: alpha(opacity=50);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-outer {
|
||||||
|
background-color: #000;
|
||||||
|
filter: alpha(opacity=50);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-selection {
|
||||||
|
}
|
36
public/lib/imgareaselect/css/imgareaselect-deprecated.css
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* imgAreaSelect style to be used with deprecated options
|
||||||
|
*/
|
||||||
|
|
||||||
|
.imgareaselect-border1, .imgareaselect-border2,
|
||||||
|
.imgareaselect-border3, .imgareaselect-border4 {
|
||||||
|
filter: alpha(opacity=50);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-border1 {
|
||||||
|
border: solid 1px #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-border2 {
|
||||||
|
border: dashed 1px #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-handle {
|
||||||
|
background-color: #fff;
|
||||||
|
border: solid 1px #000;
|
||||||
|
filter: alpha(opacity=50);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-outer {
|
||||||
|
background-color: #000;
|
||||||
|
filter: alpha(opacity=40);
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgareaselect-selection {
|
||||||
|
background-color: #fff;
|
||||||
|
filter: alpha(opacity=0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
1205
public/lib/imgareaselect/js/jquery.imgareaselect.dev.js
Normal file
@ -0,0 +1,1205 @@
|
|||||||
|
/*
|
||||||
|
* imgAreaSelect jQuery plugin
|
||||||
|
* version 0.9.11-rc.1
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2013 Michal Wojciechowski (odyniec.net)
|
||||||
|
*
|
||||||
|
* Dual licensed under the MIT (MIT-LICENSE.txt)
|
||||||
|
* and GPL (GPL-LICENSE.txt) licenses.
|
||||||
|
*
|
||||||
|
* http://odyniec.net/projects/imgareaselect/
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function($) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Math functions will be used extensively, so it's convenient to make a few
|
||||||
|
* shortcuts
|
||||||
|
*/
|
||||||
|
var abs = Math.abs,
|
||||||
|
max = Math.max,
|
||||||
|
min = Math.min,
|
||||||
|
round = Math.round;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new HTML div element
|
||||||
|
*
|
||||||
|
* @return A jQuery object representing the new element
|
||||||
|
*/
|
||||||
|
function div() {
|
||||||
|
return $('<div/>');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* imgAreaSelect initialization
|
||||||
|
*
|
||||||
|
* @param img
|
||||||
|
* A HTML image element to attach the plugin to
|
||||||
|
* @param options
|
||||||
|
* An options object
|
||||||
|
*/
|
||||||
|
$.imgAreaSelect = function (img, options) {
|
||||||
|
var
|
||||||
|
/* jQuery object representing the image */
|
||||||
|
$img = $(img),
|
||||||
|
|
||||||
|
/* Has the image finished loading? */
|
||||||
|
imgLoaded,
|
||||||
|
|
||||||
|
/* Plugin elements */
|
||||||
|
|
||||||
|
/* Container box */
|
||||||
|
$box = div(),
|
||||||
|
/* Selection area */
|
||||||
|
$area = div(),
|
||||||
|
/* Border (four divs) */
|
||||||
|
$border = div().add(div()).add(div()).add(div()),
|
||||||
|
/* Outer area (four divs) */
|
||||||
|
$outer = div().add(div()).add(div()).add(div()),
|
||||||
|
/* Handles (empty by default, initialized in setOptions()) */
|
||||||
|
$handles = $([]),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Additional element to work around a cursor problem in Opera
|
||||||
|
* (explained later)
|
||||||
|
*/
|
||||||
|
$areaOpera,
|
||||||
|
|
||||||
|
/* Image position (relative to viewport) */
|
||||||
|
left, top,
|
||||||
|
|
||||||
|
/* Image offset (as returned by .offset()) */
|
||||||
|
imgOfs = { left: 0, top: 0 },
|
||||||
|
|
||||||
|
/* Image dimensions (as returned by .width() and .height()) */
|
||||||
|
imgWidth, imgHeight,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* jQuery object representing the parent element that the plugin
|
||||||
|
* elements are appended to
|
||||||
|
*/
|
||||||
|
$parent,
|
||||||
|
|
||||||
|
/* Parent element offset (as returned by .offset()) */
|
||||||
|
parOfs = { left: 0, top: 0 },
|
||||||
|
|
||||||
|
/* Base z-index for plugin elements */
|
||||||
|
zIndex = 0,
|
||||||
|
|
||||||
|
/* Plugin elements position */
|
||||||
|
position = 'absolute',
|
||||||
|
|
||||||
|
/* X/Y coordinates of the starting point for move/resize operations */
|
||||||
|
startX, startY,
|
||||||
|
|
||||||
|
/* Horizontal and vertical scaling factors */
|
||||||
|
scaleX, scaleY,
|
||||||
|
|
||||||
|
/* Current resize mode ("nw", "se", etc.) */
|
||||||
|
resize,
|
||||||
|
|
||||||
|
/* Selection area constraints */
|
||||||
|
minWidth, minHeight, maxWidth, maxHeight,
|
||||||
|
|
||||||
|
/* Aspect ratio to maintain (floating point number) */
|
||||||
|
aspectRatio,
|
||||||
|
|
||||||
|
/* Are the plugin elements currently displayed? */
|
||||||
|
shown,
|
||||||
|
|
||||||
|
/* Current selection (relative to parent element) */
|
||||||
|
x1, y1, x2, y2,
|
||||||
|
|
||||||
|
/* Current selection (relative to scaled image) */
|
||||||
|
selection = { x1: 0, y1: 0, x2: 0, y2: 0, width: 0, height: 0 },
|
||||||
|
|
||||||
|
/* Document element */
|
||||||
|
docElem = document.documentElement,
|
||||||
|
|
||||||
|
/* User agent */
|
||||||
|
ua = navigator.userAgent,
|
||||||
|
|
||||||
|
/* Various helper variables used throughout the code */
|
||||||
|
$p, d, i, o, w, h, adjusted;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Translate selection coordinates (relative to scaled image) to viewport
|
||||||
|
* coordinates (relative to parent element)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate selection X to viewport X
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* Selection X
|
||||||
|
* @return Viewport X
|
||||||
|
*/
|
||||||
|
function viewX(x) {
|
||||||
|
return x + imgOfs.left - parOfs.left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate selection Y to viewport Y
|
||||||
|
*
|
||||||
|
* @param y
|
||||||
|
* Selection Y
|
||||||
|
* @return Viewport Y
|
||||||
|
*/
|
||||||
|
function viewY(y) {
|
||||||
|
return y + imgOfs.top - parOfs.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Translate viewport coordinates to selection coordinates
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate viewport X to selection X
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* Viewport X
|
||||||
|
* @return Selection X
|
||||||
|
*/
|
||||||
|
function selX(x) {
|
||||||
|
return x - imgOfs.left + parOfs.left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate viewport Y to selection Y
|
||||||
|
*
|
||||||
|
* @param y
|
||||||
|
* Viewport Y
|
||||||
|
* @return Selection Y
|
||||||
|
*/
|
||||||
|
function selY(y) {
|
||||||
|
return y - imgOfs.top + parOfs.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Translate event coordinates (relative to document) to viewport
|
||||||
|
* coordinates
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get event X and translate it to viewport X
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* The event object
|
||||||
|
* @return Viewport X
|
||||||
|
*/
|
||||||
|
function evX(event) {
|
||||||
|
return event.pageX - parOfs.left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get event Y and translate it to viewport Y
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* The event object
|
||||||
|
* @return Viewport Y
|
||||||
|
*/
|
||||||
|
function evY(event) {
|
||||||
|
return event.pageY - parOfs.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current selection
|
||||||
|
*
|
||||||
|
* @param noScale
|
||||||
|
* If set to <code>true</code>, scaling is not applied to the
|
||||||
|
* returned selection
|
||||||
|
* @return Selection object
|
||||||
|
*/
|
||||||
|
function getSelection(noScale) {
|
||||||
|
var sx = noScale || scaleX, sy = noScale || scaleY;
|
||||||
|
|
||||||
|
return { x1: round(selection.x1 * sx),
|
||||||
|
y1: round(selection.y1 * sy),
|
||||||
|
x2: round(selection.x2 * sx),
|
||||||
|
y2: round(selection.y2 * sy),
|
||||||
|
width: round(selection.x2 * sx) - round(selection.x1 * sx),
|
||||||
|
height: round(selection.y2 * sy) - round(selection.y1 * sy) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current selection
|
||||||
|
*
|
||||||
|
* @param x1
|
||||||
|
* X coordinate of the upper left corner of the selection area
|
||||||
|
* @param y1
|
||||||
|
* Y coordinate of the upper left corner of the selection area
|
||||||
|
* @param x2
|
||||||
|
* X coordinate of the lower right corner of the selection area
|
||||||
|
* @param y2
|
||||||
|
* Y coordinate of the lower right corner of the selection area
|
||||||
|
* @param noScale
|
||||||
|
* If set to <code>true</code>, scaling is not applied to the
|
||||||
|
* new selection
|
||||||
|
*/
|
||||||
|
function setSelection(x1, y1, x2, y2, noScale) {
|
||||||
|
var sx = noScale || scaleX, sy = noScale || scaleY;
|
||||||
|
|
||||||
|
selection = {
|
||||||
|
x1: round(x1 / sx || 0),
|
||||||
|
y1: round(y1 / sy || 0),
|
||||||
|
x2: round(x2 / sx || 0),
|
||||||
|
y2: round(y2 / sy || 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
selection.width = selection.x2 - selection.x1;
|
||||||
|
selection.height = selection.y2 - selection.y1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recalculate image and parent offsets
|
||||||
|
*/
|
||||||
|
function adjust() {
|
||||||
|
/*
|
||||||
|
* Do not adjust if image has not yet loaded or if width is not a
|
||||||
|
* positive number. The latter might happen when imgAreaSelect is put
|
||||||
|
* on a parent element which is then hidden.
|
||||||
|
*/
|
||||||
|
if (!imgLoaded || !$img.width())
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get image offset. The .offset() method returns float values, so they
|
||||||
|
* need to be rounded.
|
||||||
|
*/
|
||||||
|
imgOfs = { left: round($img.offset().left), top: round($img.offset().top) };
|
||||||
|
|
||||||
|
/* Get image dimensions */
|
||||||
|
imgWidth = $img.innerWidth();
|
||||||
|
imgHeight = $img.innerHeight();
|
||||||
|
|
||||||
|
imgOfs.top += ($img.outerHeight() - imgHeight) >> 1;
|
||||||
|
imgOfs.left += ($img.outerWidth() - imgWidth) >> 1;
|
||||||
|
|
||||||
|
/* Set minimum and maximum selection area dimensions */
|
||||||
|
minWidth = round(options.minWidth / scaleX) || 0;
|
||||||
|
minHeight = round(options.minHeight / scaleY) || 0;
|
||||||
|
maxWidth = round(min(options.maxWidth / scaleX || 1<<24, imgWidth));
|
||||||
|
maxHeight = round(min(options.maxHeight / scaleY || 1<<24, imgHeight));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Workaround for jQuery 1.3.2 incorrect offset calculation, originally
|
||||||
|
* observed in Safari 3. Firefox 2 is also affected.
|
||||||
|
*/
|
||||||
|
if ($().jquery == '1.3.2' && position == 'fixed' &&
|
||||||
|
!docElem['getBoundingClientRect'])
|
||||||
|
{
|
||||||
|
imgOfs.top += max(document.body.scrollTop, docElem.scrollTop);
|
||||||
|
imgOfs.left += max(document.body.scrollLeft, docElem.scrollLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine parent element offset */
|
||||||
|
parOfs = /absolute|relative/.test($parent.css('position')) ?
|
||||||
|
{ left: round($parent.offset().left) - $parent.scrollLeft(),
|
||||||
|
top: round($parent.offset().top) - $parent.scrollTop() } :
|
||||||
|
position == 'fixed' ?
|
||||||
|
{ left: $(document).scrollLeft(), top: $(document).scrollTop() } :
|
||||||
|
{ left: 0, top: 0 };
|
||||||
|
|
||||||
|
left = viewX(0);
|
||||||
|
top = viewY(0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if selection area is within image boundaries, adjust if
|
||||||
|
* necessary
|
||||||
|
*/
|
||||||
|
if (selection.x2 > imgWidth || selection.y2 > imgHeight)
|
||||||
|
doResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update plugin elements
|
||||||
|
*
|
||||||
|
* @param resetKeyPress
|
||||||
|
* If set to <code>false</code>, this instance's keypress
|
||||||
|
* event handler is not activated
|
||||||
|
*/
|
||||||
|
function update(resetKeyPress) {
|
||||||
|
/* If plugin elements are hidden, do nothing */
|
||||||
|
if (!shown) return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the position and size of the container box and the selection area
|
||||||
|
* inside it
|
||||||
|
*/
|
||||||
|
$box.css({ left: viewX(selection.x1), top: viewY(selection.y1) })
|
||||||
|
.add($area).width(w = selection.width).height(h = selection.height);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset the position of selection area, borders, and handles (IE6/IE7
|
||||||
|
* position them incorrectly if we don't do this)
|
||||||
|
*/
|
||||||
|
$area.add($border).add($handles).css({ left: 0, top: 0 });
|
||||||
|
|
||||||
|
/* Set border dimensions */
|
||||||
|
$border
|
||||||
|
.width(max(w - $border.outerWidth() + $border.innerWidth(), 0))
|
||||||
|
.height(max(h - $border.outerHeight() + $border.innerHeight(), 0));
|
||||||
|
|
||||||
|
/* Arrange the outer area elements */
|
||||||
|
$($outer[0]).css({ left: left, top: top,
|
||||||
|
width: selection.x1, height: imgHeight });
|
||||||
|
$($outer[1]).css({ left: left + selection.x1, top: top,
|
||||||
|
width: w, height: selection.y1 });
|
||||||
|
$($outer[2]).css({ left: left + selection.x2, top: top,
|
||||||
|
width: imgWidth - selection.x2, height: imgHeight });
|
||||||
|
$($outer[3]).css({ left: left + selection.x1, top: top + selection.y2,
|
||||||
|
width: w, height: imgHeight - selection.y2 });
|
||||||
|
|
||||||
|
w -= $handles.outerWidth();
|
||||||
|
h -= $handles.outerHeight();
|
||||||
|
|
||||||
|
/* Arrange handles */
|
||||||
|
switch ($handles.length) {
|
||||||
|
case 8:
|
||||||
|
$($handles[4]).css({ left: w >> 1 });
|
||||||
|
$($handles[5]).css({ left: w, top: h >> 1 });
|
||||||
|
$($handles[6]).css({ left: w >> 1, top: h });
|
||||||
|
$($handles[7]).css({ top: h >> 1 });
|
||||||
|
case 4:
|
||||||
|
$handles.slice(1,3).css({ left: w });
|
||||||
|
$handles.slice(2,4).css({ top: h });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resetKeyPress !== false) {
|
||||||
|
/*
|
||||||
|
* Need to reset the document keypress event handler -- unbind the
|
||||||
|
* current handler
|
||||||
|
*/
|
||||||
|
if ($.imgAreaSelect.onKeyPress != docKeyPress)
|
||||||
|
$(document).unbind($.imgAreaSelect.keyPress,
|
||||||
|
$.imgAreaSelect.onKeyPress);
|
||||||
|
|
||||||
|
if (options.keys)
|
||||||
|
/*
|
||||||
|
* Set the document keypress event handler to this instance's
|
||||||
|
* docKeyPress() function
|
||||||
|
*/
|
||||||
|
$(document)[$.imgAreaSelect.keyPress](
|
||||||
|
$.imgAreaSelect.onKeyPress = docKeyPress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internet Explorer displays 1px-wide dashed borders incorrectly by
|
||||||
|
* filling the spaces between dashes with white. Toggling the margin
|
||||||
|
* property between 0 and "auto" fixes this in IE6 and IE7 (IE8 is still
|
||||||
|
* broken). This workaround is not perfect, as it requires setTimeout()
|
||||||
|
* and thus causes the border to flicker a bit, but I haven't found a
|
||||||
|
* better solution.
|
||||||
|
*
|
||||||
|
* Note: This only happens with CSS borders, set with the borderWidth,
|
||||||
|
* borderOpacity, borderColor1, and borderColor2 options (which are now
|
||||||
|
* deprecated). Borders created with GIF background images are fine.
|
||||||
|
*/
|
||||||
|
if (msie && $border.outerWidth() - $border.innerWidth() == 2) {
|
||||||
|
$border.css('margin', 0);
|
||||||
|
setTimeout(function () { $border.css('margin', 'auto'); }, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do the complete update sequence: recalculate offsets, update the
|
||||||
|
* elements, and set the correct values of x1, y1, x2, and y2.
|
||||||
|
*
|
||||||
|
* @param resetKeyPress
|
||||||
|
* If set to <code>false</code>, this instance's keypress
|
||||||
|
* event handler is not activated
|
||||||
|
*/
|
||||||
|
function doUpdate(resetKeyPress) {
|
||||||
|
adjust();
|
||||||
|
update(resetKeyPress);
|
||||||
|
x1 = viewX(selection.x1); y1 = viewY(selection.y1);
|
||||||
|
x2 = viewX(selection.x2); y2 = viewY(selection.y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide or fade out an element (or multiple elements)
|
||||||
|
*
|
||||||
|
* @param $elem
|
||||||
|
* A jQuery object containing the element(s) to hide/fade out
|
||||||
|
* @param fn
|
||||||
|
* Callback function to be called when fadeOut() completes
|
||||||
|
*/
|
||||||
|
function hide($elem, fn) {
|
||||||
|
options.fadeSpeed ? $elem.fadeOut(options.fadeSpeed, fn) : $elem.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selection area mousemove event handler
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* The event object
|
||||||
|
*/
|
||||||
|
function areaMouseMove(event) {
|
||||||
|
var x = selX(evX(event)) - selection.x1,
|
||||||
|
y = selY(evY(event)) - selection.y1;
|
||||||
|
|
||||||
|
if (!adjusted) {
|
||||||
|
adjust();
|
||||||
|
adjusted = true;
|
||||||
|
|
||||||
|
$box.one('mouseout', function () { adjusted = false; });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the resize mode */
|
||||||
|
resize = '';
|
||||||
|
|
||||||
|
if (options.resizable) {
|
||||||
|
/*
|
||||||
|
* Check if the mouse pointer is over the resize margin area and set
|
||||||
|
* the resize mode accordingly
|
||||||
|
*/
|
||||||
|
if (y <= options.resizeMargin)
|
||||||
|
resize = 'n';
|
||||||
|
else if (y >= selection.height - options.resizeMargin)
|
||||||
|
resize = 's';
|
||||||
|
if (x <= options.resizeMargin)
|
||||||
|
resize += 'w';
|
||||||
|
else if (x >= selection.width - options.resizeMargin)
|
||||||
|
resize += 'e';
|
||||||
|
}
|
||||||
|
|
||||||
|
$box.css('cursor', resize ? resize + '-resize' :
|
||||||
|
options.movable ? 'move' : '');
|
||||||
|
if ($areaOpera)
|
||||||
|
$areaOpera.toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document mouseup event handler
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* The event object
|
||||||
|
*/
|
||||||
|
function docMouseUp(event) {
|
||||||
|
/* Set back the default cursor */
|
||||||
|
$('body').css('cursor', '');
|
||||||
|
/*
|
||||||
|
* If autoHide is enabled, or if the selection has zero width/height,
|
||||||
|
* hide the selection and the outer area
|
||||||
|
*/
|
||||||
|
if (options.autoHide || selection.width * selection.height == 0)
|
||||||
|
hide($box.add($outer), function () { $(this).hide(); });
|
||||||
|
|
||||||
|
$(document).unbind('mousemove', selectingMouseMove);
|
||||||
|
$box.mousemove(areaMouseMove);
|
||||||
|
|
||||||
|
options.onSelectEnd(img, getSelection());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selection area mousedown event handler
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* The event object
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
function areaMouseDown(event) {
|
||||||
|
if (event.which != 1) return false;
|
||||||
|
|
||||||
|
adjust();
|
||||||
|
|
||||||
|
if (resize) {
|
||||||
|
/* Resize mode is in effect */
|
||||||
|
$('body').css('cursor', resize + '-resize');
|
||||||
|
|
||||||
|
x1 = viewX(selection[/w/.test(resize) ? 'x2' : 'x1']);
|
||||||
|
y1 = viewY(selection[/n/.test(resize) ? 'y2' : 'y1']);
|
||||||
|
|
||||||
|
$(document).mousemove(selectingMouseMove)
|
||||||
|
.one('mouseup', docMouseUp);
|
||||||
|
$box.unbind('mousemove', areaMouseMove);
|
||||||
|
}
|
||||||
|
else if (options.movable) {
|
||||||
|
startX = left + selection.x1 - evX(event);
|
||||||
|
startY = top + selection.y1 - evY(event);
|
||||||
|
|
||||||
|
$box.unbind('mousemove', areaMouseMove);
|
||||||
|
|
||||||
|
$(document).mousemove(movingMouseMove)
|
||||||
|
.one('mouseup', function () {
|
||||||
|
options.onSelectEnd(img, getSelection());
|
||||||
|
|
||||||
|
$(document).unbind('mousemove', movingMouseMove);
|
||||||
|
$box.mousemove(areaMouseMove);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$img.mousedown(event);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust the x2/y2 coordinates to maintain aspect ratio (if defined)
|
||||||
|
*
|
||||||
|
* @param xFirst
|
||||||
|
* If set to <code>true</code>, calculate x2 first. Otherwise,
|
||||||
|
* calculate y2 first.
|
||||||
|
*/
|
||||||
|
function fixAspectRatio(xFirst) {
|
||||||
|
if (aspectRatio)
|
||||||
|
if (xFirst) {
|
||||||
|
x2 = max(left, min(left + imgWidth,
|
||||||
|
x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1)));
|
||||||
|
y2 = round(max(top, min(top + imgHeight,
|
||||||
|
y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1))));
|
||||||
|
x2 = round(x2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
y2 = max(top, min(top + imgHeight,
|
||||||
|
y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1)));
|
||||||
|
x2 = round(max(left, min(left + imgWidth,
|
||||||
|
x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1))));
|
||||||
|
y2 = round(y2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize the selection area respecting the minimum/maximum dimensions and
|
||||||
|
* aspect ratio
|
||||||
|
*/
|
||||||
|
function doResize() {
|
||||||
|
/*
|
||||||
|
* Make sure the top left corner of the selection area stays within
|
||||||
|
* image boundaries (it might not if the image source was dynamically
|
||||||
|
* changed).
|
||||||
|
*/
|
||||||
|
x1 = min(x1, left + imgWidth);
|
||||||
|
y1 = min(y1, top + imgHeight);
|
||||||
|
|
||||||
|
if (abs(x2 - x1) < minWidth) {
|
||||||
|
/* Selection width is smaller than minWidth */
|
||||||
|
x2 = x1 - minWidth * (x2 < x1 || -1);
|
||||||
|
|
||||||
|
if (x2 < left)
|
||||||
|
x1 = left + minWidth;
|
||||||
|
else if (x2 > left + imgWidth)
|
||||||
|
x1 = left + imgWidth - minWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abs(y2 - y1) < minHeight) {
|
||||||
|
/* Selection height is smaller than minHeight */
|
||||||
|
y2 = y1 - minHeight * (y2 < y1 || -1);
|
||||||
|
|
||||||
|
if (y2 < top)
|
||||||
|
y1 = top + minHeight;
|
||||||
|
else if (y2 > top + imgHeight)
|
||||||
|
y1 = top + imgHeight - minHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
x2 = max(left, min(x2, left + imgWidth));
|
||||||
|
y2 = max(top, min(y2, top + imgHeight));
|
||||||
|
|
||||||
|
fixAspectRatio(abs(x2 - x1) < abs(y2 - y1) * aspectRatio);
|
||||||
|
|
||||||
|
if (abs(x2 - x1) > maxWidth) {
|
||||||
|
/* Selection width is greater than maxWidth */
|
||||||
|
x2 = x1 - maxWidth * (x2 < x1 || -1);
|
||||||
|
fixAspectRatio();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abs(y2 - y1) > maxHeight) {
|
||||||
|
/* Selection height is greater than maxHeight */
|
||||||
|
y2 = y1 - maxHeight * (y2 < y1 || -1);
|
||||||
|
fixAspectRatio(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
selection = { x1: selX(min(x1, x2)), x2: selX(max(x1, x2)),
|
||||||
|
y1: selY(min(y1, y2)), y2: selY(max(y1, y2)),
|
||||||
|
width: abs(x2 - x1), height: abs(y2 - y1) };
|
||||||
|
|
||||||
|
update();
|
||||||
|
|
||||||
|
options.onSelectChange(img, getSelection());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mousemove event handler triggered when the user is selecting an area
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* The event object
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
function selectingMouseMove(event) {
|
||||||
|
x2 = /w|e|^$/.test(resize) || aspectRatio ? evX(event) : viewX(selection.x2);
|
||||||
|
y2 = /n|s|^$/.test(resize) || aspectRatio ? evY(event) : viewY(selection.y2);
|
||||||
|
|
||||||
|
doResize();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the selection area
|
||||||
|
*
|
||||||
|
* @param newX1
|
||||||
|
* New viewport X1
|
||||||
|
* @param newY1
|
||||||
|
* New viewport Y1
|
||||||
|
*/
|
||||||
|
function doMove(newX1, newY1) {
|
||||||
|
x2 = (x1 = newX1) + selection.width;
|
||||||
|
y2 = (y1 = newY1) + selection.height;
|
||||||
|
|
||||||
|
$.extend(selection, { x1: selX(x1), y1: selY(y1), x2: selX(x2),
|
||||||
|
y2: selY(y2) });
|
||||||
|
|
||||||
|
update();
|
||||||
|
|
||||||
|
options.onSelectChange(img, getSelection());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mousemove event handler triggered when the selection area is being moved
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* The event object
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
function movingMouseMove(event) {
|
||||||
|
x1 = max(left, min(startX + evX(event), left + imgWidth - selection.width));
|
||||||
|
y1 = max(top, min(startY + evY(event), top + imgHeight - selection.height));
|
||||||
|
|
||||||
|
doMove(x1, y1);
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start selection
|
||||||
|
*/
|
||||||
|
function startSelection() {
|
||||||
|
$(document).unbind('mousemove', startSelection);
|
||||||
|
adjust();
|
||||||
|
|
||||||
|
x2 = x1;
|
||||||
|
y2 = y1;
|
||||||
|
doResize();
|
||||||
|
|
||||||
|
resize = '';
|
||||||
|
|
||||||
|
if (!$outer.is(':visible'))
|
||||||
|
/* Show the plugin elements */
|
||||||
|
$box.add($outer).hide().fadeIn(options.fadeSpeed||0);
|
||||||
|
|
||||||
|
shown = true;
|
||||||
|
|
||||||
|
$(document).unbind('mouseup', cancelSelection)
|
||||||
|
.mousemove(selectingMouseMove).one('mouseup', docMouseUp);
|
||||||
|
$box.unbind('mousemove', areaMouseMove);
|
||||||
|
|
||||||
|
options.onSelectStart(img, getSelection());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel selection
|
||||||
|
*/
|
||||||
|
function cancelSelection() {
|
||||||
|
$(document).unbind('mousemove', startSelection)
|
||||||
|
.unbind('mouseup', cancelSelection);
|
||||||
|
hide($box.add($outer));
|
||||||
|
|
||||||
|
setSelection(selX(x1), selY(y1), selX(x1), selY(y1));
|
||||||
|
|
||||||
|
/* If this is an API call, callback functions should not be triggered */
|
||||||
|
if (!(this instanceof $.imgAreaSelect)) {
|
||||||
|
options.onSelectChange(img, getSelection());
|
||||||
|
options.onSelectEnd(img, getSelection());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image mousedown event handler
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* The event object
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
function imgMouseDown(event) {
|
||||||
|
/* Ignore the event if animation is in progress */
|
||||||
|
if (event.which != 1 || $outer.is(':animated')) return false;
|
||||||
|
|
||||||
|
adjust();
|
||||||
|
startX = x1 = evX(event);
|
||||||
|
startY = y1 = evY(event);
|
||||||
|
|
||||||
|
/* Selection will start when the mouse is moved */
|
||||||
|
$(document).mousemove(startSelection).mouseup(cancelSelection);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Window resize event handler
|
||||||
|
*/
|
||||||
|
function windowResize() {
|
||||||
|
doUpdate(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image load event handler. This is the final part of the initialization
|
||||||
|
* process.
|
||||||
|
*/
|
||||||
|
function imgLoad() {
|
||||||
|
imgLoaded = true;
|
||||||
|
|
||||||
|
/* Set options */
|
||||||
|
setOptions(options = $.extend({
|
||||||
|
classPrefix: 'imgareaselect',
|
||||||
|
movable: true,
|
||||||
|
parent: 'body',
|
||||||
|
resizable: true,
|
||||||
|
resizeMargin: 10,
|
||||||
|
onInit: function () {},
|
||||||
|
onSelectStart: function () {},
|
||||||
|
onSelectChange: function () {},
|
||||||
|
onSelectEnd: function () {}
|
||||||
|
}, options));
|
||||||
|
|
||||||
|
$box.add($outer).css({ visibility: '' });
|
||||||
|
|
||||||
|
if (options.show) {
|
||||||
|
shown = true;
|
||||||
|
adjust();
|
||||||
|
update();
|
||||||
|
$box.add($outer).hide().fadeIn(options.fadeSpeed||0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call the onInit callback. The setTimeout() call is used to ensure
|
||||||
|
* that the plugin has been fully initialized and the object instance is
|
||||||
|
* available (so that it can be obtained in the callback).
|
||||||
|
*/
|
||||||
|
setTimeout(function () { options.onInit(img, getSelection()); }, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Document keypress event handler
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* The event object
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
var docKeyPress = function(event) {
|
||||||
|
var k = options.keys, d, t, key = event.keyCode;
|
||||||
|
|
||||||
|
d = !isNaN(k.alt) && (event.altKey || event.originalEvent.altKey) ? k.alt :
|
||||||
|
!isNaN(k.ctrl) && event.ctrlKey ? k.ctrl :
|
||||||
|
!isNaN(k.shift) && event.shiftKey ? k.shift :
|
||||||
|
!isNaN(k.arrows) ? k.arrows : 10;
|
||||||
|
|
||||||
|
if (k.arrows == 'resize' || (k.shift == 'resize' && event.shiftKey) ||
|
||||||
|
(k.ctrl == 'resize' && event.ctrlKey) ||
|
||||||
|
(k.alt == 'resize' && (event.altKey || event.originalEvent.altKey)))
|
||||||
|
{
|
||||||
|
/* Resize selection */
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case 37:
|
||||||
|
/* Left */
|
||||||
|
d = -d;
|
||||||
|
case 39:
|
||||||
|
/* Right */
|
||||||
|
t = max(x1, x2);
|
||||||
|
x1 = min(x1, x2);
|
||||||
|
x2 = max(t + d, x1);
|
||||||
|
fixAspectRatio();
|
||||||
|
break;
|
||||||
|
case 38:
|
||||||
|
/* Up */
|
||||||
|
d = -d;
|
||||||
|
case 40:
|
||||||
|
/* Down */
|
||||||
|
t = max(y1, y2);
|
||||||
|
y1 = min(y1, y2);
|
||||||
|
y2 = max(t + d, y1);
|
||||||
|
fixAspectRatio(true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
doResize();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Move selection */
|
||||||
|
|
||||||
|
x1 = min(x1, x2);
|
||||||
|
y1 = min(y1, y2);
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case 37:
|
||||||
|
/* Left */
|
||||||
|
doMove(max(x1 - d, left), y1);
|
||||||
|
break;
|
||||||
|
case 38:
|
||||||
|
/* Up */
|
||||||
|
doMove(x1, max(y1 - d, top));
|
||||||
|
break;
|
||||||
|
case 39:
|
||||||
|
/* Right */
|
||||||
|
doMove(x1 + min(d, imgWidth - selX(x2)), y1);
|
||||||
|
break;
|
||||||
|
case 40:
|
||||||
|
/* Down */
|
||||||
|
doMove(x1, y1 + min(d, imgHeight - selY(y2)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply style options to plugin element (or multiple elements)
|
||||||
|
*
|
||||||
|
* @param $elem
|
||||||
|
* A jQuery object representing the element(s) to style
|
||||||
|
* @param props
|
||||||
|
* An object that maps option names to corresponding CSS
|
||||||
|
* properties
|
||||||
|
*/
|
||||||
|
function styleOptions($elem, props) {
|
||||||
|
for (var option in props)
|
||||||
|
if (options[option] !== undefined)
|
||||||
|
$elem.css(props[option], options[option]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set plugin options
|
||||||
|
*
|
||||||
|
* @param newOptions
|
||||||
|
* The new options object
|
||||||
|
*/
|
||||||
|
function setOptions(newOptions) {
|
||||||
|
if (newOptions.parent)
|
||||||
|
($parent = $(newOptions.parent)).append($box).append($outer);
|
||||||
|
|
||||||
|
/* Merge the new options with the existing ones */
|
||||||
|
$.extend(options, newOptions);
|
||||||
|
|
||||||
|
adjust();
|
||||||
|
|
||||||
|
if (newOptions.handles != null) {
|
||||||
|
/* Recreate selection area handles */
|
||||||
|
$handles.remove();
|
||||||
|
$handles = $([]);
|
||||||
|
|
||||||
|
i = newOptions.handles ? newOptions.handles == 'corners' ? 4 : 8 : 0;
|
||||||
|
|
||||||
|
while (i--)
|
||||||
|
$handles = $handles.add(div());
|
||||||
|
|
||||||
|
/* Add a class to handles and set the CSS properties */
|
||||||
|
$handles.addClass(options.classPrefix + '-handle').css({
|
||||||
|
position: 'absolute',
|
||||||
|
/*
|
||||||
|
* The font-size property needs to be set to zero, otherwise
|
||||||
|
* Internet Explorer makes the handles too large
|
||||||
|
*/
|
||||||
|
fontSize: 0,
|
||||||
|
zIndex: zIndex + 1 || 1
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If handle width/height has not been set with CSS rules, set the
|
||||||
|
* default 5px
|
||||||
|
*/
|
||||||
|
if (!parseInt($handles.css('width')) >= 0)
|
||||||
|
$handles.width(5).height(5);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the borderWidth option is in use, add a solid border to
|
||||||
|
* handles
|
||||||
|
*/
|
||||||
|
if (o = options.borderWidth)
|
||||||
|
$handles.css({ borderWidth: o, borderStyle: 'solid' });
|
||||||
|
|
||||||
|
/* Apply other style options */
|
||||||
|
styleOptions($handles, { borderColor1: 'border-color',
|
||||||
|
borderColor2: 'background-color',
|
||||||
|
borderOpacity: 'opacity' });
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate scale factors */
|
||||||
|
scaleX = options.imageWidth / imgWidth || 1;
|
||||||
|
scaleY = options.imageHeight / imgHeight || 1;
|
||||||
|
|
||||||
|
/* Set selection */
|
||||||
|
if (newOptions.x1 != null) {
|
||||||
|
setSelection(newOptions.x1, newOptions.y1, newOptions.x2,
|
||||||
|
newOptions.y2);
|
||||||
|
newOptions.show = !newOptions.hide;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newOptions.keys)
|
||||||
|
/* Enable keyboard support */
|
||||||
|
options.keys = $.extend({ shift: 1, ctrl: 'resize' },
|
||||||
|
newOptions.keys);
|
||||||
|
|
||||||
|
/* Add classes to plugin elements */
|
||||||
|
$outer.addClass(options.classPrefix + '-outer');
|
||||||
|
$area.addClass(options.classPrefix + '-selection');
|
||||||
|
for (i = 0; i++ < 4;)
|
||||||
|
$($border[i-1]).addClass(options.classPrefix + '-border' + i);
|
||||||
|
|
||||||
|
/* Apply style options */
|
||||||
|
styleOptions($area, { selectionColor: 'background-color',
|
||||||
|
selectionOpacity: 'opacity' });
|
||||||
|
styleOptions($border, { borderOpacity: 'opacity',
|
||||||
|
borderWidth: 'border-width' });
|
||||||
|
styleOptions($outer, { outerColor: 'background-color',
|
||||||
|
outerOpacity: 'opacity' });
|
||||||
|
if (o = options.borderColor1)
|
||||||
|
$($border[0]).css({ borderStyle: 'solid', borderColor: o });
|
||||||
|
if (o = options.borderColor2)
|
||||||
|
$($border[1]).css({ borderStyle: 'dashed', borderColor: o });
|
||||||
|
|
||||||
|
/* Append all the selection area elements to the container box */
|
||||||
|
$box.append($area.add($border).add($areaOpera)).append($handles);
|
||||||
|
|
||||||
|
if (msie) {
|
||||||
|
if (o = ($outer.css('filter')||'').match(/opacity=(\d+)/))
|
||||||
|
$outer.css('opacity', o[1]/100);
|
||||||
|
if (o = ($border.css('filter')||'').match(/opacity=(\d+)/))
|
||||||
|
$border.css('opacity', o[1]/100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newOptions.hide)
|
||||||
|
hide($box.add($outer));
|
||||||
|
else if (newOptions.show && imgLoaded) {
|
||||||
|
shown = true;
|
||||||
|
$box.add($outer).fadeIn(options.fadeSpeed||0);
|
||||||
|
doUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the aspect ratio factor */
|
||||||
|
aspectRatio = (d = (options.aspectRatio || '').split(/:/))[0] / d[1];
|
||||||
|
|
||||||
|
$img.add($outer).unbind('mousedown', imgMouseDown);
|
||||||
|
|
||||||
|
if (options.disable || options.enable === false) {
|
||||||
|
/* Disable the plugin */
|
||||||
|
$box.unbind('mousemove', areaMouseMove).unbind('mousedown', areaMouseDown);
|
||||||
|
$(window).unbind('resize', windowResize);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (options.enable || options.disable === false) {
|
||||||
|
/* Enable the plugin */
|
||||||
|
if (options.resizable || options.movable)
|
||||||
|
$box.mousemove(areaMouseMove).mousedown(areaMouseDown);
|
||||||
|
|
||||||
|
$(window).resize(windowResize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.persistent)
|
||||||
|
$img.add($outer).mousedown(imgMouseDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
options.enable = options.disable = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove plugin completely
|
||||||
|
*/
|
||||||
|
this.remove = function () {
|
||||||
|
/*
|
||||||
|
* Call setOptions with { disable: true } to unbind the event handlers
|
||||||
|
*/
|
||||||
|
setOptions({ disable: true });
|
||||||
|
$box.add($outer).remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Public API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current options
|
||||||
|
*
|
||||||
|
* @return An object containing the set of options currently in use
|
||||||
|
*/
|
||||||
|
this.getOptions = function () { return options; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set plugin options
|
||||||
|
*
|
||||||
|
* @param newOptions
|
||||||
|
* The new options object
|
||||||
|
*/
|
||||||
|
this.setOptions = setOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current selection
|
||||||
|
*
|
||||||
|
* @param noScale
|
||||||
|
* If set to <code>true</code>, scaling is not applied to the
|
||||||
|
* returned selection
|
||||||
|
* @return Selection object
|
||||||
|
*/
|
||||||
|
this.getSelection = getSelection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current selection
|
||||||
|
*
|
||||||
|
* @param x1
|
||||||
|
* X coordinate of the upper left corner of the selection area
|
||||||
|
* @param y1
|
||||||
|
* Y coordinate of the upper left corner of the selection area
|
||||||
|
* @param x2
|
||||||
|
* X coordinate of the lower right corner of the selection area
|
||||||
|
* @param y2
|
||||||
|
* Y coordinate of the lower right corner of the selection area
|
||||||
|
* @param noScale
|
||||||
|
* If set to <code>true</code>, scaling is not applied to the
|
||||||
|
* new selection
|
||||||
|
*/
|
||||||
|
this.setSelection = setSelection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel selection
|
||||||
|
*/
|
||||||
|
this.cancelSelection = cancelSelection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update plugin elements
|
||||||
|
*
|
||||||
|
* @param resetKeyPress
|
||||||
|
* If set to <code>false</code>, this instance's keypress
|
||||||
|
* event handler is not activated
|
||||||
|
*/
|
||||||
|
this.update = doUpdate;
|
||||||
|
|
||||||
|
/* Do the dreaded browser detection */
|
||||||
|
var msie = (/msie ([\w.]+)/i.exec(ua)||[])[1],
|
||||||
|
opera = /opera/i.test(ua),
|
||||||
|
safari = /webkit/i.test(ua) && !/chrome/i.test(ua);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Traverse the image's parent elements (up to <body>) and find the
|
||||||
|
* highest z-index
|
||||||
|
*/
|
||||||
|
$p = $img;
|
||||||
|
|
||||||
|
while ($p.length) {
|
||||||
|
zIndex = max(zIndex,
|
||||||
|
!isNaN($p.css('z-index')) ? $p.css('z-index') : zIndex);
|
||||||
|
/* Also check if any of the ancestor elements has fixed position */
|
||||||
|
if ($p.css('position') == 'fixed')
|
||||||
|
position = 'fixed';
|
||||||
|
|
||||||
|
$p = $p.parent(':not(body)');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If z-index is given as an option, it overrides the one found by the
|
||||||
|
* above loop
|
||||||
|
*/
|
||||||
|
zIndex = options.zIndex || zIndex;
|
||||||
|
|
||||||
|
if (msie)
|
||||||
|
$img.attr('unselectable', 'on');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In MSIE and WebKit, we need to use the keydown event instead of keypress
|
||||||
|
*/
|
||||||
|
$.imgAreaSelect.keyPress = msie || safari ? 'keydown' : 'keypress';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is a bug affecting the CSS cursor property in Opera (observed in
|
||||||
|
* versions up to 10.00) that prevents the cursor from being updated unless
|
||||||
|
* the mouse leaves and enters the element again. To trigger the mouseover
|
||||||
|
* event, we're adding an additional div to $box and we're going to toggle
|
||||||
|
* it when mouse moves inside the selection area.
|
||||||
|
*/
|
||||||
|
if (opera)
|
||||||
|
$areaOpera = div().css({ width: '100%', height: '100%',
|
||||||
|
position: 'absolute', zIndex: zIndex + 2 || 2 });
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We initially set visibility to "hidden" as a workaround for a weird
|
||||||
|
* behaviour observed in Google Chrome 1.0.154.53 (on Windows XP). Normally
|
||||||
|
* we would just set display to "none", but, for some reason, if we do so
|
||||||
|
* then Chrome refuses to later display the element with .show() or
|
||||||
|
* .fadeIn().
|
||||||
|
*/
|
||||||
|
$box.add($outer).css({ visibility: 'hidden', position: position,
|
||||||
|
overflow: 'hidden', zIndex: zIndex || '0' });
|
||||||
|
$box.css({ zIndex: zIndex + 2 || 2 });
|
||||||
|
$area.add($border).css({ position: 'absolute', fontSize: 0 });
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the image has been fully loaded, or if it is not really an image (eg.
|
||||||
|
* a div), call imgLoad() immediately; otherwise, bind it to be called once
|
||||||
|
* on image load event.
|
||||||
|
*/
|
||||||
|
img.complete || img.readyState == 'complete' || !$img.is('img') ?
|
||||||
|
imgLoad() : $img.one('load', imgLoad);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MSIE 9.0 doesn't always fire the image load event -- resetting the src
|
||||||
|
* attribute seems to trigger it. The check is for version 7 and above to
|
||||||
|
* accommodate for MSIE 9 running in compatibility mode.
|
||||||
|
*/
|
||||||
|
if (!imgLoaded && msie && msie >= 7)
|
||||||
|
img.src = img.src;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke imgAreaSelect on a jQuery object containing the image(s)
|
||||||
|
*
|
||||||
|
* @param options
|
||||||
|
* Options object
|
||||||
|
* @return The jQuery object or a reference to imgAreaSelect instance (if the
|
||||||
|
* <code>instance</code> option was specified)
|
||||||
|
*/
|
||||||
|
$.fn.imgAreaSelect = function (options) {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
this.each(function () {
|
||||||
|
/* Is there already an imgAreaSelect instance bound to this element? */
|
||||||
|
if ($(this).data('imgAreaSelect')) {
|
||||||
|
/* Yes there is -- is it supposed to be removed? */
|
||||||
|
if (options.remove) {
|
||||||
|
/* Remove the plugin */
|
||||||
|
$(this).data('imgAreaSelect').remove();
|
||||||
|
$(this).removeData('imgAreaSelect');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* Reset options */
|
||||||
|
$(this).data('imgAreaSelect').setOptions(options);
|
||||||
|
}
|
||||||
|
else if (!options.remove) {
|
||||||
|
/* No exising instance -- create a new one */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If neither the "enable" nor the "disable" option is present, add
|
||||||
|
* "enable" as the default
|
||||||
|
*/
|
||||||
|
if (options.enable === undefined && options.disable === undefined)
|
||||||
|
options.enable = true;
|
||||||
|
|
||||||
|
$(this).data('imgAreaSelect', new $.imgAreaSelect(this, options));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.instance)
|
||||||
|
/*
|
||||||
|
* Return the imgAreaSelect instance bound to the first element in the
|
||||||
|
* set
|
||||||
|
*/
|
||||||
|
return $(this).data('imgAreaSelect');
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
})(jQuery);
|
3
public/lib/select2/select2.init.js
vendored
@ -1,5 +1,4 @@
|
|||||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fr",[],function(){return{errorLoading:function(){return"Les résultats ne peuvent pas être chargés."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Supprimez "+t+" caractère"+(t>1)?"s":""},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Saisissez au moins "+t+" caractère"+(t>1)?"s":""},loadingMore:function(){return"Chargement de résultats supplémentaires…"},maximumSelected:function(e){return"Vous pouvez seulement sélectionner "+e.maximum+" élément"+(e.maximum>1)?"s":""},noResults:function(){return"Aucun résultat trouvé"},searching:function(){return"Recherche en cours…"}}}),{define:e.define,require:e.require}})();
|
$(document).ready(function(){
|
||||||
$(() => {
|
|
||||||
$('.select2').select2(
|
$('.select2').select2(
|
||||||
{
|
{
|
||||||
language: "fr",
|
language: "fr",
|
||||||
|
BIN
public/uploads/avatar/6755c306c70e3.jpg
Normal file
After Width: | Height: | Size: 598 KiB |
BIN
public/uploads/avatar/6755c33c48442.jpg
Normal file
After Width: | Height: | Size: 598 KiB |
BIN
public/uploads/avatar/6755c3e4605d5.jpeg
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
public/uploads/avatar/6755c480910c9.jpg
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
public/uploads/avatar/6755c4acac2c8.jpg
Normal file
After Width: | Height: | Size: 598 KiB |
BIN
public/uploads/avatar/6755c5ad0b1fb.jpeg
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
public/uploads/avatar/6755c5bc9f6af.jpg
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
public/uploads/avatar/6755c5d54e967.jpeg
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
public/uploads/avatar/6755c63be9d98.jpg
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
public/uploads/avatar/6755cd1809e70.jpg
Normal file
After Width: | Height: | Size: 315 KiB |
BIN
public/uploads/avatar/6755cd47262e7.jpg
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
public/uploads/avatar/6755cd8c6ead0.webp
Normal file
After Width: | Height: | Size: 274 KiB |
BIN
public/uploads/avatar/6755cded42349.jpg
Normal file
After Width: | Height: | Size: 598 KiB |
BIN
public/uploads/avatar/6755ce2851b15.jpeg
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
public/uploads/avatar/6755ce9065632.jpg
Normal file
After Width: | Height: | Size: 262 KiB |
BIN
public/uploads/avatar/6755cea677d58.webp
Normal file
After Width: | Height: | Size: 274 KiB |
BIN
public/uploads/avatar/6755ceb7b5b2b.jpg
Normal file
After Width: | Height: | Size: 262 KiB |
BIN
public/uploads/avatar/6755cf4965e56.jpg
Normal file
After Width: | Height: | Size: 598 KiB |
BIN
public/uploads/avatar/6755cf71f0a2e.jpg
Normal file
After Width: | Height: | Size: 262 KiB |
BIN
public/uploads/avatar/6755d03b757e8.jpg
Normal file
After Width: | Height: | Size: 262 KiB |
BIN
public/uploads/avatar/6755d0f260125.jpg
Normal file
After Width: | Height: | Size: 262 KiB |
BIN
public/uploads/avatar/6755d1936285a.jpg
Normal file
After Width: | Height: | Size: 598 KiB |
BIN
public/uploads/avatar/6755d2c6b8d1a.webp
Normal file
After Width: | Height: | Size: 149 KiB |
BIN
public/uploads/avatar/6755d30b21927.webp
Normal file
After Width: | Height: | Size: 283 KiB |
BIN
public/uploads/avatar/6755d33aa8297.jpg
Normal file
After Width: | Height: | Size: 370 KiB |
BIN
public/uploads/avatar/67573d494838e.jpg
Normal file
After Width: | Height: | Size: 598 KiB |
BIN
public/uploads/avatar/67573d9e340db.webp
Normal file
After Width: | Height: | Size: 274 KiB |
BIN
public/uploads/avatar/67573e27aa9f6.webp
Normal file
After Width: | Height: | Size: 274 KiB |
BIN
public/uploads/avatar/67573f17ebfb1.webp
Normal file
After Width: | Height: | Size: 283 KiB |
BIN
public/uploads/avatar/67573f3773ec9.webp
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
public/uploads/avatar/67573f6dcb273.jpg
Normal file
After Width: | Height: | Size: 102 KiB |
BIN
public/uploads/avatar/67573f8e618d0.webp
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
public/uploads/avatar/67573fb157b7c.jpg
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
public/uploads/avatar/67573fc08649b.jpg
Normal file
After Width: | Height: | Size: 160 KiB |
BIN
public/uploads/avatar/6757400c07998.jpg
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
public/uploads/avatar/675740139651f.webp
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
public/uploads/avatar/6757402c16f06.jpg
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
public/uploads/avatar/675740f397386.jpeg
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
public/uploads/avatar/675741016fefd.jpg
Normal file
After Width: | Height: | Size: 138 KiB |
BIN
public/uploads/avatar/6757443c77760.jpg
Normal file
After Width: | Height: | Size: 173 KiB |
BIN
public/uploads/avatar/67574c62b8e9f.webp
Normal file
After Width: | Height: | Size: 135 KiB |
BIN
public/uploads/avatar/thumb_6755c5ad0b1fb.jpeg
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
public/uploads/avatar/thumb_6755c5bc9f6af.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
public/uploads/avatar/thumb_6755c5d54e967.jpeg
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
public/uploads/avatar/thumb_6755c63be9d98.jpg
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
public/uploads/avatar/thumb_6755cd1809e70.jpg
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
public/uploads/avatar/thumb_6755cd47262e7.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
public/uploads/avatar/thumb_6755cded42349.jpg
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
public/uploads/avatar/thumb_6755ce2851b15.jpeg
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
public/uploads/avatar/thumb_6755ce9065632.jpg
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
public/uploads/avatar/thumb_6755ceb7b5b2b.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
public/uploads/avatar/thumb_6755cf4965e56.jpg
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
public/uploads/avatar/thumb_6755cf71f0a2e.jpg
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
public/uploads/avatar/thumb_6755d03b757e8.jpg
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
public/uploads/avatar/thumb_6755d0f260125.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
public/uploads/avatar/thumb_6755d1936285a.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
public/uploads/avatar/thumb_6755d2c6b8d1a.webp
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
public/uploads/avatar/thumb_6755d30b21927.webp
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
public/uploads/avatar/thumb_6755d33aa8297.jpg
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
public/uploads/avatar/thumb_67573d494838e.jpg
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
public/uploads/avatar/thumb_67573d9e340db.webp
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
public/uploads/avatar/thumb_67573f6dcb273.jpg
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
public/uploads/avatar/thumb_67573f8e618d0.webp
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
public/uploads/avatar/thumb_67573fb157b7c.jpg
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
public/uploads/avatar/thumb_67573fc08649b.jpg
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
public/uploads/avatar/thumb_675740139651f.webp
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
public/uploads/avatar/thumb_6757402c16f06.jpg
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
public/uploads/avatar/thumb_675741016fefd.jpg
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
public/uploads/avatar/thumb_6757443c77760.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
public/uploads/avatar/thumb_67574c62b8e9f.webp
Normal file
After Width: | Height: | Size: 8.7 KiB |
@ -39,10 +39,19 @@ class InitCommand extends Command
|
|||||||
$io->text('Initialisation of the app');
|
$io->text('Initialisation of the app');
|
||||||
$io->text('');
|
$io->text('');
|
||||||
|
|
||||||
// Création du compte admin
|
// Création d'un company par defaut
|
||||||
$io->text('> Création du compte admin');
|
$io->text("> Création d'un company par defaut");
|
||||||
|
$company = $this->em->getRepository("App\Entity\Company")->findOneBy([], ['id' => 'ASC']);
|
||||||
|
if (!$company) {
|
||||||
|
$company = new Company();
|
||||||
|
$company->setTitle($this->params->get('appName'));
|
||||||
|
$this->em->persist($company);
|
||||||
|
$this->em->flush();
|
||||||
|
}
|
||||||
|
|
||||||
$user = $this->em->getRepository("App\Entity\User")->findOneBy(['username' => 'admin']);
|
$user = $this->em->getRepository("App\Entity\User")->findOneBy(['username' => 'admin']);
|
||||||
if (!$user) {
|
if (!$user) {
|
||||||
|
$io->text('> Création du compte admin par defaut');
|
||||||
$user = new User();
|
$user = new User();
|
||||||
|
|
||||||
$hashedPassword = $this->passwordHasher->hashPassword(
|
$hashedPassword = $this->passwordHasher->hashPassword(
|
||||||
@ -54,22 +63,13 @@ class InitCommand extends Command
|
|||||||
$user->setPassword($hashedPassword);
|
$user->setPassword($hashedPassword);
|
||||||
$user->setAvatar('medias/avatar/admin.jpg');
|
$user->setAvatar('medias/avatar/admin.jpg');
|
||||||
$user->setEmail($this->params->get('appNoreply'));
|
$user->setEmail($this->params->get('appNoreply'));
|
||||||
|
$user->addCompany($company);
|
||||||
|
$user->setCompany($company);
|
||||||
$this->em->persist($user);
|
$this->em->persist($user);
|
||||||
}
|
}
|
||||||
$user->setRoles(['ROLE_ADMIN']);
|
$user->setRoles(['ROLE_ADMIN']);
|
||||||
$this->em->flush();
|
$this->em->flush();
|
||||||
|
|
||||||
// Création d'un company par defaut
|
|
||||||
$io->text("> Création d'un company par defaut");
|
|
||||||
$nbcompanys = $this->em->getRepository("App\Entity\Company")->count([]);
|
|
||||||
if (0 == $nbcompanys) {
|
|
||||||
$company = new Company();
|
|
||||||
$company->setTitle($this->params->get('appName'));
|
|
||||||
$company->setLogo('logo.png');
|
|
||||||
$this->em->persist($company);
|
|
||||||
$this->em->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
108
src/Controller/AccountingController.php
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\Accounting;
|
||||||
|
use App\Form\AccountingType;
|
||||||
|
use App\Repository\AccountingRepository;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|
||||||
|
class AccountingController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route('/user/accounting', name: 'app_user_accounting')]
|
||||||
|
public function list(Request $request, AccountingRepository $accountingRepository): Response
|
||||||
|
{
|
||||||
|
$company = $request->getSession()->get('company');
|
||||||
|
$accountings = $accountingRepository->findBy(['company' => $company], ['num01' => 'ASC', 'num02' => 'ASC']);
|
||||||
|
|
||||||
|
return $this->render('accounting/list.html.twig', [
|
||||||
|
'usemenu' => true,
|
||||||
|
'usesidebar' => true,
|
||||||
|
'title' => 'Plan Comptable',
|
||||||
|
'routesubmit' => 'app_user_accounting_submit',
|
||||||
|
'routeupdate' => 'app_user_accounting_update',
|
||||||
|
'accountings' => $accountings,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/user/accounting/submit', name: 'app_user_accounting_submit')]
|
||||||
|
public function submit(Request $request, EntityManagerInterface $em): Response
|
||||||
|
{
|
||||||
|
$company = $request->getSession()->get('company');
|
||||||
|
$accounting = new Accounting();
|
||||||
|
$accounting->setCompany($company);
|
||||||
|
|
||||||
|
$form = $this->createForm(AccountingType::class, $accounting, ['mode' => 'submit']);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$em->persist($accounting);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_user_accounting');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('accounting/edit.html.twig', [
|
||||||
|
'usemenu' => true,
|
||||||
|
'usesidebar' => true,
|
||||||
|
'title' => 'Création Compagnie',
|
||||||
|
'routecancel' => 'app_user_accounting',
|
||||||
|
'routedelete' => 'app_user_accounting_delete',
|
||||||
|
'mode' => 'submit',
|
||||||
|
'form' => $form,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/user/accounting/update/{id}', name: 'app_user_accounting_update')]
|
||||||
|
public function update(int $id, Request $request, EntityManagerInterface $em): Response
|
||||||
|
{
|
||||||
|
$company = $request->getSession()->get('company');
|
||||||
|
$accounting = $em->getRepository(Accounting::class)->find($id);
|
||||||
|
if (!$accounting || $accounting->getCompany() != $company) {
|
||||||
|
return $this->redirectToRoute('app_user_accounting');
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = $this->createForm(AccountingType::class, $accounting, ['mode' => 'update']);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_user_accounting');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('accounting/edit.html.twig', [
|
||||||
|
'usemenu' => true,
|
||||||
|
'usesidebar' => true,
|
||||||
|
'title' => 'Modification Compagnie = '.$accounting->getTitle(),
|
||||||
|
'routecancel' => 'app_user_accounting',
|
||||||
|
'routedelete' => 'app_user_accounting_delete',
|
||||||
|
'mode' => 'update',
|
||||||
|
'form' => $form,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/user/accounting/delete/{id}', name: 'app_user_accounting_delete')]
|
||||||
|
public function delete(int $id, Request $request, EntityManagerInterface $em): Response
|
||||||
|
{
|
||||||
|
$company = $request->getSession()->get('company');
|
||||||
|
$accounting = $em->getRepository(Accounting::class)->find($id);
|
||||||
|
if (!$accounting || $accounting->getCompany() != $company) {
|
||||||
|
return $this->redirectToRoute('app_user_accounting');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tentative de suppression
|
||||||
|
try {
|
||||||
|
$em->remove($accounting);
|
||||||
|
$em->flush();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->addflash('error', $e->getMessage());
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_user_accounting_update', ['id' => $id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_user_accounting');
|
||||||
|
}
|
||||||
|
}
|
@ -2,17 +2,103 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\Company;
|
||||||
|
use App\Form\CompanyType;
|
||||||
|
use App\Repository\CompanyRepository;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|
||||||
class CompanyController extends AbstractController
|
class CompanyController extends AbstractController
|
||||||
{
|
{
|
||||||
#[Route('/admin/company', name: 'app_company')]
|
#[Route('/admin/company', name: 'app_admin_company')]
|
||||||
public function index(): Response
|
public function list(CompanyRepository $companyRepository): Response
|
||||||
{
|
{
|
||||||
return $this->render('company/index.html.twig', [
|
$companys = $companyRepository->findAll();
|
||||||
'controller_name' => 'CompanyController',
|
|
||||||
|
return $this->render('company/list.html.twig', [
|
||||||
|
'usemenu' => true,
|
||||||
|
'usesidebar' => true,
|
||||||
|
'title' => 'Liste des Compagnies',
|
||||||
|
'routesubmit' => 'app_admin_company_submit',
|
||||||
|
'routeupdate' => 'app_admin_company_update',
|
||||||
|
'companys' => $companys,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/admin/company/submit', name: 'app_admin_company_submit')]
|
||||||
|
public function submit(Request $request, EntityManagerInterface $em): Response
|
||||||
|
{
|
||||||
|
$company = new Company();
|
||||||
|
|
||||||
|
$form = $this->createForm(CompanyType::class, $company, ['mode' => 'submit']);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$em->persist($company);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_admin_company');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('company/edit.html.twig', [
|
||||||
|
'usemenu' => true,
|
||||||
|
'usesidebar' => true,
|
||||||
|
'title' => 'Création Compagnie',
|
||||||
|
'routecancel' => 'app_admin_company',
|
||||||
|
'routedelete' => 'app_admin_company_delete',
|
||||||
|
'mode' => 'submit',
|
||||||
|
'form' => $form,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/admin/company/update/{id}', name: 'app_admin_company_update')]
|
||||||
|
public function update(int $id, Request $request, EntityManagerInterface $em): Response
|
||||||
|
{
|
||||||
|
$company = $em->getRepository(Company::class)->find($id);
|
||||||
|
if (!$company) {
|
||||||
|
return $this->redirectToRoute('app_admin_company');
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = $this->createForm(CompanyType::class, $company, ['mode' => 'update']);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_admin_user');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('company/edit.html.twig', [
|
||||||
|
'usemenu' => true,
|
||||||
|
'usesidebar' => true,
|
||||||
|
'title' => 'Modification Compagnie = '.$company->getTitle(),
|
||||||
|
'routecancel' => 'app_admin_company',
|
||||||
|
'routedelete' => 'app_admin_company_delete',
|
||||||
|
'mode' => 'update',
|
||||||
|
'form' => $form,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/admin/company/delete/{id}', name: 'app_admin_company_delete')]
|
||||||
|
public function delete(int $id, EntityManagerInterface $em): Response
|
||||||
|
{
|
||||||
|
// Récupération de l'enregistrement courant
|
||||||
|
$company = $em->getRepository(Company::class)->find($id);
|
||||||
|
if (!$company) {
|
||||||
|
return $this->redirectToRoute('app_admin_company');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tentative de suppression
|
||||||
|
try {
|
||||||
|
$em->remove($company);
|
||||||
|
$em->flush();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->addflash('error', $e->getMessage());
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_admin_company_update', ['id' => $id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_admin_company');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Controller;
|
|
||||||
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
|
||||||
|
|
||||||
class CropController extends AbstractController
|
|
||||||
{
|
|
||||||
#[Route('/user/crop01/{endpoint}', name: 'app_user_crop01')]
|
|
||||||
public function crop01(string $endpoint): Response
|
|
||||||
{
|
|
||||||
return $this->render('crop\crop01.html.twig', [
|
|
||||||
'useheader' => false,
|
|
||||||
'usemenu' => false,
|
|
||||||
'usesidebar' => false,
|
|
||||||
'endpoint' => $endpoint,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route('/user/crop02', name: 'app_user_crop02')]
|
|
||||||
public function crop02(): Response
|
|
||||||
{
|
|
||||||
return $this->render('crop\crop01.html.twig', [
|
|
||||||
'useheader' => false,
|
|
||||||
'usemenu' => false,
|
|
||||||
'usesidebar' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,18 +2,24 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Repository\AccountingRepository;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|
||||||
class HomeController extends AbstractController
|
class HomeController extends AbstractController
|
||||||
{
|
{
|
||||||
#[Route('/', name: 'app_home')]
|
#[Route('/', name: 'app_home')]
|
||||||
public function home(): Response
|
public function home(Request $request, AccountingRepository $accountingRepository): Response
|
||||||
{
|
{
|
||||||
|
$company = $request->getSession()->get('company');
|
||||||
|
$accountings = $accountingRepository->findBy(['company' => $company, 'category' => 'actif'], ['num01' => 'ASC', 'num02' => 'ASC']);
|
||||||
|
|
||||||
return $this->render('home/home.html.twig', [
|
return $this->render('home/home.html.twig', [
|
||||||
'usemenu' => true,
|
'usemenu' => true,
|
||||||
'usesidebar' => false,
|
'usesidebar' => false,
|
||||||
|
'accountings' => $accountings,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,4 +31,13 @@ class HomeController extends AbstractController
|
|||||||
'usesidebar' => true,
|
'usesidebar' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/user/nocompany', name: 'app_user_nocompany')]
|
||||||
|
public function nocompany(): Response
|
||||||
|
{
|
||||||
|
return $this->render('home/nocompany.html.twig', [
|
||||||
|
'usemenu' => true,
|
||||||
|
'usesidebar' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
81
src/Controller/UploadController.php
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Service\ImageService;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|
||||||
|
class UploadController extends AbstractController
|
||||||
|
{
|
||||||
|
private ImageService $imageService;
|
||||||
|
|
||||||
|
public function __construct(ImageService $imageService)
|
||||||
|
{
|
||||||
|
$this->imageService = $imageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/user/upload/crop01/{endpoint}', name: 'app_user_upload_crop01')]
|
||||||
|
public function crop01(string $endpoint, Request $request): Response
|
||||||
|
{
|
||||||
|
$reportThumb = $request->get('reportThumb');
|
||||||
|
|
||||||
|
return $this->render('upload\crop01.html.twig', [
|
||||||
|
'useheader' => false,
|
||||||
|
'usemenu' => false,
|
||||||
|
'usesidebar' => false,
|
||||||
|
'endpoint' => $endpoint,
|
||||||
|
'reportThumb' => $reportThumb,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/user/upload/crop02', name: 'app_user_upload_crop02')]
|
||||||
|
public function crop02(Request $request): Response
|
||||||
|
{
|
||||||
|
$reportThumb = $request->get('reportThumb');
|
||||||
|
$path = $request->get('path');
|
||||||
|
$file = $request->get('file');
|
||||||
|
$image = $this->getParameter('kernel.project_dir').'/public/'.$path.'/'.$file;
|
||||||
|
$thumb = $this->getParameter('kernel.project_dir').'/public/'.$path.'/thumb_'.$file;
|
||||||
|
|
||||||
|
// Redimentionner
|
||||||
|
$this->imageService->resizeImage($image, 700, 700);
|
||||||
|
|
||||||
|
// Construction du formulaire
|
||||||
|
$form = $this->createFormBuilder()
|
||||||
|
->add('submit', SubmitType::class, ['label' => 'Valider', 'attr' => ['class' => 'btn btn-success']])
|
||||||
|
->add('x1', HiddenType::class)
|
||||||
|
->add('y1', HiddenType::class)
|
||||||
|
->add('x2', HiddenType::class)
|
||||||
|
->add('y2', HiddenType::class)
|
||||||
|
->add('w', HiddenType::class)
|
||||||
|
->add('h', HiddenType::class)
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
// Récupération des data du formulaire
|
||||||
|
$form->handleRequest($request);
|
||||||
|
$toReport = false;
|
||||||
|
// Sur validation on généère la miniature croppée
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$data = $form->getData();
|
||||||
|
$toReport = true;
|
||||||
|
dump($data);
|
||||||
|
$this->imageService->cropImage($image, $thumb, $data['x1'], $data['y1'], $data['w'], $data['h'], 150, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('upload\crop02.html.twig', [
|
||||||
|
'useheader' => false,
|
||||||
|
'usemenu' => false,
|
||||||
|
'usesidebar' => false,
|
||||||
|
'reportThumb' => $reportThumb,
|
||||||
|
'image' => $path.'/'.$file,
|
||||||
|
'thumb' => $path.'/thumb_'.$file,
|
||||||
|
'form' => $form,
|
||||||
|
'toReport' => $toReport,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\Company;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Form\UserType;
|
use App\Form\UserType;
|
||||||
use App\Repository\UserRepository;
|
use App\Repository\UserRepository;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||||
@ -69,7 +71,6 @@ class UserController extends AbstractController
|
|||||||
return $this->redirectToRoute('app_admin_user');
|
return $this->redirectToRoute('app_admin_user');
|
||||||
}
|
}
|
||||||
$hashedPassword = $user->getPassword();
|
$hashedPassword = $user->getPassword();
|
||||||
$user->setPassword('');
|
|
||||||
|
|
||||||
$form = $this->createForm(UserType::class, $user, ['mode' => 'update']);
|
$form = $this->createForm(UserType::class, $user, ['mode' => 'update']);
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
@ -82,8 +83,6 @@ class UserController extends AbstractController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
$user->setPassword($hashedPassword);
|
$user->setPassword($hashedPassword);
|
||||||
|
|
||||||
$em->persist($user);
|
|
||||||
$em->flush();
|
$em->flush();
|
||||||
|
|
||||||
return $this->redirectToRoute('app_admin_user');
|
return $this->redirectToRoute('app_admin_user');
|
||||||
@ -101,14 +100,87 @@ class UserController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[Route('/admin/user/delete/{id}', name: 'app_admin_user_delete')]
|
#[Route('/admin/user/delete/{id}', name: 'app_admin_user_delete')]
|
||||||
public function delete(int $id): Response
|
public function delete(int $id, EntityManagerInterface $em): Response
|
||||||
{
|
{
|
||||||
|
$user = $em->getRepository(User::class)->find($id);
|
||||||
|
if (!$user) {
|
||||||
|
return $this->redirectToRoute('app_admin_user');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tentative de suppression
|
||||||
|
try {
|
||||||
|
$em->remove($user);
|
||||||
|
$em->flush();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->addflash('error', $e->getMessage());
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_admin_user_update', ['id' => $id]);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->redirectToRoute('app_admin_user');
|
return $this->redirectToRoute('app_admin_user');
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route('/user', name: 'app_user_profil')]
|
#[Route('/user', name: 'app_user_profil')]
|
||||||
public function profil(): Response
|
public function profil(Request $request, UserPasswordHasherInterface $passwordHasher, EntityManagerInterface $em): Response
|
||||||
{
|
{
|
||||||
return $this->redirectToRoute('app_home');
|
$user = $em->getRepository(User::class)->find($this->getUser());
|
||||||
|
if (!$user) {
|
||||||
|
return $this->redirectToRoute('app_home');
|
||||||
|
}
|
||||||
|
$hashedPassword = $user->getPassword();
|
||||||
|
|
||||||
|
$form = $this->createForm(UserType::class, $user, ['mode' => 'profil']);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$user = $form->getData();
|
||||||
|
if ($user->getPassword()) {
|
||||||
|
$hashedPassword = $passwordHasher->hashPassword(
|
||||||
|
$user,
|
||||||
|
$user->getPassword()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$user->setPassword($hashedPassword);
|
||||||
|
|
||||||
|
$em->persist($user);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_home');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('user/edit.html.twig', [
|
||||||
|
'usemenu' => true,
|
||||||
|
'usesidebar' => false,
|
||||||
|
'title' => 'Profil = '.$user->getUsername(),
|
||||||
|
'routecancel' => 'app_home',
|
||||||
|
'routedelete' => '',
|
||||||
|
'mode' => 'profil',
|
||||||
|
'form' => $form,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/admin/user/selectcompany', name: 'app_user_selectcompany')]
|
||||||
|
public function selectcompany(Request $request, EntityManagerInterface $em): JsonResponse
|
||||||
|
{
|
||||||
|
$id = $request->get('id');
|
||||||
|
|
||||||
|
$company = $em->getRepository(Company::class)->find($id);
|
||||||
|
if (!$company) {
|
||||||
|
return new JsonResponse(['status' => 'KO', 'message' => 'ID non fourni'], Response::HTTP_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $this->getUser();
|
||||||
|
if (!$user instanceof User) {
|
||||||
|
throw new \LogicException('L\'utilisateur actuel n\'est pas une instance de App\Entity\User.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$companys = $user->getCompanys();
|
||||||
|
if (!$companys->contains($company)) {
|
||||||
|
return new JsonResponse(['status' => 'KO', 'message' => 'Compangnie non autorisée'], Response::HTTP_FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->setCompany($company);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
return new JsonResponse(['status' => 'OK', 'message' => 'Compangnie selectionnée'], Response::HTTP_OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,11 @@ use Doctrine\Common\Collections\ArrayCollection;
|
|||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||||
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
#[ORM\Entity(repositoryClass: AccountingRepository::class)]
|
#[ORM\Entity(repositoryClass: AccountingRepository::class)]
|
||||||
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_ACCOUNTING', fields: ['num01', 'num02'])]
|
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_ACCOUNTING', fields: ['company', 'num01', 'num02'])]
|
||||||
#[UniqueEntity(fields: ['num01', 'num02'], message: 'Ce numéro de compte est déjà utilisé.')]
|
#[UniqueEntity(fields: ['company', 'num01', 'num02'], message: 'Ce numéro de compte est déjà utilisé.')]
|
||||||
class Accounting
|
class Accounting
|
||||||
{
|
{
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
@ -18,10 +19,18 @@ class Accounting
|
|||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
private ?int $id = null;
|
private ?int $id = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255)]
|
#[ORM\Column(length: 3)]
|
||||||
|
#[Assert\Regex(
|
||||||
|
pattern: '/^[0-9]{3}$/',
|
||||||
|
message: 'La valeur doit être un nombre de trois chiffres (ex: 001, 098, 540).'
|
||||||
|
)]
|
||||||
private ?string $num01 = null;
|
private ?string $num01 = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255)]
|
#[ORM\Column(length: 10)]
|
||||||
|
#[Assert\Regex(
|
||||||
|
pattern: '/^[0-9]{3}$/',
|
||||||
|
message: 'La valeur doit être un nombre de trois chiffres (ex: 001, 098, 540).'
|
||||||
|
)]
|
||||||
private ?string $num02 = null;
|
private ?string $num02 = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255)]
|
#[ORM\Column(length: 255)]
|
||||||
@ -67,7 +76,7 @@ class Accounting
|
|||||||
|
|
||||||
public function setNum01(string $num01): static
|
public function setNum01(string $num01): static
|
||||||
{
|
{
|
||||||
$this->num01 = $num01;
|
$this->num01 = str_pad($num01, 3, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -79,7 +88,7 @@ class Accounting
|
|||||||
|
|
||||||
public function setNum02(string $num02): static
|
public function setNum02(string $num02): static
|
||||||
{
|
{
|
||||||
$this->num02 = $num02;
|
$this->num02 = str_pad($num02, 3, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ use Doctrine\Common\Collections\Collection;
|
|||||||
use Doctrine\DBAL\Types\Types;
|
use Doctrine\DBAL\Types\Types;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||||
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
#[ORM\Entity(repositoryClass: CompanyRepository::class)]
|
#[ORM\Entity(repositoryClass: CompanyRepository::class)]
|
||||||
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_COMPANY', fields: ['title'])]
|
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_COMPANY', fields: ['title'])]
|
||||||
@ -22,12 +23,16 @@ class Company
|
|||||||
#[ORM\Column(length: 255, unique: true)]
|
#[ORM\Column(length: 255, unique: true)]
|
||||||
private ?string $title = null;
|
private ?string $title = null;
|
||||||
|
|
||||||
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
|
||||||
private ?string $adress = null;
|
|
||||||
|
|
||||||
#[ORM\Column(length: 255, nullable: true)]
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
private ?string $logo = null;
|
private ?string $logo = null;
|
||||||
|
|
||||||
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
|
#[Assert\Email(message: 'Veuillez entrer un email valide.')]
|
||||||
|
private ?string $email = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
||||||
|
private ?string $adress = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 255, nullable: true)]
|
#[ORM\Column(length: 255, nullable: true)]
|
||||||
private ?string $bankname = null;
|
private ?string $bankname = null;
|
||||||
|
|
||||||
@ -87,6 +92,30 @@ class Company
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getLogo(): ?string
|
||||||
|
{
|
||||||
|
return $this->logo ? $this->logo : 'medias/logo/logo.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLogo(?string $logo): static
|
||||||
|
{
|
||||||
|
$this->logo = $logo;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEmail(): ?string
|
||||||
|
{
|
||||||
|
return $this->email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setEmail(?string $email): static
|
||||||
|
{
|
||||||
|
$this->email = $email;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function getAdress(): ?string
|
public function getAdress(): ?string
|
||||||
{
|
{
|
||||||
return $this->adress;
|
return $this->adress;
|
||||||
@ -99,18 +128,6 @@ class Company
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLogo(): ?string
|
|
||||||
{
|
|
||||||
return $this->logo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setLogo(?string $logo): static
|
|
||||||
{
|
|
||||||
$this->logo = $logo;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBankname(): ?string
|
public function getBankname(): ?string
|
||||||
{
|
{
|
||||||
return $this->bankname;
|
return $this->bankname;
|
||||||
|
@ -46,6 +46,10 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
|||||||
#[ORM\ManyToMany(targetEntity: Company::class, inversedBy: 'users')]
|
#[ORM\ManyToMany(targetEntity: Company::class, inversedBy: 'users')]
|
||||||
private Collection $companys;
|
private Collection $companys;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne()]
|
||||||
|
#[ORM\JoinColumn(nullable: true)]
|
||||||
|
private ?Company $company = null;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->companys = new ArrayCollection();
|
$this->companys = new ArrayCollection();
|
||||||
@ -173,4 +177,20 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCompany(): ?Company
|
||||||
|
{
|
||||||
|
if (!$this->companys) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->company;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCompany(?Company $company): static
|
||||||
|
{
|
||||||
|
$this->company = $company;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
47
src/EventListener/SessionListener.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\EventListener;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
|
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
||||||
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
|
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||||
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
|
use Symfony\Component\Routing\RouterInterface;
|
||||||
|
|
||||||
|
final class SessionListener
|
||||||
|
{
|
||||||
|
private Security $security;
|
||||||
|
private RouterInterface $router;
|
||||||
|
|
||||||
|
public function __construct(Security $security, RouterInterface $router)
|
||||||
|
{
|
||||||
|
$this->security = $security;
|
||||||
|
$this->router = $router;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[AsEventListener(event: KernelEvents::REQUEST)]
|
||||||
|
public function onKernelRequest(RequestEvent $event): void
|
||||||
|
{
|
||||||
|
$request = $event->getRequest();
|
||||||
|
$session = $request->getSession();
|
||||||
|
|
||||||
|
$user = $this->security->getUser();
|
||||||
|
if ($user instanceof User && $user) {
|
||||||
|
// Intialisation de la compagnie en cours
|
||||||
|
if (!$user->getCompany()) {
|
||||||
|
if ($user->getCompanys()) {
|
||||||
|
$user->setCompany($user->getCompanys()[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$session->set('company', $user->getCompany());
|
||||||
|
$session->set('companys', $user->getCompanys());
|
||||||
|
|
||||||
|
if (!$user->getCompany()) {
|
||||||
|
$event->setResponse(new RedirectResponse($this->router->generate('app_user_nocompany', [])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
src/EventListener/UploadListener.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\EventListener;
|
||||||
|
|
||||||
|
use Oneup\UploaderBundle\Event\PostPersistEvent;
|
||||||
|
use Oneup\UploaderBundle\Event\ValidationEvent;
|
||||||
|
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
use Symfony\Component\HttpKernel\KernelInterface;
|
||||||
|
|
||||||
|
final class UploadListener
|
||||||
|
{
|
||||||
|
private string $projectDir;
|
||||||
|
|
||||||
|
public function __construct(KernelInterface $kernel)
|
||||||
|
{
|
||||||
|
// Utiliser le projectDir pour construire le chemin
|
||||||
|
$this->projectDir = $kernel->getProjectDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[AsEventListener(event: 'oneup_uploader.validation')]
|
||||||
|
public function onOneupUploaderValidation(ValidationEvent $event): void
|
||||||
|
{
|
||||||
|
// On s'assure que le repertoire de destination existe bien
|
||||||
|
$fs = new Filesystem();
|
||||||
|
$fs->mkdir($this->projectDir.'/public/uploads');
|
||||||
|
$fs->mkdir($this->projectDir.'/public/uploads/'.$event->getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[AsEventListener(event: 'oneup_uploader.post_persist')]
|
||||||
|
public function onOneupUploaderPostPersit(PostPersistEvent $event): void
|
||||||
|
{
|
||||||
|
$file = $event->getFile();
|
||||||
|
$type = $event->getType();
|
||||||
|
$filename = $file->getFilename();
|
||||||
|
$response = $event->getResponse();
|
||||||
|
$response['file'] = $filename;
|
||||||
|
$response['path'] = 'uploads/'.$type;
|
||||||
|
$response['filepath'] = 'uploads/'.$type.'/'.$filename;
|
||||||
|
}
|
||||||
|
}
|
62
src/Form/AccountingType.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use App\Entity\Accounting;
|
||||||
|
use App\Form\DataTransformer\ThreeDigitTransformer;
|
||||||
|
use App\Form\Type\ThreeDigitType;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class AccountingType extends AbstractType
|
||||||
|
{
|
||||||
|
private ThreeDigitTransformer $threeDigitTransformer;
|
||||||
|
|
||||||
|
public function __construct(ThreeDigitTransformer $threeDigitTransformer)
|
||||||
|
{
|
||||||
|
$this->threeDigitTransformer = $threeDigitTransformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('submit', SubmitType::class, [
|
||||||
|
'label' => 'Valider',
|
||||||
|
'attr' => ['class' => 'btn btn-success no-print'],
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('num01', ThreeDigitType::class, [
|
||||||
|
'label' => 'Numéro de Compte 01',
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('num02', ThreeDigitType::class, [
|
||||||
|
'label' => 'Numéro de Compte 01',
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('title', TextType::class, [
|
||||||
|
'label' => 'Titre',
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('category', ChoiceType::class, [
|
||||||
|
'label' => 'Catégorie',
|
||||||
|
'choices' => ['passif' => 'passif', 'actif' => 'actif', 'charge' => 'charge', 'produit' => 'produit'],
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('icon', FontawsomeType::class, [
|
||||||
|
'label' => 'Icône',
|
||||||
|
'attr' => ['class' => 'select2'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => Accounting::class,
|
||||||
|
'mode' => 'submit',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
91
src/Form/CompanyType.php
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use App\Entity\Company;
|
||||||
|
use App\Entity\User;
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class CompanyType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('submit', SubmitType::class, [
|
||||||
|
'label' => 'Valider',
|
||||||
|
'attr' => ['class' => 'btn btn-success no-print'],
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('title', TextType::class, [
|
||||||
|
'label' => 'Nom',
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('email', EmailType::class, [
|
||||||
|
'label' => 'Email',
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('logo', HiddenType::class)
|
||||||
|
|
||||||
|
->add('adress', TextType::class, [
|
||||||
|
'label' => 'Adresse',
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('bankname', TextType::class, [
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('bankcode', TextType::class, [
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('bankguichet', TextType::class, [
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('banknum', TextType::class, [
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('bankkey', TextType::class, [
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('banklocality', TextType::class, [
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('bankiban', TextType::class, [
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('bankbic', TextType::class, [
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('users', EntityType::class, [
|
||||||
|
'label' => 'Utilisateurs',
|
||||||
|
'class' => User::class,
|
||||||
|
'choice_label' => 'username',
|
||||||
|
'multiple' => true,
|
||||||
|
'attr' => ['class' => 'select2'],
|
||||||
|
'required' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => Company::class,
|
||||||
|
'mode' => 'submit',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
18
src/Form/DataTransformer/ThreeDigitTransformer.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\DataTransformer;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\DataTransformerInterface;
|
||||||
|
|
||||||
|
class ThreeDigitTransformer implements DataTransformerInterface
|
||||||
|
{
|
||||||
|
public function transform($value): ?string
|
||||||
|
{
|
||||||
|
return null !== $value ? str_pad($value, 3, '0', STR_PAD_LEFT) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reverseTransform($value): ?int
|
||||||
|
{
|
||||||
|
return str_pad($value, 3, '0', STR_PAD_LEFT);
|
||||||
|
}
|
||||||
|
}
|
999
src/Form/Type/FontawsomeType.php
Normal file
@ -0,0 +1,999 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\Type;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class FontawsomeType extends AbstractType
|
||||||
|
{
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'choices' => $this->getFontAwesomeIcons(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParent(): string
|
||||||
|
{
|
||||||
|
return ChoiceType::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFontAwesomeIcons(): array
|
||||||
|
{
|
||||||
|
$icons = [
|
||||||
|
'ad',
|
||||||
|
'address-book',
|
||||||
|
'address-card',
|
||||||
|
'adjust',
|
||||||
|
'air-freshener',
|
||||||
|
'align-center',
|
||||||
|
'align-justify',
|
||||||
|
'align-left',
|
||||||
|
'align-right',
|
||||||
|
'allergies',
|
||||||
|
'ambulance',
|
||||||
|
'american-sign-language-interpreting',
|
||||||
|
'anchor',
|
||||||
|
'angle-double-down',
|
||||||
|
'angle-double-left',
|
||||||
|
'angle-double-right',
|
||||||
|
'angle-double-up',
|
||||||
|
'angle-down',
|
||||||
|
'angle-left',
|
||||||
|
'angle-right',
|
||||||
|
'angle-up',
|
||||||
|
'angry',
|
||||||
|
'ankh',
|
||||||
|
'apple-alt',
|
||||||
|
'archive',
|
||||||
|
'archway',
|
||||||
|
'arrow-alt-circle-down',
|
||||||
|
'arrow-alt-circle-left',
|
||||||
|
'arrow-alt-circle-right',
|
||||||
|
'arrow-alt-circle-up',
|
||||||
|
'arrow-circle-down',
|
||||||
|
'arrow-circle-left',
|
||||||
|
'arrow-circle-right',
|
||||||
|
'arrow-circle-up',
|
||||||
|
'arrow-down',
|
||||||
|
'arrow-left',
|
||||||
|
'arrow-right',
|
||||||
|
'arrow-up',
|
||||||
|
'arrows-alt',
|
||||||
|
'arrows-alt-h',
|
||||||
|
'arrows-alt-v',
|
||||||
|
'assistive-listening-systems',
|
||||||
|
'asterisk',
|
||||||
|
'at',
|
||||||
|
'atlas',
|
||||||
|
'atom',
|
||||||
|
'audio-description',
|
||||||
|
'award',
|
||||||
|
'baby',
|
||||||
|
'baby-carriage',
|
||||||
|
'backspace',
|
||||||
|
'backward',
|
||||||
|
'bacon',
|
||||||
|
'bahai',
|
||||||
|
'balance-scale',
|
||||||
|
'balance-scale-left',
|
||||||
|
'balance-scale-right',
|
||||||
|
'ban',
|
||||||
|
'band-aid',
|
||||||
|
'barcode',
|
||||||
|
'bars',
|
||||||
|
'baseball-ball',
|
||||||
|
'basketball-ball',
|
||||||
|
'bath',
|
||||||
|
'battery-empty',
|
||||||
|
'battery-full',
|
||||||
|
'battery-half',
|
||||||
|
'battery-quarter',
|
||||||
|
'battery-three-quarters',
|
||||||
|
'bed',
|
||||||
|
'beer',
|
||||||
|
'bell',
|
||||||
|
'bell-slash',
|
||||||
|
'bezier-curve',
|
||||||
|
'bible',
|
||||||
|
'bicycle',
|
||||||
|
'biking',
|
||||||
|
'binoculars',
|
||||||
|
'biohazard',
|
||||||
|
'birthday-cake',
|
||||||
|
'blender',
|
||||||
|
'blender-phone',
|
||||||
|
'blind',
|
||||||
|
'blog',
|
||||||
|
'bold',
|
||||||
|
'bolt',
|
||||||
|
'bomb',
|
||||||
|
'bone',
|
||||||
|
'bong',
|
||||||
|
'book',
|
||||||
|
'book-dead',
|
||||||
|
'book-medical',
|
||||||
|
'book-open',
|
||||||
|
'book-reader',
|
||||||
|
'bookmark',
|
||||||
|
'border-all',
|
||||||
|
'border-none',
|
||||||
|
'border-style',
|
||||||
|
'bowling-ball',
|
||||||
|
'box',
|
||||||
|
'box-open',
|
||||||
|
'boxes',
|
||||||
|
'braille',
|
||||||
|
'brain',
|
||||||
|
'bread-slice',
|
||||||
|
'briefcase',
|
||||||
|
'briefcase-medical',
|
||||||
|
'broadcast-tower',
|
||||||
|
'broom',
|
||||||
|
'brush',
|
||||||
|
'bug',
|
||||||
|
'building',
|
||||||
|
'bullhorn',
|
||||||
|
'bullseye',
|
||||||
|
'burn',
|
||||||
|
'bus',
|
||||||
|
'bus-alt',
|
||||||
|
'business-time',
|
||||||
|
'calculator',
|
||||||
|
'calendar',
|
||||||
|
'calendar-alt',
|
||||||
|
'calendar-check',
|
||||||
|
'calendar-day',
|
||||||
|
'calendar-minus',
|
||||||
|
'calendar-plus',
|
||||||
|
'calendar-times',
|
||||||
|
'calendar-week',
|
||||||
|
'camera',
|
||||||
|
'camera-retro',
|
||||||
|
'campground',
|
||||||
|
'candy-cane',
|
||||||
|
'cannabis',
|
||||||
|
'capsules',
|
||||||
|
'car',
|
||||||
|
'car-alt',
|
||||||
|
'car-battery',
|
||||||
|
'car-crash',
|
||||||
|
'car-side',
|
||||||
|
'caravan',
|
||||||
|
'caret-down',
|
||||||
|
'caret-left',
|
||||||
|
'caret-right',
|
||||||
|
'caret-square-down',
|
||||||
|
'caret-square-left',
|
||||||
|
'caret-square-right',
|
||||||
|
'caret-square-up',
|
||||||
|
'caret-up',
|
||||||
|
'carrot',
|
||||||
|
'cart-arrow-down',
|
||||||
|
'cart-plus',
|
||||||
|
'cash-register',
|
||||||
|
'cat',
|
||||||
|
'certificate',
|
||||||
|
'chair',
|
||||||
|
'chalkboard',
|
||||||
|
'chalkboard-teacher',
|
||||||
|
'charging-station',
|
||||||
|
'chart-area',
|
||||||
|
'chart-bar',
|
||||||
|
'chart-line',
|
||||||
|
'chart-pie',
|
||||||
|
'check',
|
||||||
|
'check-circle',
|
||||||
|
'check-double',
|
||||||
|
'check-square',
|
||||||
|
'cheese',
|
||||||
|
'chess',
|
||||||
|
'chess-bishop',
|
||||||
|
'chess-board',
|
||||||
|
'chess-king',
|
||||||
|
'chess-knight',
|
||||||
|
'chess-pawn',
|
||||||
|
'chess-queen',
|
||||||
|
'chess-rook',
|
||||||
|
'chevron-circle-down',
|
||||||
|
'chevron-circle-left',
|
||||||
|
'chevron-circle-right',
|
||||||
|
'chevron-circle-up',
|
||||||
|
'chevron-down',
|
||||||
|
'chevron-left',
|
||||||
|
'chevron-right',
|
||||||
|
'chevron-up',
|
||||||
|
'child',
|
||||||
|
'church',
|
||||||
|
'circle',
|
||||||
|
'circle-notch',
|
||||||
|
'city',
|
||||||
|
'clinic-medical',
|
||||||
|
'clipboard',
|
||||||
|
'clipboard-check',
|
||||||
|
'clipboard-list',
|
||||||
|
'clock',
|
||||||
|
'clone',
|
||||||
|
'closed-captioning',
|
||||||
|
'cloud',
|
||||||
|
'cloud-download-alt',
|
||||||
|
'cloud-meatball',
|
||||||
|
'cloud-moon',
|
||||||
|
'cloud-moon-rain',
|
||||||
|
'cloud-rain',
|
||||||
|
'cloud-showers-heavy',
|
||||||
|
'cloud-sun',
|
||||||
|
'cloud-sun-rain',
|
||||||
|
'cloud-upload-alt',
|
||||||
|
'cocktail',
|
||||||
|
'code',
|
||||||
|
'code-branch',
|
||||||
|
'coffee',
|
||||||
|
'cog',
|
||||||
|
'cogs',
|
||||||
|
'coins',
|
||||||
|
'columns',
|
||||||
|
'comment',
|
||||||
|
'comment-alt',
|
||||||
|
'comment-dollar',
|
||||||
|
'comment-dots',
|
||||||
|
'comment-medical',
|
||||||
|
'comment-slash',
|
||||||
|
'comments',
|
||||||
|
'comments-dollar',
|
||||||
|
'compact-disc',
|
||||||
|
'compass',
|
||||||
|
'compress',
|
||||||
|
'compress-alt',
|
||||||
|
'compress-arrows-alt',
|
||||||
|
'concierge-bell',
|
||||||
|
'cookie',
|
||||||
|
'cookie-bite',
|
||||||
|
'copy',
|
||||||
|
'copyright',
|
||||||
|
'couch',
|
||||||
|
'credit-card',
|
||||||
|
'crop',
|
||||||
|
'crop-alt',
|
||||||
|
'cross',
|
||||||
|
'crosshairs',
|
||||||
|
'crow',
|
||||||
|
'crown',
|
||||||
|
'crutch',
|
||||||
|
'cube',
|
||||||
|
'cubes',
|
||||||
|
'cut',
|
||||||
|
'database',
|
||||||
|
'deaf',
|
||||||
|
'democrat',
|
||||||
|
'desktop',
|
||||||
|
'dharmachakra',
|
||||||
|
'diagnoses',
|
||||||
|
'dice',
|
||||||
|
'dice-d20',
|
||||||
|
'dice-d6',
|
||||||
|
'dice-five',
|
||||||
|
'dice-four',
|
||||||
|
'dice-one',
|
||||||
|
'dice-six',
|
||||||
|
'dice-three',
|
||||||
|
'dice-two',
|
||||||
|
'digital-tachograph',
|
||||||
|
'directions',
|
||||||
|
'divide',
|
||||||
|
'dizzy',
|
||||||
|
'dna',
|
||||||
|
'dog',
|
||||||
|
'dollar-sign',
|
||||||
|
'dolly',
|
||||||
|
'dolly-flatbed',
|
||||||
|
'donate',
|
||||||
|
'door-closed',
|
||||||
|
'door-open',
|
||||||
|
'dot-circle',
|
||||||
|
'dove',
|
||||||
|
'download',
|
||||||
|
'drafting-compass',
|
||||||
|
'dragon',
|
||||||
|
'draw-polygon',
|
||||||
|
'drum',
|
||||||
|
'drum-steelpan',
|
||||||
|
'drumstick-bite',
|
||||||
|
'dumbbell',
|
||||||
|
'dumpster',
|
||||||
|
'dumpster-fire',
|
||||||
|
'dungeon',
|
||||||
|
'edit',
|
||||||
|
'egg',
|
||||||
|
'eject',
|
||||||
|
'ellipsis-h',
|
||||||
|
'ellipsis-v',
|
||||||
|
'envelope',
|
||||||
|
'envelope-open',
|
||||||
|
'envelope-open-text',
|
||||||
|
'envelope-square',
|
||||||
|
'equals',
|
||||||
|
'eraser',
|
||||||
|
'ethernet',
|
||||||
|
'euro-sign',
|
||||||
|
'exchange-alt',
|
||||||
|
'exclamation',
|
||||||
|
'exclamation-circle',
|
||||||
|
'exclamation-triangle',
|
||||||
|
'expand',
|
||||||
|
'expand-alt',
|
||||||
|
'expand-arrows-alt',
|
||||||
|
'external-link-alt',
|
||||||
|
'external-link-square-alt',
|
||||||
|
'eye',
|
||||||
|
'eye-dropper',
|
||||||
|
'eye-slash',
|
||||||
|
'fan',
|
||||||
|
'fast-backward',
|
||||||
|
'fast-forward',
|
||||||
|
'fax',
|
||||||
|
'feather',
|
||||||
|
'feather-alt',
|
||||||
|
'female',
|
||||||
|
'fighter-jet',
|
||||||
|
'file',
|
||||||
|
'file-alt',
|
||||||
|
'file-archive',
|
||||||
|
'file-audio',
|
||||||
|
'file-code',
|
||||||
|
'file-contract',
|
||||||
|
'file-csv',
|
||||||
|
'file-download',
|
||||||
|
'file-excel',
|
||||||
|
'file-export',
|
||||||
|
'file-image',
|
||||||
|
'file-import',
|
||||||
|
'file-invoice',
|
||||||
|
'file-invoice-dollar',
|
||||||
|
'file-medical',
|
||||||
|
'file-medical-alt',
|
||||||
|
'file-pdf',
|
||||||
|
'file-powerpoint',
|
||||||
|
'file-prescription',
|
||||||
|
'file-signature',
|
||||||
|
'file-upload',
|
||||||
|
'file-video',
|
||||||
|
'file-word',
|
||||||
|
'fill',
|
||||||
|
'fill-drip',
|
||||||
|
'film',
|
||||||
|
'filter',
|
||||||
|
'fingerprint',
|
||||||
|
'fire',
|
||||||
|
'fire-alt',
|
||||||
|
'fire-extinguisher',
|
||||||
|
'first-aid',
|
||||||
|
'fish',
|
||||||
|
'fist-raised',
|
||||||
|
'flag',
|
||||||
|
'flag-checkered',
|
||||||
|
'flag-usa',
|
||||||
|
'flask',
|
||||||
|
'flushed',
|
||||||
|
'folder',
|
||||||
|
'folder-minus',
|
||||||
|
'folder-open',
|
||||||
|
'folder-plus',
|
||||||
|
'font',
|
||||||
|
'football-ball',
|
||||||
|
'forward',
|
||||||
|
'frog',
|
||||||
|
'frown',
|
||||||
|
'frown-open',
|
||||||
|
'funnel-dollar',
|
||||||
|
'futbol',
|
||||||
|
'gamepad',
|
||||||
|
'gas-pump',
|
||||||
|
'gavel',
|
||||||
|
'gem',
|
||||||
|
'genderless',
|
||||||
|
'ghost',
|
||||||
|
'gift',
|
||||||
|
'gifts',
|
||||||
|
'glass-cheers',
|
||||||
|
'glass-martini',
|
||||||
|
'glass-martini-alt',
|
||||||
|
'glass-whiskey',
|
||||||
|
'glasses',
|
||||||
|
'globe',
|
||||||
|
'globe-africa',
|
||||||
|
'globe-americas',
|
||||||
|
'globe-asia',
|
||||||
|
'globe-europe',
|
||||||
|
'golf-ball',
|
||||||
|
'gopuram',
|
||||||
|
'graduation-cap',
|
||||||
|
'greater-than',
|
||||||
|
'greater-than-equal',
|
||||||
|
'grimace',
|
||||||
|
'grin',
|
||||||
|
'grin-alt',
|
||||||
|
'grin-beam',
|
||||||
|
'grin-beam-sweat',
|
||||||
|
'grin-hearts',
|
||||||
|
'grin-squint',
|
||||||
|
'grin-squint-tears',
|
||||||
|
'grin-stars',
|
||||||
|
'grin-tears',
|
||||||
|
'grin-tongue',
|
||||||
|
'grin-tongue-squint',
|
||||||
|
'grin-tongue-wink',
|
||||||
|
'grin-wink',
|
||||||
|
'grip-horizontal',
|
||||||
|
'grip-lines',
|
||||||
|
'grip-lines-vertical',
|
||||||
|
'grip-vertical',
|
||||||
|
'guitar',
|
||||||
|
'h-square',
|
||||||
|
'hamburger',
|
||||||
|
'hammer',
|
||||||
|
'hamsa',
|
||||||
|
'hand-holding',
|
||||||
|
'hand-holding-heart',
|
||||||
|
'hand-holding-usd',
|
||||||
|
'hand-lizard',
|
||||||
|
'hand-middle-finger',
|
||||||
|
'hand-paper',
|
||||||
|
'hand-peace',
|
||||||
|
'hand-point-down',
|
||||||
|
'hand-point-left',
|
||||||
|
'hand-point-right',
|
||||||
|
'hand-point-up',
|
||||||
|
'hand-pointer',
|
||||||
|
'hand-rock',
|
||||||
|
'hand-scissors',
|
||||||
|
'hand-spock',
|
||||||
|
'hands',
|
||||||
|
'hands-helping',
|
||||||
|
'handshake',
|
||||||
|
'hanukiah',
|
||||||
|
'hard-hat',
|
||||||
|
'hashtag',
|
||||||
|
'hat-cowboy',
|
||||||
|
'hat-cowboy-side',
|
||||||
|
'hat-wizard',
|
||||||
|
'hdd',
|
||||||
|
'heading',
|
||||||
|
'headphones',
|
||||||
|
'headphones-alt',
|
||||||
|
'headset',
|
||||||
|
'heart',
|
||||||
|
'heart-broken',
|
||||||
|
'heartbeat',
|
||||||
|
'helicopter',
|
||||||
|
'highlighter',
|
||||||
|
'hiking',
|
||||||
|
'hippo',
|
||||||
|
'history',
|
||||||
|
'hockey-puck',
|
||||||
|
'holly-berry',
|
||||||
|
'home',
|
||||||
|
'horse',
|
||||||
|
'horse-head',
|
||||||
|
'hospital',
|
||||||
|
'hospital-alt',
|
||||||
|
'hospital-symbol',
|
||||||
|
'hot-tub',
|
||||||
|
'hotdog',
|
||||||
|
'hotel',
|
||||||
|
'hourglass',
|
||||||
|
'hourglass-end',
|
||||||
|
'hourglass-half',
|
||||||
|
'hourglass-start',
|
||||||
|
'house-damage',
|
||||||
|
'hryvnia',
|
||||||
|
'i-cursor',
|
||||||
|
'ice-cream',
|
||||||
|
'icicles',
|
||||||
|
'icons',
|
||||||
|
'id-badge',
|
||||||
|
'id-card',
|
||||||
|
'id-card-alt',
|
||||||
|
'igloo',
|
||||||
|
'image',
|
||||||
|
'images',
|
||||||
|
'inbox',
|
||||||
|
'indent',
|
||||||
|
'industry',
|
||||||
|
'infinity',
|
||||||
|
'info',
|
||||||
|
'info-circle',
|
||||||
|
'italic',
|
||||||
|
'jedi',
|
||||||
|
'joint',
|
||||||
|
'journal-whills',
|
||||||
|
'kaaba',
|
||||||
|
'key',
|
||||||
|
'keyboard',
|
||||||
|
'khanda',
|
||||||
|
'kiss',
|
||||||
|
'kiss-beam',
|
||||||
|
'kiss-wink-heart',
|
||||||
|
'kiwi-bird',
|
||||||
|
'landmark',
|
||||||
|
'language',
|
||||||
|
'laptop',
|
||||||
|
'laptop-code',
|
||||||
|
'laptop-medical',
|
||||||
|
'laugh',
|
||||||
|
'laugh-beam',
|
||||||
|
'laugh-squint',
|
||||||
|
'laugh-wink',
|
||||||
|
'layer-group',
|
||||||
|
'leaf',
|
||||||
|
'lemon',
|
||||||
|
'less-than',
|
||||||
|
'less-than-equal',
|
||||||
|
'level-down-alt',
|
||||||
|
'level-up-alt',
|
||||||
|
'life-ring',
|
||||||
|
'lightbulb',
|
||||||
|
'link',
|
||||||
|
'lira-sign',
|
||||||
|
'list',
|
||||||
|
'list-alt',
|
||||||
|
'list-ol',
|
||||||
|
'list-ul',
|
||||||
|
'location-arrow',
|
||||||
|
'lock',
|
||||||
|
'lock-open',
|
||||||
|
'long-arrow-alt-down',
|
||||||
|
'long-arrow-alt-left',
|
||||||
|
'long-arrow-alt-right',
|
||||||
|
'long-arrow-alt-up',
|
||||||
|
'low-vision',
|
||||||
|
'luggage-cart',
|
||||||
|
'magic',
|
||||||
|
'magnet',
|
||||||
|
'mail-bulk',
|
||||||
|
'male',
|
||||||
|
'map',
|
||||||
|
'map-marked',
|
||||||
|
'map-marked-alt',
|
||||||
|
'map-marker',
|
||||||
|
'map-marker-alt',
|
||||||
|
'map-pin',
|
||||||
|
'map-signs',
|
||||||
|
'marker',
|
||||||
|
'mars',
|
||||||
|
'mars-double',
|
||||||
|
'mars-stroke',
|
||||||
|
'mars-stroke-h',
|
||||||
|
'mars-stroke-v',
|
||||||
|
'mask',
|
||||||
|
'medal',
|
||||||
|
'medkit',
|
||||||
|
'meh',
|
||||||
|
'meh-blank',
|
||||||
|
'meh-rolling-eyes',
|
||||||
|
'memory',
|
||||||
|
'menorah',
|
||||||
|
'mercury',
|
||||||
|
'meteor',
|
||||||
|
'microchip',
|
||||||
|
'microphone',
|
||||||
|
'microphone-alt',
|
||||||
|
'microphone-alt-slash',
|
||||||
|
'microphone-slash',
|
||||||
|
'microscope',
|
||||||
|
'minus',
|
||||||
|
'minus-circle',
|
||||||
|
'minus-square',
|
||||||
|
'mitten',
|
||||||
|
'mobile',
|
||||||
|
'mobile-alt',
|
||||||
|
'money-bill',
|
||||||
|
'money-bill-alt',
|
||||||
|
'money-bill-wave',
|
||||||
|
'money-bill-wave-alt',
|
||||||
|
'money-check',
|
||||||
|
'money-check-alt',
|
||||||
|
'monument',
|
||||||
|
'moon',
|
||||||
|
'mortar-pestle',
|
||||||
|
'mosque',
|
||||||
|
'motorcycle',
|
||||||
|
'mountain',
|
||||||
|
'mouse',
|
||||||
|
'mouse-pointer',
|
||||||
|
'mug-hot',
|
||||||
|
'music',
|
||||||
|
'network-wired',
|
||||||
|
'neuter',
|
||||||
|
'newspaper',
|
||||||
|
'not-equal',
|
||||||
|
'notes-medical',
|
||||||
|
'object-group',
|
||||||
|
'object-ungroup',
|
||||||
|
'oil-can',
|
||||||
|
'om',
|
||||||
|
'otter',
|
||||||
|
'outdent',
|
||||||
|
'pager',
|
||||||
|
'paint-brush',
|
||||||
|
'paint-roller',
|
||||||
|
'palette',
|
||||||
|
'pallet',
|
||||||
|
'paper-plane',
|
||||||
|
'paperclip',
|
||||||
|
'parachute-box',
|
||||||
|
'paragraph',
|
||||||
|
'parking',
|
||||||
|
'passport',
|
||||||
|
'pastafarianism',
|
||||||
|
'paste',
|
||||||
|
'pause',
|
||||||
|
'pause-circle',
|
||||||
|
'paw',
|
||||||
|
'peace',
|
||||||
|
'pen',
|
||||||
|
'pen-alt',
|
||||||
|
'pen-fancy',
|
||||||
|
'pen-nib',
|
||||||
|
'pen-square',
|
||||||
|
'pencil-alt',
|
||||||
|
'pencil-ruler',
|
||||||
|
'people-carry',
|
||||||
|
'pepper-hot',
|
||||||
|
'percent',
|
||||||
|
'percentage',
|
||||||
|
'person-booth',
|
||||||
|
'phone',
|
||||||
|
'phone-alt',
|
||||||
|
'phone-slash',
|
||||||
|
'phone-square',
|
||||||
|
'phone-square-alt',
|
||||||
|
'phone-volume',
|
||||||
|
'photo-video',
|
||||||
|
'piggy-bank',
|
||||||
|
'pills',
|
||||||
|
'pizza-slice',
|
||||||
|
'place-of-worship',
|
||||||
|
'plane',
|
||||||
|
'plane-arrival',
|
||||||
|
'plane-departure',
|
||||||
|
'play',
|
||||||
|
'play-circle',
|
||||||
|
'plug',
|
||||||
|
'plus',
|
||||||
|
'plus-circle',
|
||||||
|
'plus-square',
|
||||||
|
'podcast',
|
||||||
|
'poll',
|
||||||
|
'poll-h',
|
||||||
|
'poo',
|
||||||
|
'poo-storm',
|
||||||
|
'poop',
|
||||||
|
'portrait',
|
||||||
|
'pound-sign',
|
||||||
|
'power-off',
|
||||||
|
'pray',
|
||||||
|
'praying-hands',
|
||||||
|
'prescription',
|
||||||
|
'prescription-bottle',
|
||||||
|
'prescription-bottle-alt',
|
||||||
|
'print',
|
||||||
|
'procedures',
|
||||||
|
'project-diagram',
|
||||||
|
'puzzle-piece',
|
||||||
|
'qrcode',
|
||||||
|
'question',
|
||||||
|
'question-circle',
|
||||||
|
'quidditch',
|
||||||
|
'quote-left',
|
||||||
|
'quote-right',
|
||||||
|
'quran',
|
||||||
|
'radiation',
|
||||||
|
'radiation-alt',
|
||||||
|
'rainbow',
|
||||||
|
'random',
|
||||||
|
'receipt',
|
||||||
|
'record-vinyl',
|
||||||
|
'recycle',
|
||||||
|
'redo',
|
||||||
|
'redo-alt',
|
||||||
|
'registered',
|
||||||
|
'remove-format',
|
||||||
|
'reply',
|
||||||
|
'reply-all',
|
||||||
|
'republican',
|
||||||
|
'restroom',
|
||||||
|
'retweet',
|
||||||
|
'ribbon',
|
||||||
|
'ring',
|
||||||
|
'road',
|
||||||
|
'robot',
|
||||||
|
'rocket',
|
||||||
|
'route',
|
||||||
|
'rss',
|
||||||
|
'rss-square',
|
||||||
|
'ruble-sign',
|
||||||
|
'ruler',
|
||||||
|
'ruler-combined',
|
||||||
|
'ruler-horizontal',
|
||||||
|
'ruler-vertical',
|
||||||
|
'running',
|
||||||
|
'rupee-sign',
|
||||||
|
'sad-cry',
|
||||||
|
'sad-tear',
|
||||||
|
'satellite',
|
||||||
|
'satellite-dish',
|
||||||
|
'save',
|
||||||
|
'school',
|
||||||
|
'screwdriver',
|
||||||
|
'scroll',
|
||||||
|
'sd-card',
|
||||||
|
'search',
|
||||||
|
'search-dollar',
|
||||||
|
'search-location',
|
||||||
|
'search-minus',
|
||||||
|
'search-plus',
|
||||||
|
'seedling',
|
||||||
|
'server',
|
||||||
|
'shapes',
|
||||||
|
'share',
|
||||||
|
'share-alt',
|
||||||
|
'share-alt-square',
|
||||||
|
'share-square',
|
||||||
|
'shekel-sign',
|
||||||
|
'shield-alt',
|
||||||
|
'ship',
|
||||||
|
'shipping-fast',
|
||||||
|
'shoe-prints',
|
||||||
|
'shopping-bag',
|
||||||
|
'shopping-basket',
|
||||||
|
'shopping-cart',
|
||||||
|
'shower',
|
||||||
|
'shuttle-van',
|
||||||
|
'sign',
|
||||||
|
'sign-in-alt',
|
||||||
|
'sign-language',
|
||||||
|
'sign-out-alt',
|
||||||
|
'signal',
|
||||||
|
'signature',
|
||||||
|
'sim-card',
|
||||||
|
'sitemap',
|
||||||
|
'skating',
|
||||||
|
'skiing',
|
||||||
|
'skiing-nordic',
|
||||||
|
'skull',
|
||||||
|
'skull-crossbones',
|
||||||
|
'slash',
|
||||||
|
'sleigh',
|
||||||
|
'sliders-h',
|
||||||
|
'smile',
|
||||||
|
'smile-beam',
|
||||||
|
'smile-wink',
|
||||||
|
'smog',
|
||||||
|
'smoking',
|
||||||
|
'smoking-ban',
|
||||||
|
'sms',
|
||||||
|
'snowboarding',
|
||||||
|
'snowflake',
|
||||||
|
'snowman',
|
||||||
|
'snowplow',
|
||||||
|
'socks',
|
||||||
|
'solar-panel',
|
||||||
|
'sort',
|
||||||
|
'sort-alpha-down',
|
||||||
|
'sort-alpha-down-alt',
|
||||||
|
'sort-alpha-up',
|
||||||
|
'sort-alpha-up-alt',
|
||||||
|
'sort-amount-down',
|
||||||
|
'sort-amount-down-alt',
|
||||||
|
'sort-amount-up',
|
||||||
|
'sort-amount-up-alt',
|
||||||
|
'sort-down',
|
||||||
|
'sort-numeric-down',
|
||||||
|
'sort-numeric-down-alt',
|
||||||
|
'sort-numeric-up',
|
||||||
|
'sort-numeric-up-alt',
|
||||||
|
'sort-up',
|
||||||
|
'spa',
|
||||||
|
'space-shuttle',
|
||||||
|
'spell-check',
|
||||||
|
'spider',
|
||||||
|
'spinner',
|
||||||
|
'splotch',
|
||||||
|
'spray-can',
|
||||||
|
'square',
|
||||||
|
'square-full',
|
||||||
|
'square-root-alt',
|
||||||
|
'stamp',
|
||||||
|
'star',
|
||||||
|
'star-and-crescent',
|
||||||
|
'star-half',
|
||||||
|
'star-half-alt',
|
||||||
|
'star-of-david',
|
||||||
|
'star-of-life',
|
||||||
|
'step-backward',
|
||||||
|
'step-forward',
|
||||||
|
'stethoscope',
|
||||||
|
'sticky-note',
|
||||||
|
'stop',
|
||||||
|
'stop-circle',
|
||||||
|
'stopwatch',
|
||||||
|
'store',
|
||||||
|
'store-alt',
|
||||||
|
'stream',
|
||||||
|
'street-view',
|
||||||
|
'strikethrough',
|
||||||
|
'stroopwafel',
|
||||||
|
'subscript',
|
||||||
|
'subway',
|
||||||
|
'suitcase',
|
||||||
|
'suitcase-rolling',
|
||||||
|
'sun',
|
||||||
|
'superscript',
|
||||||
|
'surprise',
|
||||||
|
'swatchbook',
|
||||||
|
'swimmer',
|
||||||
|
'swimming-pool',
|
||||||
|
'synagogue',
|
||||||
|
'sync',
|
||||||
|
'sync-alt',
|
||||||
|
'syringe',
|
||||||
|
'table',
|
||||||
|
'table-tennis',
|
||||||
|
'tablet',
|
||||||
|
'tablet-alt',
|
||||||
|
'tablets',
|
||||||
|
'tachometer-alt',
|
||||||
|
'tag',
|
||||||
|
'tags',
|
||||||
|
'tape',
|
||||||
|
'tasks',
|
||||||
|
'taxi',
|
||||||
|
'teeth',
|
||||||
|
'teeth-open',
|
||||||
|
'temperature-high',
|
||||||
|
'temperature-low',
|
||||||
|
'tenge',
|
||||||
|
'terminal',
|
||||||
|
'text-height',
|
||||||
|
'text-width',
|
||||||
|
'th',
|
||||||
|
'th-large',
|
||||||
|
'th-list',
|
||||||
|
'theater-masks',
|
||||||
|
'thermometer',
|
||||||
|
'thermometer-empty',
|
||||||
|
'thermometer-full',
|
||||||
|
'thermometer-half',
|
||||||
|
'thermometer-quarter',
|
||||||
|
'thermometer-three-quarters',
|
||||||
|
'thumbs-down',
|
||||||
|
'thumbs-up',
|
||||||
|
'thumbtack',
|
||||||
|
'ticket-alt',
|
||||||
|
'times',
|
||||||
|
'times-circle',
|
||||||
|
'tint',
|
||||||
|
'tint-slash',
|
||||||
|
'tired',
|
||||||
|
'toggle-off',
|
||||||
|
'toggle-on',
|
||||||
|
'toilet',
|
||||||
|
'toilet-paper',
|
||||||
|
'toolbox',
|
||||||
|
'tools',
|
||||||
|
'tooth',
|
||||||
|
'torah',
|
||||||
|
'torii-gate',
|
||||||
|
'tractor',
|
||||||
|
'trademark',
|
||||||
|
'traffic-light',
|
||||||
|
'trailer',
|
||||||
|
'train',
|
||||||
|
'tram',
|
||||||
|
'transgender',
|
||||||
|
'transgender-alt',
|
||||||
|
'trash',
|
||||||
|
'trash-alt',
|
||||||
|
'trash-restore',
|
||||||
|
'trash-restore-alt',
|
||||||
|
'tree',
|
||||||
|
'trophy',
|
||||||
|
'truck',
|
||||||
|
'truck-loading',
|
||||||
|
'truck-monster',
|
||||||
|
'truck-moving',
|
||||||
|
'truck-pickup',
|
||||||
|
'tshirt',
|
||||||
|
'tty',
|
||||||
|
'tv',
|
||||||
|
'umbrella',
|
||||||
|
'umbrella-beach',
|
||||||
|
'underline',
|
||||||
|
'undo',
|
||||||
|
'undo-alt',
|
||||||
|
'universal-access',
|
||||||
|
'university',
|
||||||
|
'unlink',
|
||||||
|
'unlock',
|
||||||
|
'unlock-alt',
|
||||||
|
'upload',
|
||||||
|
'user',
|
||||||
|
'user-alt',
|
||||||
|
'user-alt-slash',
|
||||||
|
'user-astronaut',
|
||||||
|
'user-check',
|
||||||
|
'user-circle',
|
||||||
|
'user-clock',
|
||||||
|
'user-cog',
|
||||||
|
'user-edit',
|
||||||
|
'user-friends',
|
||||||
|
'user-graduate',
|
||||||
|
'user-injured',
|
||||||
|
'user-lock',
|
||||||
|
'user-md',
|
||||||
|
'user-minus',
|
||||||
|
'user-ninja',
|
||||||
|
'user-nurse',
|
||||||
|
'user-plus',
|
||||||
|
'user-secret',
|
||||||
|
'user-shield',
|
||||||
|
'user-slash',
|
||||||
|
'user-tag',
|
||||||
|
'user-tie',
|
||||||
|
'user-times',
|
||||||
|
'users',
|
||||||
|
'users-cog',
|
||||||
|
'utensil-spoon',
|
||||||
|
'utensils',
|
||||||
|
'vector-square',
|
||||||
|
'venus',
|
||||||
|
'venus-double',
|
||||||
|
'venus-mars',
|
||||||
|
'vial',
|
||||||
|
'vials',
|
||||||
|
'video',
|
||||||
|
'video-slash',
|
||||||
|
'vihara',
|
||||||
|
'voicemail',
|
||||||
|
'volleyball-ball',
|
||||||
|
'volume-down',
|
||||||
|
'volume-mute',
|
||||||
|
'volume-off',
|
||||||
|
'volume-up',
|
||||||
|
'vote-yea',
|
||||||
|
'vr-cardboard',
|
||||||
|
'walking',
|
||||||
|
'wallet',
|
||||||
|
'warehouse',
|
||||||
|
'water',
|
||||||
|
'wave-square',
|
||||||
|
'weight',
|
||||||
|
'weight-hanging',
|
||||||
|
'wheelchair',
|
||||||
|
'wifi',
|
||||||
|
'wind',
|
||||||
|
'window-close',
|
||||||
|
'window-maximize',
|
||||||
|
'window-minimize',
|
||||||
|
'window-restore',
|
||||||
|
'wine-bottle',
|
||||||
|
'wine-glass',
|
||||||
|
'wine-glass-alt',
|
||||||
|
'won-sign',
|
||||||
|
'wrench',
|
||||||
|
'x-ray',
|
||||||
|
'yen-sign',
|
||||||
|
'yin-yang', ];
|
||||||
|
|
||||||
|
$tbicons = [];
|
||||||
|
foreach ($icons as $value) {
|
||||||
|
$tbicons[$value] = 'fas fa-'.$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Liste d'exemples d'icônes Font Awesome
|
||||||
|
return $tbicons;
|
||||||
|
}
|
||||||
|
}
|
33
src/Form/Type/ThreeDigitType.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\Type;
|
||||||
|
|
||||||
|
use App\Form\DataTransformer\ThreeDigitTransformer;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class ThreeDigitType extends AbstractType
|
||||||
|
{
|
||||||
|
private ThreeDigitTransformer $transformer;
|
||||||
|
|
||||||
|
public function __construct(ThreeDigitTransformer $transformer)
|
||||||
|
{
|
||||||
|
$this->transformer = $transformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder->addModelTransformer($this->transformer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParent(): string
|
||||||
|
{
|
||||||
|
return TextType::class;
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
|||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
|
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
@ -20,43 +21,45 @@ class UserType extends AbstractType
|
|||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
$builder
|
$builder
|
||||||
->add('submit', SubmitType::class, [
|
->add('submit', SubmitType::class, [
|
||||||
'label' => 'Valider',
|
'label' => 'Valider',
|
||||||
'attr' => ['class' => 'btn btn-success no-print'],
|
'attr' => ['class' => 'btn btn-success no-print'],
|
||||||
])
|
])
|
||||||
|
|
||||||
->add('username', TextType::class, [
|
->add('username', TextType::class, [
|
||||||
'label' => 'Login',
|
'label' => 'Login',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
->add('password', RepeatedType::class, [
|
||||||
|
'type' => PasswordType::class,
|
||||||
|
'required' => ('submit' == $options['mode'] ? true : false),
|
||||||
|
'options' => ['always_empty' => true],
|
||||||
|
'first_options' => ['label' => 'Mot de Passe', 'attr' => ['class' => 'form-control', 'style' => 'margin-bottom:15px', 'autocomplete' => 'new-password']],
|
||||||
|
'second_options' => ['label' => 'Confirmer Mot de Passe', 'attr' => ['class' => 'form-control', 'style' => 'margin-bottom:15px']],
|
||||||
|
])
|
||||||
|
|
||||||
|
->add('avatar', HiddenType::class)
|
||||||
|
|
||||||
|
->add('email', EmailType::class, [
|
||||||
|
'label' => 'Email',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ('profil' != $options['mode']) {
|
||||||
|
$builder
|
||||||
->add('roles', ChoiceType::class, [
|
->add('roles', ChoiceType::class, [
|
||||||
'choices' => ['ROLE_ADMIN' => 'ROLE_ADMIN', 'ROLE_USER' => 'ROLE_USER'],
|
'choices' => ['ROLE_ADMIN' => 'ROLE_ADMIN', 'ROLE_USER' => 'ROLE_USER'],
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'expanded' => true,
|
'expanded' => true,
|
||||||
])
|
])
|
||||||
|
|
||||||
->add('password', RepeatedType::class, [
|
|
||||||
'type' => PasswordType::class,
|
|
||||||
'required' => ('submit' == $options['mode'] ? true : false),
|
|
||||||
'options' => ['always_empty' => true],
|
|
||||||
'first_options' => ['label' => 'Mot de Passe', 'attr' => ['class' => 'form-control', 'style' => 'margin-bottom:15px', 'autocomplete' => 'new-password']],
|
|
||||||
'second_options' => ['label' => 'Confirmer Mot de Passe', 'attr' => ['class' => 'form-control', 'style' => 'margin-bottom:15px']],
|
|
||||||
])
|
|
||||||
|
|
||||||
->add('avatar')
|
|
||||||
|
|
||||||
->add('email', EmailType::class, [
|
|
||||||
'label' => 'Email',
|
|
||||||
])
|
|
||||||
|
|
||||||
->add('companys', EntityType::class, [
|
->add('companys', EntityType::class, [
|
||||||
'label' => 'Companie',
|
'label' => 'Compagnies',
|
||||||
'class' => Company::class,
|
'class' => Company::class,
|
||||||
'choice_label' => 'title',
|
'choice_label' => 'title',
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'attr' => ['class' => 'select2'],
|
'attr' => ['class' => 'select2'],
|
||||||
])
|
]);
|
||||||
;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver): void
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|