diff --git a/Controller/UploadController.php b/Controller/UploadController.php index 34d30e6..d36db64 100644 --- a/Controller/UploadController.php +++ b/Controller/UploadController.php @@ -282,7 +282,7 @@ protected function handleUpload(File $file, ResponseInterface $response, Request if (!($file instanceof FileInterface)) { $file = new FilesystemFile($file); } - + // validate $this->validate($file, $request, $context); diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 13a72fd..99c2929 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -54,6 +54,10 @@ public function getConfigTreeBuilder() ->defaultValue(\PHP_INT_MAX) ->info('Set max_size to -1 for gracefully downgrade this number to the systems max upload size.') ->end() + ->scalarNode('max_files') + ->defaultValue(\PHP_INT_MAX) + ->info('Set max files.') + ->end() ->booleanNode('use_orphanage')->defaultFalse()->end() ->booleanNode('enable_progress')->defaultFalse()->end() ->booleanNode('enable_cancelation')->defaultFalse()->end() diff --git a/Driver/AnnotationDriver.php b/Driver/AnnotationDriver.php new file mode 100644 index 0000000..27f7dd0 --- /dev/null +++ b/Driver/AnnotationDriver.php @@ -0,0 +1,86 @@ +reader = $reader; + } + + public function loadDataForClass(\ReflectionClass $class) + { + if (!$this->isUploadable($class)) { + throw new ClassNotUploadableException(); + } + + $data = array(); + + foreach ($class->getProperties() as $property) { + $uploadableField = $this->reader->getPropertyAnnotation($property, self::UPLOADABLE_FIELD_ANNOTATION); + if ($uploadableField === null) { + continue; + } + + $fieldData = array( + 'mapping' => $uploadableField->getMapping(), + 'nameAddFunction' => $uploadableField->getNameAddFunction(), + 'nameGetFunction' => $uploadableField->getNameGetFunction() + ); + +// $metadata->fields[$property->getName()] = $fieldData; + $data[$property->getName()] = $fieldData; + } + +// return $metadata; + return $data; + } + + protected function isUploadable(\ReflectionClass $class) + { + return $this->reader->getClassAnnotation($class, self::UPLOADABLE_ANNOTATION) !== null; + } + + public function getDataByFieldName(\ReflectionClass $class, $fieldName) + { + if (!$this->isUploadable($class)) { + throw new ClassNotUploadableException(); + } + + if (!$fieldName) { + throw new ValueEmptyException(); + } + + $property = $class->getProperty($fieldName); + + if (!$property) { + return false; + } + + $uploadableField = $this->reader->getPropertyAnnotation($property, self::UPLOADABLE_FIELD_ANNOTATION); + + if (!$uploadableField) { + return false; + } + + return array( + 'mapping' => $uploadableField->getMapping(), + 'nameAddFunction' => $uploadableField->getNameAddFunction(), + 'nameGetFunction' => $uploadableField->getNameGetFunction() + ); + } +} diff --git a/EventListener/MaxFilesValidationListener.php b/EventListener/MaxFilesValidationListener.php new file mode 100644 index 0000000..bb92c56 --- /dev/null +++ b/EventListener/MaxFilesValidationListener.php @@ -0,0 +1,42 @@ +doctrine = $doctrine; + } + + public function onValidate(ValidationEvent $event) + { +// $config = $event->getConfig(); +// +// if (!isset($config['max_files'])) { +// return; +// } +// +// $request = $event->getRequest(); +// $requestId = $request->get('_glavweb_uploader_request_id'); +// $qb = $this->doctrine->getEntityManager()->createQueryBuilder(); +// $qb->select('COUNT(m.id)') +// ->from('GlavwebUploaderBundle:Media', 'm') +// ->where('m.requestId = :requestId') +// ->setParameter('requestId', $requestId); +// $result = $qb->getQuery()->getSingleScalarResult(); +// +// if ($result >= $config['max_files']) { +// throw new ValidationException('error.maxfiles'); +// } + } +} diff --git a/EventListener/MaxSizeValidationListener.php b/EventListener/MaxSizeValidationListener.php index 313c8a7..1722f01 100644 --- a/EventListener/MaxSizeValidationListener.php +++ b/EventListener/MaxSizeValidationListener.php @@ -12,7 +12,7 @@ public function onValidate(ValidationEvent $event) $config = $event->getConfig(); $file = $event->getFile(); - if ($file->getSize() > $config['max_size']) { + if ($file->getSize() > $config['max_size']*(1024*1024)) { throw new ValidationException('error.maxsize'); } } diff --git a/Exception/ClassNotUploadableException.php b/Exception/ClassNotUploadableException.php new file mode 100644 index 0000000..feccf6c --- /dev/null +++ b/Exception/ClassNotUploadableException.php @@ -0,0 +1,12 @@ +router = $router; - $this->mediaHelper = $mediaHelper; + $this->router = $router; + $this->mediaHelper = $mediaHelper; + $this->config = $config; + $this->driverAnnotation = $driverAnnotation; } /** - * @param FormView $view + * @param FormView $view * @param FormInterface $form - * @param array $options + * @param array $options + * @throws MappingNotSetException + * @throws NotFoundPropertiesInAnnotationException + * @throws \Glavweb\UploaderBundle\Exception\ClassNotUploadableException + * @throws \Glavweb\UploaderBundle\Exception\ValueEmptyException */ public function buildView(FormView $view, FormInterface $form, array $options) { - if (!isset($options['files'])) { - $options['files'] = array(); - } + $entity = $form->getParent()->getData(); + $fieldName = $form->getConfig()->getName(); if (!isset($options['requestId'])) { $options['requestId'] = uniqid(); } - $files = $this->prepareFiles($options['files']); + $dataPropertyAnnotation = $this->driverAnnotation->getDataByFieldName(new \ReflectionClass($entity), $fieldName); + + if (!$dataPropertyAnnotation) { + throw new NotFoundPropertiesInAnnotationException(); + } + + $files = $entity->$dataPropertyAnnotation['nameGetFunction'](); + $context = $dataPropertyAnnotation['mapping']; + + if (!$context) { + throw new MappingNotSetException(); + } + + $config = $this->getConfigByContext($context); + $maxFiles = $config['max_files']; $view->vars['uploadedFilesTpl'] = $options['uploadedFilesTpl']; $view->vars['uploadItemTpl'] = $options['uploadItemTpl']; $view->vars['viewFormTpl'] = $options['viewFormTpl']; - $view->vars['type'] = $options['type']; + $view->vars['type'] = $context; $view->vars['previewImg'] = $options['previewImg']; $view->vars['useLink'] = $options['useLink']; $view->vars['useForm'] = $options['useForm']; $view->vars['showMark'] = $options['showMark']; $view->vars['showUploadButton'] = $options['showUploadButton']; $view->vars['files'] = $files; + $view->vars['countFiles'] = $files->count(); $view->vars['showLabel'] = $options['showLabel']; $view->vars['requestId'] = $options['requestId']; - $view->vars['uploadDir'] = $this->mediaHelper->getUploadDirectoryUrl($options['type']); - $view->vars['deleteUrl'] = $this->router->generate('glavweb_uploader_delete', array('context' => $options['type'])); - $view->vars['renameUrl'] = $this->router->generate('glavweb_uploader_rename', array('context' => $options['type'])); + $view->vars['uploadDir'] = $this->mediaHelper->getUploadDirectoryUrl($context); + $view->vars['deleteUrl'] = $this->router->generate('glavweb_uploader_delete', array('context' => $context)); + $view->vars['renameUrl'] = $this->router->generate('glavweb_uploader_rename', array('context' => $context)); + $view->vars['maxFiles'] = $maxFiles; + $view->vars['maxSize'] = $config['max_size']; } /** @@ -75,14 +112,12 @@ public function buildView(FormView $view, FormInterface $form, array $options) public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( - 'files' => array(), 'previewImg' => null, 'requestId' => null, 'useLink' => true, 'useForm' => true, 'showMark' => true, 'showUploadButton' => true, - 'type' => null, 'uploadedFilesTpl' => 'GlavwebUploaderBundle:Form:base_upload_item_tpl.html.twig', 'uploadItemTpl' => 'GlavwebUploaderBundle:Form:base_uploaded_files.html.twig', 'viewFormTpl' => 'GlavwebUploaderBundle:Form:view-form.html.twig', @@ -125,4 +160,13 @@ protected function prepareFiles($files) return $files; } + + /** + * @param string $context + * @return array + */ + protected function getConfigByContext($context) + { + return $this->config['mappings'][$context]; + } } \ No newline at end of file diff --git a/Manager/UploaderManager.php b/Manager/UploaderManager.php index 465ae5d..bd4a91e 100644 --- a/Manager/UploaderManager.php +++ b/Manager/UploaderManager.php @@ -3,6 +3,7 @@ namespace Glavweb\UploaderBundle\Manager; use Glavweb\UploaderBundle\Exception\ProviderNotFoundException; +use Glavweb\UploaderBundle\Exception\RequestEmptyException; use Glavweb\UploaderBundle\File\FileInterface; use Glavweb\UploaderBundle\Model\MediaInterface; use Glavweb\UploaderBundle\Provider\FileProvider; @@ -41,6 +42,16 @@ class UploaderManager extends ContainerAware 'php' ); + /** + * @var \Metadata\Driver\DriverInterface + */ + private $driverAnnotation; + + /** + * @var \Symfony\Component\HttpFoundation\RequestStack + */ + private $requestStack; + /** * @param array $config */ @@ -61,6 +72,30 @@ public function getStorage() return $this->storage; } + /** + * @return \Glavweb\UploaderBundle\Driver\AnnotationDriver + */ + public function getDriverAnnotation() + { + if (!$this->driverAnnotation) { + $this->driverAnnotation = $this->container->get('glavweb_uploader.data_driver.annotation'); + } + + return $this->driverAnnotation; + } + + /** + * @return \Symfony\Component\HttpFoundation\RequestStack + */ + public function getRequestStack() + { + if (!$this->requestStack) { + $this->requestStack = $this->container->get('request_stack'); + } + + return $this->requestStack; + } + /** * @return \Glavweb\UploaderBundle\Model\ModelManagerInterface */ @@ -243,17 +278,66 @@ public function getProviderByName($name) } /** - * @param $context - * @param $requestId + * @param $entity + * @param null $requestId * @param array $options - * @return array + * @throws RequestEmptyException + * @throws \Glavweb\UploaderBundle\Exception\RequestEmptyException */ - public function handleUpload($context, $requestId, $options = array()) + public function handleUpload($entity, $requestId=null, $options = array()) { - $this->removeMarkedMedia($context, $requestId); - $this->renameMarkedMedia($context, $requestId); + $request = $this->getRequestStack()->getCurrentRequest(); + $requestId = $request->get('_glavweb_uploader_request_id'); - return $this->uploadOrphans($context, $requestId); + if (!$requestId) { + throw new RequestEmptyException(); + } + + $driverAnnotation = $this->getDriverAnnotation(); + $data = $driverAnnotation->loadDataForClass(new \ReflectionClass($entity)); + + foreach ($data as $property) { + $context = $property['mapping']; + $mediaEntities = $entity->$property['nameGetFunction'](); + + $this->removeMarkedMedia($context, $requestId); + $this->renameMarkedMedia($context, $requestId); + $uploadedMediaEntities = $this->uploadOrphans($context, $requestId); + + $this->addMediaEntities($uploadedMediaEntities, $entity, $property ); + + $positions = explode(',', $request->get('_glavweb_uploader_sorted_array')[$context]); + + $this->sortMedia($mediaEntities, $positions); + } + } + + /** + * @param $mediaEntities + * @param $entity + * @param $property + */ + private function addMediaEntities($mediaEntities, $entity, $property) + { + $nameAddFunction = $property['nameAddFunction']; + $nameGetFunction = $property['nameGetFunction']; + $mapping = $property['mapping']; + + $contextConfig = $this->config['mappings'][$mapping]; + + $entityMedia = $entity->$nameGetFunction(); + + $maxFiles = $contextConfig['max_files'] - $entityMedia->count(); + + if (!empty($mediaEntities)) { + foreach ($mediaEntities as $mediaEntity) { + if ($maxFiles == 0) { + break; + } + $entity->$nameAddFunction($mediaEntity); + $maxFiles--; + } + } } /** diff --git a/Mapping/Annotation/Uploadable.php b/Mapping/Annotation/Uploadable.php new file mode 100644 index 0000000..2275685 --- /dev/null +++ b/Mapping/Annotation/Uploadable.php @@ -0,0 +1,13 @@ +mapping = $options['mapping']; + } else { + throw new \InvalidArgumentException('The "mapping" attribute of UploadableField is required.'); + } + + if (isset($options['nameAddFunction'])) { + $this->nameAddFunction = $options['nameAddFunction']; + } else { + throw new \InvalidArgumentException('The "nameAddFunction" attribute of UploadableField is required.'); + } + + if (isset($options['nameGetFunction'])) { + $this->nameGetFunction = $options['nameGetFunction']; + } else { + throw new \InvalidArgumentException('The "nameGetFunction" attribute of UploadableField is required.'); + } + } + + /** + * Gets the mapping name. + * + * @return string The mapping name. + */ + public function getMapping() + { + return $this->mapping; + } + + /** + * Sets the mapping name. + * + * @param $mapping The mapping name. + */ + public function setMapping($mapping) + { + $this->mapping = $mapping; + } + + /** + * @return string + */ + public function getNameAddFunction() + { + return $this->nameAddFunction; + } + + /** + * @param string $nameAddFunction + */ + public function setNameAddFunction($nameAddFunction) + { + $this->nameAddFunction = $nameAddFunction; + } + + /** + * @return string + */ + public function getNameGetFunction() + { + return $this->nameGetFunction; + } + + /** + * @param string $nameGetFunction + */ + public function setNameGetFunction($nameGetFunction) + { + $this->nameGetFunction = $nameGetFunction; + } +} diff --git a/Resources/config/services.xml b/Resources/config/services.xml index af31ded..d8d598e 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -26,6 +26,8 @@ %glavweb_uploader.config% + + @@ -40,6 +42,8 @@ + %glavweb_uploader.config% + @@ -86,5 +90,14 @@ + + + + + + + + + diff --git a/Resources/public/css/dropzone.css b/Resources/public/css/dropzone.css new file mode 100644 index 0000000..8bb793f --- /dev/null +++ b/Resources/public/css/dropzone.css @@ -0,0 +1 @@ +#previews{margin-top:40px}.hidden-upload-item{display:none!important}.dz{min-height:80px;-webkit-border-radius:3px;border-radius:3px;padding:0}.dz.dz-clickable,.dz.dz-clickable .dz-message,.dz.dz-clickable .dz-message span{cursor:pointer}.dz.dz-clickable *{cursor:default}.dz .dz-message{border:1px solid rgba(0,0,0,.03);background:rgba(0,0,0,.03);padding:15px;opacity:1;-ms-filter:none;filter:none;min-height:80px;cursor:pointer!important;margin:15px 0;text-align:center}.dz.dz-drag-hover{border-color:rgba(0,0,0,.15);background:rgba(0,0,0,.04)}.dz.dz-drag-hover .dz-message{opacity:.15;filter:alpha(opacity=15);-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=15)"}.dz.dz-started .dz-message{display:block;opacity:0;filter:alpha(opacity=0);-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"}.dz .dz-preview{position:relative;display:inline-block;vertical-align:top;border:none;font-size:14px;width:100%;padding:15px;-webkit-border-radius:4px;-webkit-background-clip:padding-box;-moz-border-radius:4px;-moz-background-clip:padding;border-radius:4px;background-clip:padding-box}.dz .dz-preview.current{border:2px dashed #2c6600}.dz .dz-preview.dz-file-preview [data-dz-thumbnail]{display:none}.dz .dz-preview .dz-details,.dz .dz-preview .dz-details__img{position:relative}.dz .dz-preview .dz-details__img .dz-button{position:absolute;right:0;bottom:0}.dz .dz-preview .dz-details__img img{position:relative;top:0;left:0}.dz .dz-preview .dz-details .dz-filename{overflow:hidden;height:100%}.dz .dz-preview .dz-details .dz-description{padding:10px 0}.dz .dz-preview .dz-details .dz-size{position:absolute;bottom:-28px;left:3px;height:28px;line-height:28px}.dz .dz-preview.dz-error .dz-error-mark,.dz .dz-preview.dz-success .dz-success-mark,.dz .dz-preview:hover.dz-error .dz-error-message{display:block}.dz .dz-preview .dz-error-mark,.dz .dz-preview .dz-success-mark{position:absolute;width:40px;height:40px;font-size:30px;text-align:center;right:-10px;top:-10px}.dz .dz-preview .dz-error-mark{color:#ee162d}.dz .dz-preview .dz-success-mark{color:#8cc657}.dz .dz-preview .dz-progress{position:absolute;top:100px;left:6px;right:6px;height:6px;background:#d7d7d7;display:none}.dz .dz-preview .dz-progress .dz-upload{bottom:0;background-color:#8cc657}.dz .dz-default.dz-message{opacity:1;-ms-filter:none;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;-o-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out;background-image:url(../images/spritemap.png);background-repeat:no-repeat;background-position:0 0;position:absolute;width:428px;height:123px;margin-left:-214px;margin-top:-61.5px;top:50%;left:50%}.dz .dz-default.dz-message span{display:none}.dz.dz-square .dz-default.dz-message{background-position:0 -123px;width:268px;margin-left:-134px;height:174px;margin-top:-87px}.dz .dz-button{text-align:center}.dz .dz-button .dz-button-active{display:inline-block}.dz .dz-button .disable{display:none}.dz-previews .dz-preview{background:rgba(255,255,255,.8);position:relative;display:inline-block;margin:17px;vertical-align:top;border:1px solid #acacac;padding:6px;-webkit-box-shadow:1px 1px 4px rgba(0,0,0,.16);box-shadow:1px 1px 4px rgba(0,0,0,.16);font-size:14px}.dz-previews .dz-preview.dz-file-preview [data-dz-thumbnail]{display:none}.dz-previews .dz-preview .dz-details{position:relative;background:#ebebeb;padding:5px;margin-bottom:22px}.dz-previews .dz-preview .dz-details .dz-filename{overflow:hidden;height:100%}.dz-previews .dz-preview .dz-details img{position:relative;top:0;left:0;width:160px;height:120px}.dz-previews .dz-preview .dz-details .dz-size{position:absolute;bottom:-28px;left:3px;height:28px;line-height:28px}.dz-previews .dz-preview.dz-error .dz-error-mark,.dz-previews .dz-preview.dz-success .dz-success-mark,.dz-previews .dz-preview:hover.dz-error .dz-error-message{display:block}.dz-previews .dz-preview .dz-error-mark,.dz-previews .dz-preview .dz-success-mark{position:absolute;width:40px;height:40px;font-size:30px;text-align:center;right:-10px;top:-10px}.dz-previews .dz-preview .dz-error-mark{color:#ee162d}.dz-previews .dz-preview .dz-success-mark{color:#8cc657}.dz-previews .dz-preview .dz-progress{position:absolute;top:100px;left:6px;right:6px;height:6px;background:#d7d7d7;display:none}.dz-previews .dz-preview .dz-progress .dz-upload{bottom:0;background-color:#8cc657}.dz-previews .dz-preview.dz-processing .dz-progress{display:block}.dz .dz-preview.dz-processing .dz-progress,.dz-previews .dz-preview .dz .dz-preview .dz-error-message,.dz-previews .dz-preview .dz-error-message{display:none;position:absolute;top:-5px;left:-20px;background:rgba(245,245,245,.8);padding:8px 10px;color:#800;min-width:140px;max-width:500px;z-index:500}.dz .dz-preview .dz-error-message,.dz-previews .dz-preview .dz-error-message{opacity:1!important}.dz .dz-preview .dz-error-message span,.dz-previews .dz-preview .dz-error-message span{display:block;color:#fff;background-color:#800;border-color:#800;padding:0 6px;font-size:14px;line-height:24px;border-radius:2px}.dz .dz-preview .dz-details__img img{max-width:100%;width:inherit}@media all and (-webkit-min-device-pixel-ratio:1.5){.dz .dz-default.dz-message{background-image:url(../images/spritemap@2x.png);-webkit-background-size:428px 406px;-moz-background-size:428px 406px;background-size:428px 406px}}.dz .dz-preview.dz-error .dz-error-mark,.dz .dz-preview.dz-success .dz-success-mark,.dz-previews .dz-preview.dz-error .dz-error-mark,.dz-previews .dz-preview.dz-success .dz-success-mark{opacity:1;-ms-filter:none;filter:none}.dz .dz-preview.dz-error .dz-progress .dz-upload,.dz-previews .dz-preview.dz-error .dz-progress .dz-upload{background:#ee1e2d}.dz .dz-preview .dz-error-mark,.dz .dz-preview .dz-success-mark,.dz-previews .dz-preview .dz-error-mark,.dz-previews .dz-preview .dz-success-mark{display:block;opacity:0;filter:alpha(opacity=0);-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";-webkit-transition:opacity .4s ease-in-out;-moz-transition:opacity .4s ease-in-out;-o-transition:opacity .4s ease-in-out;-ms-transition:opacity .4s ease-in-out;transition:opacity .4s ease-in-out;background-image:url(../images/spritemap.png);background-repeat:no-repeat}@media all and (-webkit-min-device-pixel-ratio:1.5){.dz .dz-preview .dz-error-mark,.dz .dz-preview .dz-success-mark,.dz-previews .dz-preview .dz-error-mark,.dz-previews .dz-preview .dz-success-mark{background-image:url(../images/spritemap@2x.png);-webkit-background-size:428px 406px;-moz-background-size:428px 406px;background-size:428px 406px}}.dz .dz-preview .dz-error-mark span,.dz .dz-preview .dz-success-mark span,.dz-previews .dz-preview .dz-error-mark span,.dz-previews .dz-preview .dz-success-mark span{display:none}.dz .dz-preview .dz-error-mark,.dz-previews .dz-preview .dz-error-mark{background-position:-268px -123px}.dz .dz-preview .dz-success-mark,.dz-previews .dz-preview .dz-success-mark{background-position:-268px -163px}.dz .dz-preview .dz-progress .dz-upload,.dz-previews .dz-preview .dz-progress .dz-upload{-webkit-animation:loading .4s linear infinite;-moz-animation:loading .4s linear infinite;-o-animation:loading .4s linear infinite;-ms-animation:loading .4s linear infinite;animation:loading .4s linear infinite;-webkit-transition:width .3s ease-in-out;-moz-transition:width .3s ease-in-out;-o-transition:width .3s ease-in-out;-ms-transition:width .3s ease-in-out;transition:width .3s ease-in-out;-webkit-border-radius:2px;border-radius:2px;position:absolute;top:0;left:0;width:0;height:100%;background-image:url(../images/spritemap.png);background-repeat:repeat-x;background-position:0 -400px}@media all and (-webkit-min-device-pixel-ratio:1.5){.dz .dz-preview .dz-progress .dz-upload,.dz-previews .dz-preview .dz-progress .dz-upload{background-image:url(../images/spritemap@2x.png);-webkit-background-size:428px 406px;-moz-background-size:428px 406px;background-size:428px 406px}}.dz .dz-preview.dz-success .dz-progress,.dz-previews .dz-preview.dz-success .dz-progress{display:block;opacity:0;filter:alpha(opacity=0);-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";-webkit-transition:opacity .4s ease-in-out;-moz-transition:opacity .4s ease-in-out;-o-transition:opacity .4s ease-in-out;-ms-transition:opacity .4s ease-in-out;transition:opacity .4s ease-in-out}.dz .dz-preview .dz-error-message,.dz-previews .dz-preview .dz-error-message{display:block;filter:alpha(opacity=0);-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;-o-transition:opacity .3s ease-in-out;-ms-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.dz .dz-preview:hover.dz-error .dz-error-message,.dz-previews .dz-preview:hover.dz-error .dz-error-message{opacity:1;-ms-filter:none;filter:none}#actions{margin:15px 0}div.table{display:table}div.table .file-row{display:table-row}div.table .file-row>div{display:table-cell;vertical-align:top;border-top:1px solid #ddd;padding:8px}div.table .file-row:nth-child(odd){background:#f9f9f9}#previews .file-row.dz-success .progress{opacity:0;transition:opacity .3s linear}#previews .file-row .delete,#previews .file-row.dz-success .cancel,#previews .file-row.dz-success .start{display:none}#previews .file-row.dz-success .delete{display:block}.do-remove-file{margin-bottom:0!important} \ No newline at end of file diff --git a/Resources/public/js/Sortable.js b/Resources/public/js/Sortable.js new file mode 100644 index 0000000..9a24f6e --- /dev/null +++ b/Resources/public/js/Sortable.js @@ -0,0 +1,1144 @@ +/**! + * Sortable + * @author RubaXa + * @license MIT + */ + + +(function (factory) { + "use strict"; + + if (typeof define === "function" && define.amd) { + define(factory); + } + else if (typeof module != "undefined" && typeof module.exports != "undefined") { + module.exports = factory(); + } + else if (typeof Package !== "undefined") { + Sortable = factory(); // export for Meteor.js + } + else { + /* jshint sub:true */ + window["Sortable"] = factory(); + } +})(function () { + "use strict"; + + var dragEl, + ghostEl, + cloneEl, + rootEl, + nextEl, + + scrollEl, + scrollParentEl, + + lastEl, + lastCSS, + + oldIndex, + newIndex, + + activeGroup, + autoScroll = {}, + + tapEvt, + touchEvt, + + /** @const */ + RSPACE = /\s+/g, + + expando = 'Sortable' + (new Date).getTime(), + + win = window, + document = win.document, + parseInt = win.parseInt, + + supportDraggable = !!('draggable' in document.createElement('div')), + + _silent = false, + + abs = Math.abs, + slice = [].slice, + + touchDragOverListeners = [], + + _autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl) { + // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521 + if (rootEl && options.scroll) { + var el, + rect, + sens = options.scrollSensitivity, + speed = options.scrollSpeed, + + x = evt.clientX, + y = evt.clientY, + + winWidth = window.innerWidth, + winHeight = window.innerHeight, + + vx, + vy + ; + + // Delect scrollEl + if (scrollParentEl !== rootEl) { + scrollEl = options.scroll; + scrollParentEl = rootEl; + + if (scrollEl === true) { + scrollEl = rootEl; + + do { + if ((scrollEl.offsetWidth < scrollEl.scrollWidth) || + (scrollEl.offsetHeight < scrollEl.scrollHeight) + ) { + break; + } + /* jshint boss:true */ + } while (scrollEl = scrollEl.parentNode); + } + } + + if (scrollEl) { + el = scrollEl; + rect = scrollEl.getBoundingClientRect(); + vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens); + vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens); + } + + + if (!(vx || vy)) { + vx = (winWidth - x <= sens) - (x <= sens); + vy = (winHeight - y <= sens) - (y <= sens); + + /* jshint expr:true */ + (vx || vy) && (el = win); + } + + + if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) { + autoScroll.el = el; + autoScroll.vx = vx; + autoScroll.vy = vy; + + clearInterval(autoScroll.pid); + + if (el) { + autoScroll.pid = setInterval(function () { + if (el === win) { + win.scrollTo(win.pageXOffset + vx * speed, win.pageYOffset + vy * speed); + } else { + vy && (el.scrollTop += vy * speed); + vx && (el.scrollLeft += vx * speed); + } + }, 24); + } + } + } + }, 30) + ; + + + + /** + * @class Sortable + * @param {HTMLElement} el + * @param {Object} [options] + */ + function Sortable(el, options) { + this.el = el; // root element + this.options = options = _extend({}, options); + + + // Export instance + el[expando] = this; + + + // Default options + var defaults = { + group: Math.random(), + sort: true, + disabled: false, + store: null, + handle: null, + scroll: true, + scrollSensitivity: 30, + scrollSpeed: 10, + draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*', + ghostClass: 'sortable-ghost', + ignore: 'a, img', + filter: null, + animation: 0, + setData: function (dataTransfer, dragEl) { + dataTransfer.setData('Text', dragEl.textContent); + }, + dropBubble: false, + dragoverBubble: false, + dataIdAttr: 'data-id', + delay: 0 + }; + + + // Set default options + for (var name in defaults) { + !(name in options) && (options[name] = defaults[name]); + } + + + var group = options.group; + + if (!group || typeof group != 'object') { + group = options.group = { name: group }; + } + + + ['pull', 'put'].forEach(function (key) { + if (!(key in group)) { + group[key] = true; + } + }); + + + options.groups = ' ' + group.name + (group.put.join ? ' ' + group.put.join(' ') : '') + ' '; + + + // Bind all private methods + for (var fn in this) { + if (fn.charAt(0) === '_') { + this[fn] = _bind(this, this[fn]); + } + } + + + // Bind events + _on(el, 'mousedown', this._onTapStart); + _on(el, 'touchstart', this._onTapStart); + + _on(el, 'dragover', this); + _on(el, 'dragenter', this); + + touchDragOverListeners.push(this._onDragOver); + + // Restore sorting + options.store && this.sort(options.store.get(this)); + } + + + Sortable.prototype = /** @lends Sortable.prototype */ { + constructor: Sortable, + + _onTapStart: function (/** Event|TouchEvent */evt) { + var _this = this, + el = this.el, + options = this.options, + type = evt.type, + touch = evt.touches && evt.touches[0], + target = (touch || evt).target, + originalTarget = target, + filter = options.filter; + + + if (type === 'mousedown' && evt.button !== 0 || options.disabled) { + return; // only left button or enabled + } + + target = _closest(target, options.draggable, el); + + if (!target) { + return; + } + + // get the index of the dragged element within its parent + oldIndex = _index(target); + + // Check filter + if (typeof filter === 'function') { + if (filter.call(this, evt, target, this)) { + _dispatchEvent(_this, originalTarget, 'filter', target, el, oldIndex); + evt.preventDefault(); + return; // cancel dnd + } + } + else if (filter) { + filter = filter.split(',').some(function (criteria) { + criteria = _closest(originalTarget, criteria.trim(), el); + + if (criteria) { + _dispatchEvent(_this, criteria, 'filter', target, el, oldIndex); + return true; + } + }); + + if (filter) { + evt.preventDefault(); + return; // cancel dnd + } + } + + + if (options.handle && !_closest(originalTarget, options.handle, el)) { + return; + } + + + // Prepare `dragstart` + this._prepareDragStart(evt, touch, target); + }, + + _prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target) { + var _this = this, + el = _this.el, + options = _this.options, + ownerDocument = el.ownerDocument, + dragStartFn; + + if (target && !dragEl && (target.parentNode === el)) { + tapEvt = evt; + + rootEl = el; + dragEl = target; + nextEl = dragEl.nextSibling; + activeGroup = options.group; + + dragStartFn = function () { + // Delayed drag has been triggered + // we can re-enable the events: touchmove/mousemove + _this._disableDelayedDrag(); + + // Make the element draggable + dragEl.draggable = true; + + // Disable "draggable" + options.ignore.split(',').forEach(function (criteria) { + _find(dragEl, criteria.trim(), _disableDraggable); + }); + + // Bind the events: dragstart/dragend + _this._triggerDragStart(touch); + }; + + _on(ownerDocument, 'mouseup', _this._onDrop); + _on(ownerDocument, 'touchend', _this._onDrop); + _on(ownerDocument, 'touchcancel', _this._onDrop); + + if (options.delay) { + // If the user moves the pointer before the delay has been reached: + // disable the delayed drag + _on(ownerDocument, 'mousemove', _this._disableDelayedDrag); + _on(ownerDocument, 'touchmove', _this._disableDelayedDrag); + + _this._dragStartTimer = setTimeout(dragStartFn, options.delay); + } else { + dragStartFn(); + } + } + }, + + _disableDelayedDrag: function () { + var ownerDocument = this.el.ownerDocument; + + clearTimeout(this._dragStartTimer); + + _off(ownerDocument, 'mousemove', this._disableDelayedDrag); + _off(ownerDocument, 'touchmove', this._disableDelayedDrag); + }, + + _triggerDragStart: function (/** Touch */touch) { + if (touch) { + // Touch device support + tapEvt = { + target: dragEl, + clientX: touch.clientX, + clientY: touch.clientY + }; + + this._onDragStart(tapEvt, 'touch'); + } + else if (!supportDraggable) { + this._onDragStart(tapEvt, true); + } + else { + _on(dragEl, 'dragend', this); + _on(rootEl, 'dragstart', this._onDragStart); + } + + try { + if (document.selection) { + document.selection.empty(); + } else { + window.getSelection().removeAllRanges(); + } + } catch (err) { + } + }, + + _dragStarted: function () { + if (rootEl && dragEl) { + // Apply effect + _toggleClass(dragEl, this.options.ghostClass, true); + + Sortable.active = this; + + // Drag start event + _dispatchEvent(this, rootEl, 'start', dragEl, rootEl, oldIndex); + } + }, + + _emulateDragOver: function () { + if (touchEvt) { + _css(ghostEl, 'display', 'none'); + + var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY), + parent = target, + groupName = ' ' + this.options.group.name + '', + i = touchDragOverListeners.length; + + if (parent) { + do { + if (parent[expando] && parent[expando].options.groups.indexOf(groupName) > -1) { + while (i--) { + touchDragOverListeners[i]({ + clientX: touchEvt.clientX, + clientY: touchEvt.clientY, + target: target, + rootEl: parent + }); + } + + break; + } + + target = parent; // store last element + } + /* jshint boss:true */ + while (parent = parent.parentNode); + } + + _css(ghostEl, 'display', ''); + } + }, + + + _onTouchMove: function (/**TouchEvent*/evt) { + if (tapEvt) { + var touch = evt.touches ? evt.touches[0] : evt, + dx = touch.clientX - tapEvt.clientX, + dy = touch.clientY - tapEvt.clientY, + translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)'; + + touchEvt = touch; + + _css(ghostEl, 'webkitTransform', translate3d); + _css(ghostEl, 'mozTransform', translate3d); + _css(ghostEl, 'msTransform', translate3d); + _css(ghostEl, 'transform', translate3d); + + evt.preventDefault(); + } + }, + + + _onDragStart: function (/**Event*/evt, /**boolean*/useFallback) { + var dataTransfer = evt.dataTransfer, + options = this.options; + + this._offUpEvents(); + + if (activeGroup.pull == 'clone') { + cloneEl = dragEl.cloneNode(true); + _css(cloneEl, 'display', 'none'); + rootEl.insertBefore(cloneEl, dragEl); + } + + if (useFallback) { + var rect = dragEl.getBoundingClientRect(), + css = _css(dragEl), + ghostRect; + + ghostEl = dragEl.cloneNode(true); + + _css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10)); + _css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10)); + _css(ghostEl, 'width', rect.width); + _css(ghostEl, 'height', rect.height); + _css(ghostEl, 'opacity', '0.8'); + _css(ghostEl, 'position', 'fixed'); + _css(ghostEl, 'zIndex', '100000'); + + rootEl.appendChild(ghostEl); + + // Fixing dimensions. + ghostRect = ghostEl.getBoundingClientRect(); + _css(ghostEl, 'width', rect.width * 2 - ghostRect.width); + _css(ghostEl, 'height', rect.height * 2 - ghostRect.height); + + if (useFallback === 'touch') { + // Bind touch events + _on(document, 'touchmove', this._onTouchMove); + _on(document, 'touchend', this._onDrop); + _on(document, 'touchcancel', this._onDrop); + } else { + // Old brwoser + _on(document, 'mousemove', this._onTouchMove); + _on(document, 'mouseup', this._onDrop); + } + + this._loopId = setInterval(this._emulateDragOver, 150); + } + else { + if (dataTransfer) { + dataTransfer.effectAllowed = 'move'; + options.setData && options.setData.call(this, dataTransfer, dragEl); + } + + _on(document, 'drop', this); + } + + setTimeout(this._dragStarted, 0); + }, + + _onDragOver: function (/**Event*/evt) { + var el = this.el, + target, + dragRect, + revert, + options = this.options, + group = options.group, + groupPut = group.put, + isOwner = (activeGroup === group), + canSort = options.sort; + + if (evt.preventDefault !== void 0) { + evt.preventDefault(); + !options.dragoverBubble && evt.stopPropagation(); + } + + if (activeGroup && !options.disabled && + (isOwner + ? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list + : activeGroup.pull && groupPut && ( + (activeGroup.name === group.name) || // by Name + (groupPut.indexOf && ~groupPut.indexOf(activeGroup.name)) // by Array + ) + ) && + (evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback + ) { + // Smart auto-scrolling + _autoScroll(evt, options, this.el); + + if (_silent) { + return; + } + + target = _closest(evt.target, options.draggable, el); + dragRect = dragEl.getBoundingClientRect(); + + + if (revert) { + _cloneHide(true); + + if (cloneEl || nextEl) { + rootEl.insertBefore(dragEl, cloneEl || nextEl); + } + else if (!canSort) { + rootEl.appendChild(dragEl); + } + + return; + } + + + if ((el.children.length === 0) || (el.children[0] === ghostEl) || + (el === evt.target) && (target = _ghostInBottom(el, evt)) + ) { + if (target) { + if (target.animated) { + return; + } + targetRect = target.getBoundingClientRect(); + } + + _cloneHide(isOwner); + + if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect) !== false) { + el.appendChild(dragEl); + this._animate(dragRect, dragEl); + target && this._animate(targetRect, target); + } + } + else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) { + if (lastEl !== target) { + lastEl = target; + lastCSS = _css(target); + } + + + var targetRect = target.getBoundingClientRect(), + width = targetRect.right - targetRect.left, + height = targetRect.bottom - targetRect.top, + floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display), + isWide = (target.offsetWidth > dragEl.offsetWidth), + isLong = (target.offsetHeight > dragEl.offsetHeight), + halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5, + nextSibling = target.nextElementSibling, + moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect), + after + ; + + if (moveVector !== false) { + _silent = true; + setTimeout(_unsilent, 30); + + _cloneHide(isOwner); + + if (moveVector === 1 || moveVector === -1) { + after = (moveVector === 1); + } + else if (floating) { + after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide; + } else { + after = (nextSibling !== dragEl) && !isLong || halfway && isLong; + } + + if (after && !nextSibling) { + el.appendChild(dragEl); + } else { + target.parentNode.insertBefore(dragEl, after ? nextSibling : target); + } + + this._animate(dragRect, dragEl); + this._animate(targetRect, target); + } + } + } + }, + + _animate: function (prevRect, target) { + var ms = this.options.animation; + + if (ms) { + var currentRect = target.getBoundingClientRect(); + + _css(target, 'transition', 'none'); + _css(target, 'transform', 'translate3d(' + + (prevRect.left - currentRect.left) + 'px,' + + (prevRect.top - currentRect.top) + 'px,0)' + ); + + target.offsetWidth; // repaint + + _css(target, 'transition', 'all ' + ms + 'ms'); + _css(target, 'transform', 'translate3d(0,0,0)'); + + clearTimeout(target.animated); + target.animated = setTimeout(function () { + _css(target, 'transition', ''); + _css(target, 'transform', ''); + target.animated = false; + }, ms); + } + }, + + _offUpEvents: function () { + var ownerDocument = this.el.ownerDocument; + + _off(document, 'touchmove', this._onTouchMove); + _off(ownerDocument, 'mouseup', this._onDrop); + _off(ownerDocument, 'touchend', this._onDrop); + _off(ownerDocument, 'touchcancel', this._onDrop); + }, + + _onDrop: function (/**Event*/evt) { + var el = this.el, + options = this.options; + + clearInterval(this._loopId); + clearInterval(autoScroll.pid); + clearTimeout(this._dragStartTimer); + + // Unbind events + _off(document, 'drop', this); + _off(document, 'mousemove', this._onTouchMove); + _off(el, 'dragstart', this._onDragStart); + + this._offUpEvents(); + + if (evt) { + evt.preventDefault(); + !options.dropBubble && evt.stopPropagation(); + + ghostEl && ghostEl.parentNode.removeChild(ghostEl); + + if (dragEl) { + _off(dragEl, 'dragend', this); + + _disableDraggable(dragEl); + _toggleClass(dragEl, this.options.ghostClass, false); + + if (rootEl !== dragEl.parentNode) { + newIndex = _index(dragEl); + + // drag from one list and drop into another + _dispatchEvent(null, dragEl.parentNode, 'sort', dragEl, rootEl, oldIndex, newIndex); + _dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex); + + // Add event + _dispatchEvent(null, dragEl.parentNode, 'add', dragEl, rootEl, oldIndex, newIndex); + + // Remove event + _dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex); + } + else { + // Remove clone + cloneEl && cloneEl.parentNode.removeChild(cloneEl); + + if (dragEl.nextSibling !== nextEl) { + // Get the index of the dragged element within its parent + newIndex = _index(dragEl); + + // drag & drop within the same list + _dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex); + _dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex); + } + } + + if (Sortable.active) { + // Drag end event + _dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex); + + // Save sorting + this.save(); + } + } + + // Nulling + rootEl = + dragEl = + ghostEl = + nextEl = + cloneEl = + + scrollEl = + scrollParentEl = + + tapEvt = + touchEvt = + + lastEl = + lastCSS = + + activeGroup = + Sortable.active = null; + } + }, + + + handleEvent: function (/**Event*/evt) { + var type = evt.type; + + if (type === 'dragover' || type === 'dragenter') { + if (dragEl) { + this._onDragOver(evt); + _globalDragOver(evt); + } + } + else if (type === 'drop' || type === 'dragend') { + this._onDrop(evt); + } + }, + + + /** + * Serializes the item into an array of string. + * @returns {String[]} + */ + toArray: function () { + var order = [], + el, + children = this.el.children, + i = 0, + n = children.length, + options = this.options; + + for (; i < n; i++) { + el = children[i]; + if (_closest(el, options.draggable, this.el)) { + order.push(el.getAttribute(options.dataIdAttr) || _generateId(el)); + } + } + + return order; + }, + + + /** + * Sorts the elements according to the array. + * @param {String[]} order order of the items + */ + sort: function (order) { + var items = {}, rootEl = this.el; + + this.toArray().forEach(function (id, i) { + var el = rootEl.children[i]; + + if (_closest(el, this.options.draggable, rootEl)) { + items[id] = el; + } + }, this); + + order.forEach(function (id) { + if (items[id]) { + rootEl.removeChild(items[id]); + rootEl.appendChild(items[id]); + } + }); + }, + + + /** + * Save the current sorting + */ + save: function () { + var store = this.options.store; + store && store.set(this); + }, + + + /** + * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. + * @param {HTMLElement} el + * @param {String} [selector] default: `options.draggable` + * @returns {HTMLElement|null} + */ + closest: function (el, selector) { + return _closest(el, selector || this.options.draggable, this.el); + }, + + + /** + * Set/get option + * @param {string} name + * @param {*} [value] + * @returns {*} + */ + option: function (name, value) { + var options = this.options; + + if (value === void 0) { + return options[name]; + } else { + options[name] = value; + } + }, + + + /** + * Destroy + */ + destroy: function () { + var el = this.el; + + el[expando] = null; + + _off(el, 'mousedown', this._onTapStart); + _off(el, 'touchstart', this._onTapStart); + + _off(el, 'dragover', this); + _off(el, 'dragenter', this); + + // Remove draggable attributes + Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) { + el.removeAttribute('draggable'); + }); + + touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1); + + this._onDrop(); + + this.el = el = null; + } + }; + + + function _cloneHide(state) { + if (cloneEl && (cloneEl.state !== state)) { + _css(cloneEl, 'display', state ? 'none' : ''); + !state && cloneEl.state && rootEl.insertBefore(cloneEl, dragEl); + cloneEl.state = state; + } + } + + + function _bind(ctx, fn) { + var args = slice.call(arguments, 2); + return fn.bind ? fn.bind.apply(fn, [ctx].concat(args)) : function () { + return fn.apply(ctx, args.concat(slice.call(arguments))); + }; + } + + + function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) { + if (el) { + ctx = ctx || document; + selector = selector.split('.'); + + var tag = selector.shift().toUpperCase(), + re = new RegExp('\\s(' + selector.join('|') + ')(?=\\s)', 'g'); + + do { + if ( + (tag === '>*' && el.parentNode === ctx) || ( + (tag === '' || el.nodeName.toUpperCase() == tag) && + (!selector.length || ((' ' + el.className + ' ').match(re) || []).length == selector.length) + ) + ) { + return el; + } + } + while (el !== ctx && (el = el.parentNode)); + } + + return null; + } + + + function _globalDragOver(/**Event*/evt) { + evt.dataTransfer.dropEffect = 'move'; + evt.preventDefault(); + } + + + function _on(el, event, fn) { + el.addEventListener(event, fn, false); + } + + + function _off(el, event, fn) { + el.removeEventListener(event, fn, false); + } + + + function _toggleClass(el, name, state) { + if (el) { + if (el.classList) { + el.classList[state ? 'add' : 'remove'](name); + } + else { + var className = (' ' + el.className + ' ').replace(RSPACE, ' ').replace(' ' + name + ' ', ' '); + el.className = (className + (state ? ' ' + name : '')).replace(RSPACE, ' '); + } + } + } + + + function _css(el, prop, val) { + var style = el && el.style; + + if (style) { + if (val === void 0) { + if (document.defaultView && document.defaultView.getComputedStyle) { + val = document.defaultView.getComputedStyle(el, ''); + } + else if (el.currentStyle) { + val = el.currentStyle; + } + + return prop === void 0 ? val : val[prop]; + } + else { + if (!(prop in style)) { + prop = '-webkit-' + prop; + } + + style[prop] = val + (typeof val === 'string' ? '' : 'px'); + } + } + } + + + function _find(ctx, tagName, iterator) { + if (ctx) { + var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length; + + if (iterator) { + for (; i < n; i++) { + iterator(list[i], i); + } + } + + return list; + } + + return []; + } + + + + function _dispatchEvent(sortable, rootEl, name, targetEl, fromEl, startIndex, newIndex) { + var evt = document.createEvent('Event'), + options = (sortable || rootEl[expando]).options, + onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1); + + evt.initEvent(name, true, true); + + evt.to = rootEl; + evt.from = fromEl || rootEl; + evt.item = targetEl || rootEl; + evt.clone = cloneEl; + + evt.oldIndex = startIndex; + evt.newIndex = newIndex; + + rootEl.dispatchEvent(evt); + + if (options[onName]) { + options[onName].call(sortable, evt); + } + } + + + function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect) { + var evt, + sortable = fromEl[expando], + onMoveFn = sortable.options.onMove, + retVal; + + if (onMoveFn) { + evt = document.createEvent('Event'); + evt.initEvent('move', true, true); + + evt.to = toEl; + evt.from = fromEl; + evt.dragged = dragEl; + evt.draggedRect = dragRect; + evt.related = targetEl || toEl; + evt.relatedRect = targetRect || toEl.getBoundingClientRect(); + + retVal = onMoveFn.call(sortable, evt); + } + + return retVal; + } + + + function _disableDraggable(el) { + el.draggable = false; + } + + + function _unsilent() { + _silent = false; + } + + + /** @returns {HTMLElement|false} */ + function _ghostInBottom(el, evt) { + var lastEl = el.lastElementChild, + rect = lastEl.getBoundingClientRect(); + + return (evt.clientY - (rect.top + rect.height) > 5) && lastEl; // min delta + } + + + /** + * Generate id + * @param {HTMLElement} el + * @returns {String} + * @private + */ + function _generateId(el) { + var str = el.tagName + el.className + el.src + el.href + el.textContent, + i = str.length, + sum = 0; + + while (i--) { + sum += str.charCodeAt(i); + } + + return sum.toString(36); + } + + /** + * Returns the index of an element within its parent + * @param el + * @returns {number} + * @private + */ + function _index(/**HTMLElement*/el) { + var index = 0; + while (el && (el = el.previousElementSibling)) { + if (el.nodeName.toUpperCase() !== 'TEMPLATE') { + index++; + } + } + return index; + } + + function _throttle(callback, ms) { + var args, _this; + + return function () { + if (args === void 0) { + args = arguments; + _this = this; + + setTimeout(function () { + if (args.length === 1) { + callback.call(_this, args[0]); + } else { + callback.apply(_this, args); + } + + args = void 0; + }, ms); + } + }; + } + + function _extend(dst, src) { + if (dst && src) { + for (var key in src) { + if (src.hasOwnProperty(key)) { + dst[key] = src[key]; + } + } + } + + return dst; + } + + + // Export utils + Sortable.utils = { + on: _on, + off: _off, + css: _css, + find: _find, + bind: _bind, + is: function (el, selector) { + return !!_closest(el, selector, el); + }, + extend: _extend, + throttle: _throttle, + closest: _closest, + toggleClass: _toggleClass, + index: _index + }; + + + Sortable.version = '1.2.1'; + + + /** + * Create sortable instance + * @param {HTMLElement} el + * @param {Object} [options] + */ + Sortable.create = function (el, options) { + return new Sortable(el, options); + }; + + // Export + return Sortable; +}); diff --git a/Resources/public/js/dropzone.js b/Resources/public/js/dropzone.js new file mode 100644 index 0000000..babbdd4 --- /dev/null +++ b/Resources/public/js/dropzone.js @@ -0,0 +1,1728 @@ + +/* + * + * More info at [www.dropzonejs.com](http://www.dropzonejs.com) + * + * Copyright (c) 2012, Matias Meno + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +(function() { + var Dropzone, Emitter, camelize, contentLoaded, detectVerticalSquash, drawImageIOSFix, noop, without, + __slice = [].slice, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + noop = function() {}; + + Emitter = (function() { + function Emitter() {} + + Emitter.prototype.addEventListener = Emitter.prototype.on; + + Emitter.prototype.on = function(event, fn) { + this._callbacks = this._callbacks || {}; + if (!this._callbacks[event]) { + this._callbacks[event] = []; + } + this._callbacks[event].push(fn); + return this; + }; + + Emitter.prototype.emit = function() { + var args, callback, callbacks, event, _i, _len; + event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + this._callbacks = this._callbacks || {}; + callbacks = this._callbacks[event]; + if (callbacks) { + for (_i = 0, _len = callbacks.length; _i < _len; _i++) { + callback = callbacks[_i]; + callback.apply(this, args); + } + } + return this; + }; + + Emitter.prototype.removeListener = Emitter.prototype.off; + + Emitter.prototype.removeAllListeners = Emitter.prototype.off; + + Emitter.prototype.removeEventListener = Emitter.prototype.off; + + Emitter.prototype.off = function(event, fn) { + var callback, callbacks, i, _i, _len; + if (!this._callbacks || arguments.length === 0) { + this._callbacks = {}; + return this; + } + callbacks = this._callbacks[event]; + if (!callbacks) { + return this; + } + if (arguments.length === 1) { + delete this._callbacks[event]; + return this; + } + for (i = _i = 0, _len = callbacks.length; _i < _len; i = ++_i) { + callback = callbacks[i]; + if (callback === fn) { + callbacks.splice(i, 1); + break; + } + } + return this; + }; + + return Emitter; + + })(); + + Dropzone = (function(_super) { + var extend, resolveOption; + + __extends(Dropzone, _super); + + Dropzone.prototype.Emitter = Emitter; + + + /* + This is a list of all available events you can register on a dropzone object. + + You can register an event handler like this: + + dropzone.on("dragEnter", function() { }); + */ + + Dropzone.prototype.events = ["drop", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "addedfile", "removedfile", "thumbnail", "error", "errormultiple", "processing", "processingmultiple", "uploadprogress", "totaluploadprogress", "sending", "sendingmultiple", "success", "successmultiple", "canceled", "canceledmultiple", "complete", "completemultiple", "reset", "maxfilesexceeded", "maxfilesreached", "queuecomplete"]; + + Dropzone.prototype.defaultOptions = { + url: null, + method: "post", + withCredentials: false, + parallelUploads: 2, + uploadMultiple: false, + maxFilesize: 256, + paramName: "file", + createImageThumbnails: true, + maxThumbnailFilesize: 10, + thumbnailWidth: 120, + thumbnailHeight: 120, + filesizeBase: 1000, + maxFiles: null, + filesizeBase: 1000, + params: {}, + clickable: true, + ignoreHiddenFiles: true, + acceptedFiles: null, + acceptedMimeTypes: null, + autoProcessQueue: true, + autoQueue: true, + addRemoveLinks: false, + previewsContainer: null, + capture: null, + dictDefaultMessage: "Drop files here to upload", + dictFallbackMessage: "Your browser does not support drag'n'drop file uploads.", + dictFallbackText: "Please use the fallback form below to upload your files like in the olden days.", + dictFileTooBig: "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.", + dictInvalidFileType: "You can't upload files of this type.", + dictResponseError: "Server responded with {{statusCode}} code.", + dictCancelUpload: "Cancel upload", + dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?", + dictRemoveFile: "Remove file", + dictRemoveFileConfirmation: null, + dictMaxFilesExceeded: "You can not upload any more files.", + accept: function(file, done) { + return done(); + }, + init: function() { + return noop; + }, + forceFallback: false, + fallback: function() { + var child, messageElement, span, _i, _len, _ref; + this.element.className = "" + this.element.className + " dz-browser-not-supported"; + _ref = this.element.getElementsByTagName("div"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + if (/(^| )dz-message($| )/.test(child.className)) { + messageElement = child; + child.className = "dz-message"; + continue; + } + } + if (!messageElement) { + messageElement = Dropzone.createElement("
"); + this.element.appendChild(messageElement); + } + span = messageElement.getElementsByTagName("span")[0]; + if (span) { + span.textContent = this.options.dictFallbackMessage; + } + return this.element.appendChild(this.getFallbackForm()); + }, + resize: function(file) { + var info, srcRatio, trgRatio; + info = { + srcX: 0, + srcY: 0, + srcWidth: file.width, + srcHeight: file.height + }; + srcRatio = file.width / file.height; + info.optWidth = this.options.thumbnailWidth; + info.optHeight = this.options.thumbnailHeight; + if ((info.optWidth == null) && (info.optHeight == null)) { + info.optWidth = info.srcWidth; + info.optHeight = info.srcHeight; + } else if (info.optWidth == null) { + info.optWidth = srcRatio * info.optHeight; + } else if (info.optHeight == null) { + info.optHeight = (1 / srcRatio) * info.optWidth; + } + trgRatio = info.optWidth / info.optHeight; + if (file.height < info.optHeight || file.width < info.optWidth) { + info.trgHeight = info.srcHeight; + info.trgWidth = info.srcWidth; + } else { + if (srcRatio > trgRatio) { + info.srcHeight = file.height; + info.srcWidth = info.srcHeight * trgRatio; + } else { + info.srcWidth = file.width; + info.srcHeight = info.srcWidth / trgRatio; + } + } + info.srcX = (file.width - info.srcWidth) / 2; + info.srcY = (file.height - info.srcHeight) / 2; + return info; + }, + + /* + Those functions register themselves to the events on init and handle all + the user interface specific stuff. Overwriting them won't break the upload + but can break the way it's displayed. + You can overwrite them if you don't like the default behavior. If you just + want to add an additional event handler, register it on the dropzone object + and don't overwrite those options. + */ + drop: function(e) { + return this.element.classList.remove("dz-drag-hover"); + }, + dragstart: noop, + dragend: function(e) { + return this.element.classList.remove("dz-drag-hover"); + }, + dragenter: function(e) { + return this.element.classList.add("dz-drag-hover"); + }, + dragover: function(e) { + return this.element.classList.add("dz-drag-hover"); + }, + dragleave: function(e) { + return this.element.classList.remove("dz-drag-hover"); + }, + paste: noop, + reset: function() { + return this.element.classList.remove("dz-started"); + }, + addedfile: function(file) { + var node, removeFileEvent, removeLink, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _results; + if (this.element === this.previewsContainer) { + this.element.classList.add("dz-started"); + } + if (this.previewsContainer) { + file.previewElement = Dropzone.createElement(this.options.previewTemplate.trim()); + file.previewTemplate = file.previewElement; + this.previewsContainer.appendChild(file.previewElement); + _ref = file.previewElement.querySelectorAll("[data-dz-name]"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + node.textContent = file.name; + } + _ref1 = file.previewElement.querySelectorAll("[data-dz-size]"); + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + node = _ref1[_j]; + node.innerHTML = this.filesize(file.size); + } + if (this.options.addRemoveLinks) { + file._removeLink = Dropzone.createElement("" + this.options.dictRemoveFile + ""); + file.previewElement.appendChild(file._removeLink); + } + removeFileEvent = (function(_this) { + return function(e) { + e.preventDefault(); + e.stopPropagation(); + if (file.status === Dropzone.UPLOADING) { + return Dropzone.confirm(_this.options.dictCancelUploadConfirmation, function() { + return _this.removeFile(file); + }); + } else { + if (_this.options.dictRemoveFileConfirmation) { + return Dropzone.confirm(_this.options.dictRemoveFileConfirmation, function() { + return _this.removeFile(file); + }); + } else { + return _this.removeFile(file); + } + } + }; + })(this); + _ref2 = file.previewElement.querySelectorAll("[data-dz-remove]"); + _results = []; + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + removeLink = _ref2[_k]; + _results.push(removeLink.addEventListener("click", removeFileEvent)); + } + return _results; + } + }, + removedfile: function(file) { + var _ref; + if (file.previewElement) { + if ((_ref = file.previewElement) != null) { + _ref.parentNode.removeChild(file.previewElement); + } + } + return this._updateMaxFilesReachedClass(); + }, + thumbnail: function(file, dataUrl) { + var thumbnailElement, _i, _len, _ref; + if (file.previewElement) { + file.previewElement.classList.remove("dz-file-preview"); + _ref = file.previewElement.querySelectorAll("[data-dz-thumbnail]"); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + thumbnailElement = _ref[_i]; + thumbnailElement.alt = file.name; + thumbnailElement.src = dataUrl; + } + return setTimeout(((function(_this) { + return function() { + return file.previewElement.classList.add("dz-image-preview"); + }; + })(this)), 1); + } + }, + error: function(file, message) { + var node, _i, _len, _ref, _results; + if (file.previewElement) { + file.previewElement.classList.add("dz-error"); + if (typeof message !== "String" && message.error) { + message = message.error; + } + _ref = file.previewElement.querySelectorAll("[data-dz-errormessage]"); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + _results.push(node.textContent = message); + } + return _results; + } + }, + errormultiple: noop, + processing: function(file) { + if (file.previewElement) { + file.previewElement.classList.add("dz-processing"); + if (file._removeLink) { + return file._removeLink.textContent = this.options.dictCancelUpload; + } + } + }, + processingmultiple: noop, + uploadprogress: function(file, progress, bytesSent) { + var node, _i, _len, _ref, _results; + if (file.previewElement) { + _ref = file.previewElement.querySelectorAll("[data-dz-uploadprogress]"); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + node = _ref[_i]; + if (node.nodeName === 'PROGRESS') { + _results.push(node.value = progress); + } else { + _results.push(node.style.width = "" + progress + "%"); + } + } + return _results; + } + }, + totaluploadprogress: noop, + sending: noop, + sendingmultiple: noop, + success: function(file) { + if (file.previewElement) { + return file.previewElement.classList.add("dz-success"); + } + }, + successmultiple: noop, + canceled: function(file) { + return this.emit("error", file, "Upload canceled."); + }, + canceledmultiple: noop, + complete: function(file) { + if (file._removeLink) { + file._removeLink.textContent = this.options.dictRemoveFile; + } + if (file.previewElement) { + return file.previewElement.classList.add("dz-complete"); + } + }, + completemultiple: noop, + maxfilesexceeded: noop, + maxfilesreached: noop, + queuecomplete: noop, + previewTemplate: "
\n
\n
\n
\n
\n
\n
\n
\n
\n \n Check\n \n \n \n \n \n
\n
\n \n Error\n \n \n \n \n \n \n \n
\n
" + }; + + extend = function() { + var key, object, objects, target, val, _i, _len; + target = arguments[0], objects = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + for (_i = 0, _len = objects.length; _i < _len; _i++) { + object = objects[_i]; + for (key in object) { + val = object[key]; + target[key] = val; + } + } + return target; + }; + + function Dropzone(element, options) { + var elementOptions, fallback, _ref; + this.element = element; + this.version = Dropzone.version; + this.defaultOptions.previewTemplate = this.defaultOptions.previewTemplate.replace(/\n*/g, ""); + this.clickableElements = []; + this.listeners = []; + this.files = []; + if (typeof this.element === "string") { + this.element = document.querySelector(this.element); + } + if (!(this.element && (this.element.nodeType != null))) { + throw new Error("Invalid dropzone element."); + } + if (this.element.dropzone) { + throw new Error("Dropzone already attached."); + } + Dropzone.instances.push(this); + this.element.dropzone = this; + elementOptions = (_ref = Dropzone.optionsForElement(this.element)) != null ? _ref : {}; + this.options = extend({}, this.defaultOptions, elementOptions, options != null ? options : {}); + if (this.options.forceFallback || !Dropzone.isBrowserSupported()) { + return this.options.fallback.call(this); + } + if (this.options.url == null) { + this.options.url = this.element.getAttribute("action"); + } + if (!this.options.url) { + throw new Error("No URL provided."); + } + if (this.options.acceptedFiles && this.options.acceptedMimeTypes) { + throw new Error("You can't provide both 'acceptedFiles' and 'acceptedMimeTypes'. 'acceptedMimeTypes' is deprecated."); + } + if (this.options.acceptedMimeTypes) { + this.options.acceptedFiles = this.options.acceptedMimeTypes; + delete this.options.acceptedMimeTypes; + } + this.options.method = this.options.method.toUpperCase(); + if ((fallback = this.getExistingFallback()) && fallback.parentNode) { + fallback.parentNode.removeChild(fallback); + } + if (this.options.previewsContainer !== false) { + if (this.options.previewsContainer) { + this.previewsContainer = Dropzone.getElement(this.options.previewsContainer, "previewsContainer"); + } else { + this.previewsContainer = this.element; + } + } + if (this.options.clickable) { + if (this.options.clickable === true) { + this.clickableElements = [this.element]; + } else { + this.clickableElements = Dropzone.getElements(this.options.clickable, "clickable"); + } + } + this.init(); + } + + Dropzone.prototype.getAcceptedFiles = function() { + var file, _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.accepted) { + _results.push(file); + } + } + return _results; + }; + + Dropzone.prototype.getRejectedFiles = function() { + var file, _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (!file.accepted) { + _results.push(file); + } + } + return _results; + }; + + Dropzone.prototype.getFilesWithStatus = function(status) { + var file, _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.status === status) { + _results.push(file); + } + } + return _results; + }; + + Dropzone.prototype.getQueuedFiles = function() { + return this.getFilesWithStatus(Dropzone.QUEUED); + }; + + Dropzone.prototype.getUploadingFiles = function() { + return this.getFilesWithStatus(Dropzone.UPLOADING); + }; + + Dropzone.prototype.getActiveFiles = function() { + var file, _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.status === Dropzone.UPLOADING || file.status === Dropzone.QUEUED) { + _results.push(file); + } + } + return _results; + }; + + Dropzone.prototype.init = function() { + var eventName, noPropagation, setupHiddenFileInput, _i, _len, _ref, _ref1; + if (this.element.tagName === "form") { + this.element.setAttribute("enctype", "multipart/form-data"); + } + if (this.element.classList.contains("dropzone") && !this.element.querySelector(".dz-message")) { + this.element.appendChild(Dropzone.createElement("
" + this.options.dictDefaultMessage + "
")); + } + if (this.clickableElements.length) { + setupHiddenFileInput = (function(_this) { + return function() { + if (_this.hiddenFileInput) { + document.body.removeChild(_this.hiddenFileInput); + } + _this.hiddenFileInput = document.createElement("input"); + _this.hiddenFileInput.setAttribute("type", "file"); + if ((_this.options.maxFiles == null) || _this.options.maxFiles > 1) { + _this.hiddenFileInput.setAttribute("multiple", "multiple"); + } + _this.hiddenFileInput.className = "dz-hidden-input"; + if (_this.options.acceptedFiles != null) { + _this.hiddenFileInput.setAttribute("accept", _this.options.acceptedFiles); + } + if (_this.options.capture != null) { + _this.hiddenFileInput.setAttribute("capture", _this.options.capture); + } + _this.hiddenFileInput.style.visibility = "hidden"; + _this.hiddenFileInput.style.position = "absolute"; + _this.hiddenFileInput.style.top = "0"; + _this.hiddenFileInput.style.left = "0"; + _this.hiddenFileInput.style.height = "0"; + _this.hiddenFileInput.style.width = "0"; + document.body.appendChild(_this.hiddenFileInput); + return _this.hiddenFileInput.addEventListener("change", function() { + var file, files, _i, _len; + files = _this.hiddenFileInput.files; + if (files.length) { + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + _this.addFile(file); + } + } + return setupHiddenFileInput(); + }); + }; + })(this); + setupHiddenFileInput(); + } + this.URL = (_ref = window.URL) != null ? _ref : window.webkitURL; + _ref1 = this.events; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + eventName = _ref1[_i]; + this.on(eventName, this.options[eventName]); + } + this.on("uploadprogress", (function(_this) { + return function() { + return _this.updateTotalUploadProgress(); + }; + })(this)); + this.on("removedfile", (function(_this) { + return function() { + return _this.updateTotalUploadProgress(); + }; + })(this)); + this.on("canceled", (function(_this) { + return function(file) { + return _this.emit("complete", file); + }; + })(this)); + this.on("complete", (function(_this) { + return function(file) { + if (_this.getUploadingFiles().length === 0 && _this.getQueuedFiles().length === 0) { + return setTimeout((function() { + return _this.emit("queuecomplete"); + }), 0); + } + }; + })(this)); + noPropagation = function(e) { + e.stopPropagation(); + if (e.preventDefault) { + return e.preventDefault(); + } else { + return e.returnValue = false; + } + }; + this.listeners = [ + { + element: this.element, + events: { + "dragstart": (function(_this) { + return function(e) { + return _this.emit("dragstart", e); + }; + })(this), + "dragenter": (function(_this) { + return function(e) { + noPropagation(e); + return _this.emit("dragenter", e); + }; + })(this), + "dragover": (function(_this) { + return function(e) { + var efct; + try { + efct = e.dataTransfer.effectAllowed; + } catch (_error) {} + e.dataTransfer.dropEffect = 'move' === efct || 'linkMove' === efct ? 'move' : 'copy'; + noPropagation(e); + return _this.emit("dragover", e); + }; + })(this), + "dragleave": (function(_this) { + return function(e) { + return _this.emit("dragleave", e); + }; + })(this), + "drop": (function(_this) { + return function(e) { + noPropagation(e); + return _this.drop(e); + }; + })(this), + "dragend": (function(_this) { + return function(e) { + return _this.emit("dragend", e); + }; + })(this) + } + } + ]; + this.clickableElements.forEach((function(_this) { + return function(clickableElement) { + return _this.listeners.push({ + element: clickableElement, + events: { + "click": function(evt) { + if ((clickableElement !== _this.element) || (evt.target === _this.element || Dropzone.elementInside(evt.target, _this.element.querySelector(".dz-message")))) { + return _this.hiddenFileInput.click(); + } + } + } + }); + }; + })(this)); + this.enable(); + return this.options.init.call(this); + }; + + Dropzone.prototype.destroy = function() { + var _ref; + this.disable(); + this.removeAllFiles(true); + if ((_ref = this.hiddenFileInput) != null ? _ref.parentNode : void 0) { + this.hiddenFileInput.parentNode.removeChild(this.hiddenFileInput); + this.hiddenFileInput = null; + } + delete this.element.dropzone; + return Dropzone.instances.splice(Dropzone.instances.indexOf(this), 1); + }; + + Dropzone.prototype.updateTotalUploadProgress = function() { + var activeFiles, file, totalBytes, totalBytesSent, totalUploadProgress, _i, _len, _ref; + totalBytesSent = 0; + totalBytes = 0; + activeFiles = this.getActiveFiles(); + if (activeFiles.length) { + _ref = this.getActiveFiles(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + totalBytesSent += file.upload.bytesSent; + totalBytes += file.upload.total; + } + totalUploadProgress = 100 * totalBytesSent / totalBytes; + } else { + totalUploadProgress = 100; + } + return this.emit("totaluploadprogress", totalUploadProgress, totalBytes, totalBytesSent); + }; + + Dropzone.prototype._getParamName = function(n) { + if (typeof this.options.paramName === "function") { + return this.options.paramName(n); + } else { + return "" + this.options.paramName + (this.options.uploadMultiple ? "[" + n + "]" : ""); + } + }; + + Dropzone.prototype.getFallbackForm = function() { + var existingFallback, fields, fieldsString, form; + if (existingFallback = this.getExistingFallback()) { + return existingFallback; + } + fieldsString = "
"; + if (this.options.dictFallbackText) { + fieldsString += "

" + this.options.dictFallbackText + "

"; + } + fieldsString += "
"; + fields = Dropzone.createElement(fieldsString); + if (this.element.tagName !== "FORM") { + form = Dropzone.createElement("
"); + form.appendChild(fields); + } else { + this.element.setAttribute("enctype", "multipart/form-data"); + this.element.setAttribute("method", this.options.method); + } + return form != null ? form : fields; + }; + + Dropzone.prototype.getExistingFallback = function() { + var fallback, getFallback, tagName, _i, _len, _ref; + getFallback = function(elements) { + var el, _i, _len; + for (_i = 0, _len = elements.length; _i < _len; _i++) { + el = elements[_i]; + if (/(^| )fallback($| )/.test(el.className)) { + return el; + } + } + }; + _ref = ["div", "form"]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + tagName = _ref[_i]; + if (fallback = getFallback(this.element.getElementsByTagName(tagName))) { + return fallback; + } + } + }; + + Dropzone.prototype.setupEventListeners = function() { + var elementListeners, event, listener, _i, _len, _ref, _results; + _ref = this.listeners; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + elementListeners = _ref[_i]; + _results.push((function() { + var _ref1, _results1; + _ref1 = elementListeners.events; + _results1 = []; + for (event in _ref1) { + listener = _ref1[event]; + _results1.push(elementListeners.element.addEventListener(event, listener, false)); + } + return _results1; + })()); + } + return _results; + }; + + Dropzone.prototype.removeEventListeners = function() { + var elementListeners, event, listener, _i, _len, _ref, _results; + _ref = this.listeners; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + elementListeners = _ref[_i]; + _results.push((function() { + var _ref1, _results1; + _ref1 = elementListeners.events; + _results1 = []; + for (event in _ref1) { + listener = _ref1[event]; + _results1.push(elementListeners.element.removeEventListener(event, listener, false)); + } + return _results1; + })()); + } + return _results; + }; + + Dropzone.prototype.disable = function() { + var file, _i, _len, _ref, _results; + this.clickableElements.forEach(function(element) { + return element.classList.remove("dz-clickable"); + }); + this.removeEventListeners(); + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + _results.push(this.cancelUpload(file)); + } + return _results; + }; + + Dropzone.prototype.enable = function() { + this.clickableElements.forEach(function(element) { + return element.classList.add("dz-clickable"); + }); + return this.setupEventListeners(); + }; + + Dropzone.prototype.filesize = function(size) { + var cutoff, i, selectedSize, selectedUnit, unit, units, _i, _len; + units = ['TB', 'GB', 'MB', 'KB', 'b']; + selectedSize = selectedUnit = null; + for (i = _i = 0, _len = units.length; _i < _len; i = ++_i) { + unit = units[i]; + cutoff = Math.pow(this.options.filesizeBase, 4 - i) / 10; + if (size >= cutoff) { + selectedSize = size / Math.pow(this.options.filesizeBase, 4 - i); + selectedUnit = unit; + break; + } + } + selectedSize = Math.round(10 * selectedSize) / 10; + return "" + selectedSize + " " + selectedUnit; + }; + + Dropzone.prototype._updateMaxFilesReachedClass = function() { + if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) { + if (this.getAcceptedFiles().length === this.options.maxFiles) { + this.emit('maxfilesreached', this.files); + } + return this.element.classList.add("dz-max-files-reached"); + } else { + return this.element.classList.remove("dz-max-files-reached"); + } + }; + + Dropzone.prototype.drop = function(e) { + var files, items; + if (!e.dataTransfer) { + return; + } + this.emit("drop", e); + files = e.dataTransfer.files; + if (files.length) { + items = e.dataTransfer.items; + if (items && items.length && (items[0].webkitGetAsEntry != null)) { + this._addFilesFromItems(items); + } else { + this.handleFiles(files); + } + } + }; + + Dropzone.prototype.paste = function(e) { + var items, _ref; + if ((e != null ? (_ref = e.clipboardData) != null ? _ref.items : void 0 : void 0) == null) { + return; + } + this.emit("paste", e); + items = e.clipboardData.items; + if (items.length) { + return this._addFilesFromItems(items); + } + }; + + Dropzone.prototype.handleFiles = function(files) { + var file, _i, _len, _results; + _results = []; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + _results.push(this.addFile(file)); + } + return _results; + }; + + Dropzone.prototype._addFilesFromItems = function(items) { + var entry, item, _i, _len, _results; + _results = []; + for (_i = 0, _len = items.length; _i < _len; _i++) { + item = items[_i]; + if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) { + if (entry.isFile) { + _results.push(this.addFile(item.getAsFile())); + } else if (entry.isDirectory) { + _results.push(this._addFilesFromDirectory(entry, entry.name)); + } else { + _results.push(void 0); + } + } else if (item.getAsFile != null) { + if ((item.kind == null) || item.kind === "file") { + _results.push(this.addFile(item.getAsFile())); + } else { + _results.push(void 0); + } + } else { + _results.push(void 0); + } + } + return _results; + }; + + Dropzone.prototype._addFilesFromDirectory = function(directory, path) { + var dirReader, entriesReader; + dirReader = directory.createReader(); + entriesReader = (function(_this) { + return function(entries) { + var entry, _i, _len; + for (_i = 0, _len = entries.length; _i < _len; _i++) { + entry = entries[_i]; + if (entry.isFile) { + entry.file(function(file) { + if (_this.options.ignoreHiddenFiles && file.name.substring(0, 1) === '.') { + return; + } + file.fullPath = "" + path + "/" + file.name; + return _this.addFile(file); + }); + } else if (entry.isDirectory) { + _this._addFilesFromDirectory(entry, "" + path + "/" + entry.name); + } + } + }; + })(this); + return dirReader.readEntries(entriesReader, function(error) { + return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(error) : void 0 : void 0; + }); + }; + + Dropzone.prototype.accept = function(file, done) { + if (file.size > this.options.maxFilesize * 1024 * 1024) { + return done(this.options.dictFileTooBig.replace("{{filesize}}", Math.round(file.size / 1024 / 10.24) / 100).replace("{{maxFilesize}}", this.options.maxFilesize)); + } else if (!Dropzone.isValidFile(file, this.options.acceptedFiles)) { + return done(this.options.dictInvalidFileType); + } else if ((this.options.maxFiles != null) && this.getAcceptedFiles().length >= this.options.maxFiles) { + done(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}", this.options.maxFiles)); + return this.emit("maxfilesexceeded", file); + } else { + return this.options.accept.call(this, file, done); + } + }; + + Dropzone.prototype.addFile = function(file) { + file.upload = { + progress: 0, + total: file.size, + bytesSent: 0 + }; + this.files.push(file); + file.status = Dropzone.ADDED; + this.emit("addedfile", file); + this._enqueueThumbnail(file); + return this.accept(file, (function(_this) { + return function(error) { + if (error) { + file.accepted = false; + _this._errorProcessing([file], error); + } else { + file.accepted = true; + if (_this.options.autoQueue) { + _this.enqueueFile(file); + } + } + return _this._updateMaxFilesReachedClass(); + }; + })(this)); + }; + + Dropzone.prototype.enqueueFiles = function(files) { + var file, _i, _len; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + this.enqueueFile(file); + } + return null; + }; + + Dropzone.prototype.enqueueFile = function(file) { + if (file.status === Dropzone.ADDED && file.accepted === true) { + file.status = Dropzone.QUEUED; + if (this.options.autoProcessQueue) { + return setTimeout(((function(_this) { + return function() { + return _this.processQueue(); + }; + })(this)), 0); + } + } else { + throw new Error("This file can't be queued because it has already been processed or was rejected."); + } + }; + + Dropzone.prototype._thumbnailQueue = []; + + Dropzone.prototype._processingThumbnail = false; + + Dropzone.prototype._enqueueThumbnail = function(file) { + if (this.options.createImageThumbnails && file.type.match(/image.*/) && file.size <= this.options.maxThumbnailFilesize * 1024 * 1024) { + this._thumbnailQueue.push(file); + return setTimeout(((function(_this) { + return function() { + return _this._processThumbnailQueue(); + }; + })(this)), 0); + } + }; + + Dropzone.prototype._processThumbnailQueue = function() { + if (this._processingThumbnail || this._thumbnailQueue.length === 0) { + return; + } + this._processingThumbnail = true; + return this.createThumbnail(this._thumbnailQueue.shift(), (function(_this) { + return function() { + _this._processingThumbnail = false; + return _this._processThumbnailQueue(); + }; + })(this)); + }; + + Dropzone.prototype.removeFile = function(file) { + if (file.status === Dropzone.UPLOADING) { + this.cancelUpload(file); + } + this.files = without(this.files, file); + this.emit("removedfile", file); + if (this.files.length === 0) { + return this.emit("reset"); + } + }; + + Dropzone.prototype.removeAllFiles = function(cancelIfNecessary) { + var file, _i, _len, _ref; + if (cancelIfNecessary == null) { + cancelIfNecessary = false; + } + _ref = this.files.slice(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.status !== Dropzone.UPLOADING || cancelIfNecessary) { + this.removeFile(file); + } + } + return null; + }; + + Dropzone.prototype.createThumbnail = function(file, callback) { + var fileReader; + fileReader = new FileReader; + fileReader.onload = (function(_this) { + return function() { + if (file.type === "image/svg+xml") { + _this.emit("thumbnail", file, fileReader.result); + if (callback != null) { + callback(); + } + return; + } + return _this.createThumbnailFromUrl(file, fileReader.result, callback); + }; + })(this); + return fileReader.readAsDataURL(file); + }; + + Dropzone.prototype.createThumbnailFromUrl = function(file, imageUrl, callback) { + var img; + img = document.createElement("img"); + img.onload = (function(_this) { + return function() { + var canvas, ctx, resizeInfo, thumbnail, _ref, _ref1, _ref2, _ref3; + file.width = img.width; + file.height = img.height; + resizeInfo = _this.options.resize.call(_this, file); + if (resizeInfo.trgWidth == null) { + resizeInfo.trgWidth = resizeInfo.optWidth; + } + if (resizeInfo.trgHeight == null) { + resizeInfo.trgHeight = resizeInfo.optHeight; + } + canvas = document.createElement("canvas"); + ctx = canvas.getContext("2d"); + canvas.width = resizeInfo.trgWidth; + canvas.height = resizeInfo.trgHeight; + drawImageIOSFix(ctx, img, (_ref = resizeInfo.srcX) != null ? _ref : 0, (_ref1 = resizeInfo.srcY) != null ? _ref1 : 0, resizeInfo.srcWidth, resizeInfo.srcHeight, (_ref2 = resizeInfo.trgX) != null ? _ref2 : 0, (_ref3 = resizeInfo.trgY) != null ? _ref3 : 0, resizeInfo.trgWidth, resizeInfo.trgHeight); + thumbnail = canvas.toDataURL("image/png"); + _this.emit("thumbnail", file, thumbnail); + if (callback != null) { + return callback(); + } + }; + })(this); + if (callback != null) { + img.onerror = callback; + } + return img.src = imageUrl; + }; + + Dropzone.prototype.processQueue = function() { + var i, parallelUploads, processingLength, queuedFiles; + parallelUploads = this.options.parallelUploads; + processingLength = this.getUploadingFiles().length; + i = processingLength; + if (processingLength >= parallelUploads) { + return; + } + queuedFiles = this.getQueuedFiles(); + if (!(queuedFiles.length > 0)) { + return; + } + if (this.options.uploadMultiple) { + return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength)); + } else { + while (i < parallelUploads) { + if (!queuedFiles.length) { + return; + } + this.processFile(queuedFiles.shift()); + i++; + } + } + }; + + Dropzone.prototype.processFile = function(file) { + return this.processFiles([file]); + }; + + Dropzone.prototype.processFiles = function(files) { + var file, _i, _len; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + file.processing = true; + file.status = Dropzone.UPLOADING; + this.emit("processing", file); + } + if (this.options.uploadMultiple) { + this.emit("processingmultiple", files); + } + return this.uploadFiles(files); + }; + + Dropzone.prototype._getFilesWithXhr = function(xhr) { + var file, files; + return files = (function() { + var _i, _len, _ref, _results; + _ref = this.files; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + file = _ref[_i]; + if (file.xhr === xhr) { + _results.push(file); + } + } + return _results; + }).call(this); + }; + + Dropzone.prototype.cancelUpload = function(file) { + var groupedFile, groupedFiles, _i, _j, _len, _len1, _ref; + if (file.status === Dropzone.UPLOADING) { + groupedFiles = this._getFilesWithXhr(file.xhr); + for (_i = 0, _len = groupedFiles.length; _i < _len; _i++) { + groupedFile = groupedFiles[_i]; + groupedFile.status = Dropzone.CANCELED; + } + file.xhr.abort(); + for (_j = 0, _len1 = groupedFiles.length; _j < _len1; _j++) { + groupedFile = groupedFiles[_j]; + this.emit("canceled", groupedFile); + } + if (this.options.uploadMultiple) { + this.emit("canceledmultiple", groupedFiles); + } + } else if ((_ref = file.status) === Dropzone.ADDED || _ref === Dropzone.QUEUED) { + file.status = Dropzone.CANCELED; + this.emit("canceled", file); + if (this.options.uploadMultiple) { + this.emit("canceledmultiple", [file]); + } + } + if (this.options.autoProcessQueue) { + return this.processQueue(); + } + }; + + resolveOption = function() { + var args, option; + option = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + if (typeof option === 'function') { + return option.apply(this, args); + } + return option; + }; + + Dropzone.prototype.uploadFile = function(file) { + return this.uploadFiles([file]); + }; + + Dropzone.prototype.uploadFiles = function(files) { + var file, formData, handleError, headerName, headerValue, headers, i, input, inputName, inputType, key, method, option, progressObj, response, updateProgress, url, value, xhr, _i, _j, _k, _l, _len, _len1, _len2, _len3, _m, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; + xhr = new XMLHttpRequest(); + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + file.xhr = xhr; + } + method = resolveOption(this.options.method, files); + url = resolveOption(this.options.url, files); + xhr.open(method, url, true); + xhr.withCredentials = !!this.options.withCredentials; + response = null; + handleError = (function(_this) { + return function() { + var _j, _len1, _results; + _results = []; + for (_j = 0, _len1 = files.length; _j < _len1; _j++) { + file = files[_j]; + _results.push(_this._errorProcessing(files, response || _this.options.dictResponseError.replace("{{statusCode}}", xhr.status), xhr)); + } + return _results; + }; + })(this); + updateProgress = (function(_this) { + return function(e) { + var allFilesFinished, progress, _j, _k, _l, _len1, _len2, _len3, _results; + if (e != null) { + progress = 100 * e.loaded / e.total; + for (_j = 0, _len1 = files.length; _j < _len1; _j++) { + file = files[_j]; + file.upload = { + progress: progress, + total: e.total, + bytesSent: e.loaded + }; + } + } else { + allFilesFinished = true; + progress = 100; + for (_k = 0, _len2 = files.length; _k < _len2; _k++) { + file = files[_k]; + if (!(file.upload.progress === 100 && file.upload.bytesSent === file.upload.total)) { + allFilesFinished = false; + } + file.upload.progress = progress; + file.upload.bytesSent = file.upload.total; + } + if (allFilesFinished) { + return; + } + } + _results = []; + for (_l = 0, _len3 = files.length; _l < _len3; _l++) { + file = files[_l]; + _results.push(_this.emit("uploadprogress", file, progress, file.upload.bytesSent)); + } + return _results; + }; + })(this); + xhr.onload = (function(_this) { + return function(e) { + var _ref; + if (files[0].status === Dropzone.CANCELED) { + return; + } + if (xhr.readyState !== 4) { + return; + } + response = xhr.responseText; + if (xhr.getResponseHeader("content-type") && ~xhr.getResponseHeader("content-type").indexOf("application/json")) { + try { + response = JSON.parse(response); + } catch (_error) { + e = _error; + response = "Invalid JSON response from server."; + } + } + updateProgress(); + if (!((200 <= (_ref = xhr.status) && _ref < 300))) { + return handleError(); + } else { + return _this._finished(files, response, e); + } + }; + })(this); + xhr.onerror = (function(_this) { + return function() { + if (files[0].status === Dropzone.CANCELED) { + return; + } + return handleError(); + }; + })(this); + progressObj = (_ref = xhr.upload) != null ? _ref : xhr; + progressObj.onprogress = updateProgress; + headers = { + "Accept": "application/json", + "Cache-Control": "no-cache", + "X-Requested-With": "XMLHttpRequest" + }; + if (this.options.headers) { + extend(headers, this.options.headers); + } + for (headerName in headers) { + headerValue = headers[headerName]; + xhr.setRequestHeader(headerName, headerValue); + } + formData = new FormData(); + if (this.options.params) { + _ref1 = this.options.params; + for (key in _ref1) { + value = _ref1[key]; + formData.append(key, value); + } + } + for (_j = 0, _len1 = files.length; _j < _len1; _j++) { + file = files[_j]; + this.emit("sending", file, xhr, formData); + } + if (this.options.uploadMultiple) { + this.emit("sendingmultiple", files, xhr, formData); + } + if (this.element.tagName === "FORM") { + _ref2 = this.element.querySelectorAll("input, textarea, select, button"); + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + input = _ref2[_k]; + inputName = input.getAttribute("name"); + inputType = input.getAttribute("type"); + if (input.tagName === "SELECT" && input.hasAttribute("multiple")) { + _ref3 = input.options; + for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) { + option = _ref3[_l]; + if (option.selected) { + formData.append(inputName, option.value); + } + } + } else if (!inputType || ((_ref4 = inputType.toLowerCase()) !== "checkbox" && _ref4 !== "radio") || input.checked) { + formData.append(inputName, input.value); + } + } + } + for (i = _m = 0, _ref5 = files.length - 1; 0 <= _ref5 ? _m <= _ref5 : _m >= _ref5; i = 0 <= _ref5 ? ++_m : --_m) { + formData.append(this._getParamName(i), files[i], files[i].name); + } + return xhr.send(formData); + }; + + Dropzone.prototype._finished = function(files, responseText, e) { + var file, _i, _len; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + file.status = Dropzone.SUCCESS; + this.emit("success", file, responseText, e); + this.emit("complete", file); + } + if (this.options.uploadMultiple) { + this.emit("successmultiple", files, responseText, e); + this.emit("completemultiple", files); + } + if (this.options.autoProcessQueue) { + return this.processQueue(); + } + }; + + Dropzone.prototype._errorProcessing = function(files, message, xhr) { + var file, _i, _len; + for (_i = 0, _len = files.length; _i < _len; _i++) { + file = files[_i]; + file.status = Dropzone.ERROR; + this.emit("error", file, message, xhr); + this.emit("complete", file); + } + if (this.options.uploadMultiple) { + this.emit("errormultiple", files, message, xhr); + this.emit("completemultiple", files); + } + if (this.options.autoProcessQueue) { + return this.processQueue(); + } + }; + + return Dropzone; + + })(Emitter); + + Dropzone.version = "4.0.1"; + + Dropzone.options = {}; + + Dropzone.optionsForElement = function(element) { + if (element.getAttribute("id")) { + return Dropzone.options[camelize(element.getAttribute("id"))]; + } else { + return void 0; + } + }; + + Dropzone.instances = []; + + Dropzone.forElement = function(element) { + if (typeof element === "string") { + element = document.querySelector(element); + } + if ((element != null ? element.dropzone : void 0) == null) { + throw new Error("No Dropzone found for given element. This is probably because you're trying to access it before Dropzone had the time to initialize. Use the `init` option to setup any additional observers on your Dropzone."); + } + return element.dropzone; + }; + + Dropzone.autoDiscover = true; + + Dropzone.discover = function() { + var checkElements, dropzone, dropzones, _i, _len, _results; + if (document.querySelectorAll) { + dropzones = document.querySelectorAll(".dropzone"); + } else { + dropzones = []; + checkElements = function(elements) { + var el, _i, _len, _results; + _results = []; + for (_i = 0, _len = elements.length; _i < _len; _i++) { + el = elements[_i]; + if (/(^| )dropzone($| )/.test(el.className)) { + _results.push(dropzones.push(el)); + } else { + _results.push(void 0); + } + } + return _results; + }; + checkElements(document.getElementsByTagName("div")); + checkElements(document.getElementsByTagName("form")); + } + _results = []; + for (_i = 0, _len = dropzones.length; _i < _len; _i++) { + dropzone = dropzones[_i]; + if (Dropzone.optionsForElement(dropzone) !== false) { + _results.push(new Dropzone(dropzone)); + } else { + _results.push(void 0); + } + } + return _results; + }; + + Dropzone.blacklistedBrowsers = [/opera.*Macintosh.*version\/12/i]; + + Dropzone.isBrowserSupported = function() { + var capableBrowser, regex, _i, _len, _ref; + capableBrowser = true; + if (window.File && window.FileReader && window.FileList && window.Blob && window.FormData && document.querySelector) { + if (!("classList" in document.createElement("a"))) { + capableBrowser = false; + } else { + _ref = Dropzone.blacklistedBrowsers; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + regex = _ref[_i]; + if (regex.test(navigator.userAgent)) { + capableBrowser = false; + continue; + } + } + } + } else { + capableBrowser = false; + } + return capableBrowser; + }; + + without = function(list, rejectedItem) { + var item, _i, _len, _results; + _results = []; + for (_i = 0, _len = list.length; _i < _len; _i++) { + item = list[_i]; + if (item !== rejectedItem) { + _results.push(item); + } + } + return _results; + }; + + camelize = function(str) { + return str.replace(/[\-_](\w)/g, function(match) { + return match.charAt(1).toUpperCase(); + }); + }; + + Dropzone.createElement = function(string) { + var div; + div = document.createElement("div"); + div.innerHTML = string; + return div.childNodes[0]; + }; + + Dropzone.elementInside = function(element, container) { + if (element === container) { + return true; + } + while (element = element.parentNode) { + if (element === container) { + return true; + } + } + return false; + }; + + Dropzone.getElement = function(el, name) { + var element; + if (typeof el === "string") { + element = document.querySelector(el); + } else if (el.nodeType != null) { + element = el; + } + if (element == null) { + throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector or a plain HTML element."); + } + return element; + }; + + Dropzone.getElements = function(els, name) { + var e, el, elements, _i, _j, _len, _len1, _ref; + if (els instanceof Array) { + elements = []; + try { + for (_i = 0, _len = els.length; _i < _len; _i++) { + el = els[_i]; + elements.push(this.getElement(el, name)); + } + } catch (_error) { + e = _error; + elements = null; + } + } else if (typeof els === "string") { + elements = []; + _ref = document.querySelectorAll(els); + for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { + el = _ref[_j]; + elements.push(el); + } + } else if (els.nodeType != null) { + elements = [els]; + } + if (!((elements != null) && elements.length)) { + throw new Error("Invalid `" + name + "` option provided. Please provide a CSS selector, a plain HTML element or a list of those."); + } + return elements; + }; + + Dropzone.confirm = function(question, accepted, rejected) { + if (window.confirm(question)) { + return accepted(); + } else if (rejected != null) { + return rejected(); + } + }; + + Dropzone.isValidFile = function(file, acceptedFiles) { + var baseMimeType, mimeType, validType, _i, _len; + if (!acceptedFiles) { + return true; + } + acceptedFiles = acceptedFiles.split(","); + mimeType = file.type; + baseMimeType = mimeType.replace(/\/.*$/, ""); + for (_i = 0, _len = acceptedFiles.length; _i < _len; _i++) { + validType = acceptedFiles[_i]; + validType = validType.trim(); + if (validType.charAt(0) === ".") { + if (file.name.toLowerCase().indexOf(validType.toLowerCase(), file.name.length - validType.length) !== -1) { + return true; + } + } else if (/\/\*$/.test(validType)) { + if (baseMimeType === validType.replace(/\/.*$/, "")) { + return true; + } + } else { + if (mimeType === validType) { + return true; + } + } + } + return false; + }; + + if (typeof jQuery !== "undefined" && jQuery !== null) { + jQuery.fn.dropzone = function(options) { + return this.each(function() { + return new Dropzone(this, options); + }); + }; + } + + if (typeof module !== "undefined" && module !== null) { + module.exports = Dropzone; + } else { + window.Dropzone = Dropzone; + } + + Dropzone.ADDED = "added"; + + Dropzone.QUEUED = "queued"; + + Dropzone.ACCEPTED = Dropzone.QUEUED; + + Dropzone.UPLOADING = "uploading"; + + Dropzone.PROCESSING = Dropzone.UPLOADING; + + Dropzone.CANCELED = "canceled"; + + Dropzone.ERROR = "error"; + + Dropzone.SUCCESS = "success"; + + + /* + + Bugfix for iOS 6 and 7 + Source: http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios + based on the work of https://github.com/stomita/ios-imagefile-megapixel + */ + + detectVerticalSquash = function(img) { + var alpha, canvas, ctx, data, ey, ih, iw, py, ratio, sy; + iw = img.naturalWidth; + ih = img.naturalHeight; + canvas = document.createElement("canvas"); + canvas.width = 1; + canvas.height = ih; + ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0); + data = ctx.getImageData(0, 0, 1, ih).data; + sy = 0; + ey = ih; + py = ih; + while (py > sy) { + alpha = data[(py - 1) * 4 + 3]; + if (alpha === 0) { + ey = py; + } else { + sy = py; + } + py = (ey + sy) >> 1; + } + ratio = py / ih; + if (ratio === 0) { + return 1; + } else { + return ratio; + } + }; + + drawImageIOSFix = function(ctx, img, sx, sy, sw, sh, dx, dy, dw, dh) { + var vertSquashRatio; + vertSquashRatio = detectVerticalSquash(img); + return ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh / vertSquashRatio); + }; + + + /* + * contentloaded.js + * + * Author: Diego Perini (diego.perini at gmail.com) + * Summary: cross-browser wrapper for DOMContentLoaded + * Updated: 20101020 + * License: MIT + * Version: 1.2 + * + * URL: + * http://javascript.nwbox.com/ContentLoaded/ + * http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE + */ + + contentLoaded = function(win, fn) { + var add, doc, done, init, poll, pre, rem, root, top; + done = false; + top = true; + doc = win.document; + root = doc.documentElement; + add = (doc.addEventListener ? "addEventListener" : "attachEvent"); + rem = (doc.addEventListener ? "removeEventListener" : "detachEvent"); + pre = (doc.addEventListener ? "" : "on"); + init = function(e) { + if (e.type === "readystatechange" && doc.readyState !== "complete") { + return; + } + (e.type === "load" ? win : doc)[rem](pre + e.type, init, false); + if (!done && (done = true)) { + return fn.call(win, e.type || e); + } + }; + poll = function() { + var e; + try { + root.doScroll("left"); + } catch (_error) { + e = _error; + setTimeout(poll, 50); + return; + } + return init("poll"); + }; + if (doc.readyState !== "complete") { + if (doc.createEventObject && root.doScroll) { + try { + top = !win.frameElement; + } catch (_error) {} + if (top) { + poll(); + } + } + doc[add](pre + "DOMContentLoaded", init, false); + doc[add](pre + "readystatechange", init, false); + return win[add](pre + "load", init, false); + } + }; + + Dropzone._autoDiscoverFunction = function() { + if (Dropzone.autoDiscover) { + return Dropzone.discover(); + } + }; + + contentLoaded(window, Dropzone._autoDiscoverFunction); + +}).call(this); diff --git a/Resources/views/Form/base_uploaded_files.html.twig b/Resources/views/Form/base_uploaded_files.html.twig index 245f1b4..2749df7 100644 --- a/Resources/views/Form/base_uploaded_files.html.twig +++ b/Resources/views/Form/base_uploaded_files.html.twig @@ -70,6 +70,14 @@ z-index: 3; } + .js-file-upload-hide { + display: none; + } + + .js-reached-limit-message { + color: #ff0000; + } + {% block glavweb_uploaded_files_top %}{% endblock %} diff --git a/Resources/views/Form/fields.html.twig b/Resources/views/Form/fields.html.twig index 540786a..1af1dc5 100644 --- a/Resources/views/Form/fields.html.twig +++ b/Resources/views/Form/fields.html.twig @@ -4,7 +4,7 @@
- + {% if useLink %} {% include 'GlavwebUploaderBundle:Form:view-link.html.twig' %} @@ -18,7 +18,7 @@ {# PREVIEWS #} - +
{% include uploadItemTpl with {'showMark': showMark} %} @@ -39,7 +39,6 @@ var dzSortedArray = []; $('.dz-preview').each(function(){ - console.log(); dzSortedArray.push($(this).data('id')); }); @@ -62,20 +61,23 @@ {% endblock %} {% block glavweb_uploader_dropzone_widget__stylesheet %} - - + {% endblock %} {% block glavweb_uploader_dropzone_widget__script %} {# JAVASCRIPT #} - +