├── .gitignore ├── .php_cs ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src ├── CropboxWidget.php ├── assets │ ├── CropboxAsset.php │ └── WidgetAsset.php ├── messages │ ├── es │ │ └── core.php │ ├── hu │ │ └── core.php │ ├── it │ │ └── core.php │ ├── nl │ │ └── core.php │ ├── pt-BR │ │ └── core.php │ ├── ru │ │ └── core.php │ └── tr │ │ └── core.php ├── resources │ ├── cropbox.css │ ├── cropbox.js │ ├── cropbox.min.css │ └── cropbox.min.js └── views │ └── field.php └── tests └── functionals ├── CropboxTest.php ├── TestCase.php ├── assets ├── assets │ └── .gitignore ├── config │ └── main.php ├── controllers │ └── CropboxController.php ├── forms │ └── CropboxForm.php └── views │ ├── cropbox │ └── index.php │ └── layouts │ └── main.php └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /vendor 3 | /composer.lock 4 | /build 5 | /.php_cs.cache 6 | /nbproject 7 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | in([ 7 | __DIR__ . '/src', 8 | __DIR__ . '/tests', 9 | ]); 10 | return Config::create() 11 | ->setRules([ 12 | '@PSR2' => true, 13 | 'array_syntax' => [ 14 | 'syntax' => 'short', 15 | ], 16 | ]) 17 | ->setFinder($finder); -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - '5.4' 4 | - '5.5' 5 | - '5.6' 6 | - '7.0' 7 | 8 | before_script: 9 | - travis_retry composer self-update && composer --version 10 | - travis_retry composer global require "fxp/composer-asset-plugin:^1.3.1" 11 | - export PATH="$HOME/.composer/vendor/bin:$PATH" 12 | - travis_retry composer install --prefer-dist --no-interaction 13 | 14 | after_script: 15 | - vendor/bin/coveralls -v 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | yii2-widget-cropbox 2 | =================== 3 | 4 | v5.1.1 [2017-05-22] 5 | ------------------- 6 | 7 | - Fixed uploaded bug. 8 | 9 | v5.1.0 [2017-05-20] 10 | ------------------- 11 | 12 | - Update js-cropbox extension. 13 | 14 | v5.0.2 [2017-05-13] 15 | ------------------- 16 | 17 | - Added code fixer. 18 | - Removed HHVM from tests. 19 | 20 | v5.0.1 [2017-05-13] 21 | ------------------- 22 | 23 | - Minified resource files. 24 | - Added basic tests. 25 | 26 | v5.0.0 [2017-05-12] 27 | ------------------- 28 | 29 | - Fixed headers style of markdown. (bryant1410) 30 | - Removed `bupy7\cropbox\Cropbox` and `bupy7\cropbox\MouseWheelAsset` class. 31 | - Added `bupy7\cropbox\CropboxWidget` class. 32 | - Removed `assets` directory. 33 | - Replaced `bower-asset/jq-cropbox` to `bower-asset/js-cropbox` dependency. 34 | - Added jQuery wrapper for `js-cropbox` extension. 35 | - Rewrite `README.md`. 36 | 37 | v4.1.2 [2016-09-28] 38 | ------------------- 39 | 40 | - Added Spanish translation. (pablonunez-dev) 41 | 42 | v4.1.1 [2016-05-18] 43 | ------------------- 44 | 45 | - Added dutch translation. (alexjeen) 46 | 47 | v4.1.0 [2016-03-20] 48 | ------------------- 49 | 50 | - Fixed language code 'tr' to [ISO standart](https://www.iso.org/obp/ui/#iso:code:3166:TR). 51 | - Added translation of Portuguese language. (manoelt) 52 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Belosludcev Vasilij 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Belosludcev Vasilij or BuPy7 nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | yii2-widget-cropbox 2 | =================== 3 | 4 | [![Latest Stable Version](https://poser.pugx.org/bupy7/yii2-widget-cropbox/v/stable)](https://packagist.org/packages/bupy7/yii2-widget-cropbox) 5 | [![Total Downloads](https://poser.pugx.org/bupy7/yii2-widget-cropbox/downloads)](https://packagist.org/packages/bupy7/yii2-widget-cropbox) 6 | [![License](https://poser.pugx.org/bupy7/yii2-widget-cropbox/license)](https://packagist.org/packages/bupy7/yii2-widget-cropbox) 7 | [![Build Status](https://travis-ci.org/bupy7/yii2-widget-cropbox.svg?branch=master)](https://travis-ci.org/bupy7/yii2-widget-cropbox) 8 | [![Coverage Status](https://coveralls.io/repos/github/bupy7/yii2-widget-cropbox/badge.svg?branch=master)](https://coveralls.io/github/bupy7/yii2-widget-cropbox?branch=master) 9 | 10 | This is Yii2 widget wrapper for [js-cropbox](https://github.com/bupy7/js-cropbox). 11 | 12 | Demo and documentation of plugin 13 | -------------------------------- 14 | 15 | [js-cropbox Demo](http://bupy7.github.io/js-cropbox/) 16 | 17 | [js-cropbox README](https://github.com/bupy7/js-cropbox/blob/master/README.md) 18 | 19 | Installation 20 | ------------ 21 | 22 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 23 | 24 | Either run 25 | ``` 26 | $ php composer.phar require --prefer-dist bupy7/yii2-widget-cropbox "*" 27 | ``` 28 | 29 | or add 30 | ``` 31 | "bupy7/yii2-widget-cropbox": "*" 32 | ``` 33 | 34 | to the **require** section of your **composer.json** file. 35 | 36 | If you use v4.1.2 then go to [v4.1.2](https://github.com/bupy7/yii2-widget-cropbox/tree/v4.1.2). 37 | 38 | If you use v3.0.1 then go to [v3.0.1](https://github.com/bupy7/yii2-widget-cropbox/tree/v3.0.1). 39 | 40 | If you use v2.2 then go to [v2.2](https://github.com/bupy7/yii2-widget-cropbox/tree/v2.2). 41 | 42 | If you use v1.0 then go to [v1.0](https://github.com/bupy7/yii2-widget-cropbox/tree/v1.0). 43 | 44 | Options 45 | ------- 46 | 47 | #### `$pluginOptions` 48 | 49 | Contain configuration of js-cropbox wrapper. 50 | 51 | ##### (array) `$variants`: Variants of cropping image. 52 | More info: https://github.com/bupy7/js-cropbox#object-variants 53 | 54 | ##### (array) `[$selectors]`: CSS selectors for attach events of cropbox. 55 | 56 | - (string) fileInput 57 | - (string) btnCrop 58 | - (string) btnReset 59 | - (string) btnScaleIn 60 | - (string) btnScaleOut 61 | - (string) croppedContainer 62 | - (string) croppedDataInput 63 | - (string) messageContainer 64 | 65 | ##### (array) `[$messages]`: Alert messages for each a variant. 66 | 67 | Usage 68 | ----- 69 | 70 | For example, I will use **Imagine extensions for Yii2** https://github.com/yiisoft/yii2-imagine . You can use something other. 71 | 72 | **Add in action to your controller:** 73 | 74 | ```php 75 | ... 76 | 77 | if ($model->load(Yii::$app->request->post())) 78 | { 79 | $model->image = \yii\web\UploadedFile::getInstance($model, 'image'); 80 | 81 | if ($model->save()) 82 | { 83 | return $this->redirect(['index']); 84 | } 85 | } 86 | 87 | ... 88 | ``` 89 | 90 | **Add to your view:** 91 | 92 | ```php 93 | use bupy7\cropbox\CropboxWidget; 94 | 95 | $form = ActiveForm::begin([ 96 | 'options' => ['enctype'=>'multipart/form-data'], 97 | ]); 98 | 99 | ... 100 | 101 | echo $form->field($model, 'image')->widget(CropboxWidget::className(), [ 102 | 'croppedDataAttribute' => 'crop_info', 103 | ]); 104 | 105 | ... 106 | ``` 107 | 108 | **Add to your model:** 109 | 110 | ```php 111 | ... 112 | 113 | use yii\helpers\FileHelper; 114 | use yii\imagine\Image; 115 | use yii\helpers\Json; 116 | use Imagine\Image\Box; 117 | use Imagine\Image\Point; 118 | 119 | ... 120 | 121 | public $image; 122 | public $crop_info; 123 | 124 | ... 125 | 126 | public function rules() 127 | { 128 | ... 129 | 130 | [ 131 | 'image', 132 | 'image', 133 | 'extensions' => ['jpg', 'jpeg', 'png', 'gif'], 134 | 'mimeTypes' => ['image/jpeg', 'image/pjpeg', 'image/png', 'image/gif'], 135 | ], 136 | ['crop_info', 'safe'], 137 | 138 | ... 139 | } 140 | 141 | ... 142 | 143 | public function afterSave($insert, $changedAttributes) 144 | { 145 | ... 146 | 147 | // open image 148 | $image = Image::getImagine()->open($this->image->tempName); 149 | 150 | // rendering information about crop of ONE option 151 | $cropInfo = Json::decode($this->crop_info)[0]; 152 | $cropInfo['dWidth'] = (int)$cropInfo['dWidth']; //new width image 153 | $cropInfo['dHeight'] = (int)$cropInfo['dHeight']; //new height image 154 | $cropInfo['x'] = $cropInfo['x']; //begin position of frame crop by X 155 | $cropInfo['y'] = $cropInfo['y']; //begin position of frame crop by Y 156 | // Properties bolow we don't use in this example 157 | //$cropInfo['ratio'] = $cropInfo['ratio'] == 0 ? 1.0 : (float)$cropInfo['ratio']; //ratio image. 158 | //$cropInfo['width'] = (int)$cropInfo['width']; //width of cropped image 159 | //$cropInfo['height'] = (int)$cropInfo['height']; //height of cropped image 160 | //$cropInfo['sWidth'] = (int)$cropInfo['sWidth']; //width of source image 161 | //$cropInfo['sHeight'] = (int)$cropInfo['sHeight']; //height of source image 162 | 163 | //delete old images 164 | $oldImages = FileHelper::findFiles(Yii::getAlias('@path/to/save/image'), [ 165 | 'only' => [ 166 | $this->id . '.*', 167 | 'thumb_' . $this->id . '.*', 168 | ], 169 | ]); 170 | for ($i = 0; $i != count($oldImages); $i++) { 171 | @unlink($oldImages[$i]); 172 | } 173 | 174 | //saving thumbnail 175 | $newSizeThumb = new Box($cropInfo['dWidth'], $cropInfo['dHeight']); 176 | $cropSizeThumb = new Box(200, 200); //frame size of crop 177 | $cropPointThumb = new Point($cropInfo['x'], $cropInfo['y']); 178 | $pathThumbImage = Yii::getAlias('@path/to/save/image') 179 | . '/thumb_' 180 | . $this->id 181 | . '.' 182 | . $this->image->getExtension(); 183 | 184 | $image->resize($newSizeThumb) 185 | ->crop($cropPointThumb, $cropSizeThumb) 186 | ->save($pathThumbImage, ['quality' => 100]); 187 | 188 | //saving original 189 | $this->image->saveAs( 190 | Yii::getAlias('@path/to/save/image') 191 | . '/' 192 | . $this->id 193 | . '.' 194 | . $this->image->getExtension() 195 | ); 196 | } 197 | 198 | ... 199 | ``` 200 | 201 | Configuration 202 | ------------- 203 | 204 | ### Preview exist image of item 205 | 206 | If you want to show uploaded and cropped image, you must add following code: 207 | 208 | ```php 209 | echo $form->field($model, 'image')->widget(CropboxWidget::className(), [ 210 | 211 | ... 212 | 213 | 'croppedImagesUrl' => [ 214 | 'url/to/small/image' 215 | ], 216 | 'originalImageUrl' => 'url/to/original/image', 217 | ]); 218 | ``` 219 | 220 | If you will click on preview image you see original image. 221 | 222 | ### Crop with save real size of image 223 | 224 | Difference from previous methods in that we don't resize image before crop it. 225 | We cropped image as we see it in editor box with saving real size. 226 | 227 | For this we will use property `ratio` from `$cropInfo`. 228 | 229 | ```php 230 | $cropInfo = Json::decode($this->crop_info)[0]; 231 | $cropInfo['dWidth'] = (int)$cropInfo['dWidth']; 232 | $cropInfo['dHeight'] = (int)$cropInfo['dHeight']; 233 | $cropInfo['x'] = abs($cropInfo['x']); 234 | $cropInfo['y'] = abs($cropInfo['y']); 235 | $cropInfo['ratio'] = $cropInfo['ratio'] == 0 ? 1.0 : (float)$cropInfo['ratio']; 236 | 237 | $image = Image::getImagine()->open($this->image->tempName); 238 | 239 | $cropSizeLarge = new Box(200 / $cropInfo['ratio'], 200 / $cropInfo['ratio']); 240 | $cropPointLarge = new Point($cropInfo['x'] / $cropInfo['ratio'], $cropInfo['y'] / $cropInfo['ratio']); 241 | $pathLargeImage = Yii::getAlias('path/to/save') . '/' . $this->id . '.' . $this->image->getExtension(); 242 | 243 | $image->crop($cropPointLarge, $cropSizeLarge) 244 | ->save($pathLargeImage, ['quality' => $module->qualityLarge]); 245 | ``` 246 | 247 | ### Cropping more once option 248 | 249 | If you will set few veriants crop on plugin you need make following: 250 | 251 | **In model:** 252 | 253 | ```php 254 | ... 255 | 256 | public function afterSave($insert, $changedAttributes) 257 | { 258 | ... 259 | 260 | // open image 261 | $image = Image::getImagine()->open($this->image->tempName); 262 | 263 | $variants = [ 264 | [ 265 | 'width' => 150, 266 | 'height' => 150, 267 | ], 268 | [ 269 | 'width' => 350, 270 | 'height' => 200, 271 | ], 272 | ]; 273 | for($i = 0; $i != count(Json::decode($this->crop_info)); $i++) { 274 | $cropInfo = Json::decode($this->crop_info)[$i]; 275 | $cropInfo['dWidth'] = (int)$cropInfo['dWidth']; //new width image 276 | $cropInfo['dHeight'] = (int)$cropInfo['dHeight']; //new height image 277 | $cropInfo['x'] = abs($cropInfo['x']); //begin position of frame crop by X 278 | $cropInfo['y'] = abs($cropInfo['y']); //begin position of frame crop by Y 279 | //$cropInfo['ratio'] = $cropInfo['ratio'] == 0 ? 1.0 : (float)$cropInfo['ratio']; //ratio image. We don't use in this example 280 | 281 | //delete old images 282 | $oldImages = FileHelper::findFiles(Yii::getAlias('@path/to/save/image'), [ 283 | 'only' => [ 284 | $this->id . '.' . $i . '.*', 285 | 'thumb_' . $this->id . '.' . $i . '.*', 286 | ], 287 | ]); 288 | for ($j = 0; $j != count($oldImages); $j++) { 289 | @unlink($oldImages[$j]); 290 | } 291 | 292 | //saving thumbnail 293 | $newSizeThumb = new Box($cropInfo['dWidth'], $cropInfo['dHeight']); 294 | $cropSizeThumb = new Box($variants[$i]['width'], $variants[$i]['height']); //frame size of crop 295 | $cropPointThumb = new Point($cropInfo['x'], $cropInfo['y']); 296 | $pathThumbImage = Yii::getAlias('@path/to/save/image') . '/thumb_' . $this->id . '.' . $i . '.' . $this->image->getExtension(); 297 | 298 | $image->copy() 299 | ->resize($newSizeThumb) 300 | ->crop($cropPointThumb, $cropSizeThumb) 301 | ->save($pathThumbImage, ['quality' => 100]); 302 | 303 | //saving original 304 | $this->image->saveAs(Yii::getAlias('@path/to/save/image') . $this->id . '.' . $i . '.' . $this->image->getExtension()); 305 | } 306 | } 307 | 308 | ... 309 | 310 | ``` 311 | 312 | ### Use resizing 313 | 314 | If you want use resizing you need pointer min and max size of image in `variants` of `pluginOptions`. 315 | 316 | **In model:** 317 | 318 | ```php 319 | // open image 320 | $image = Image::getImagine()->open($this->image->tempName); 321 | 322 | // rendering information about crop of ONE option 323 | $cropInfo = Json::decode($this->crop_info)[0]; 324 | $cropInfo['dWidth'] = (int)$cropInfo['dWidth']; //new width image 325 | $cropInfo['dHeight'] = (int)$cropInfo['dHeight']; //new height image 326 | $cropInfo['x'] = abs($cropInfo['x']); //begin position of frame crop by X 327 | $cropInfo['y'] = abs($cropInfo['y']); //begin position of frame crop by Y 328 | $cropInfo['width'] = (int)$cropInfo['width']; //width of cropped image 329 | $cropInfo['height'] = (int)$cropInfo['height']; //height of cropped image 330 | // Properties bolow we don't use in this example 331 | //$cropInfo['ratio'] = $cropInfo['ratio'] == 0 ? 1.0 : (float)$cropInfo['ratio']; //ratio image. 332 | 333 | //delete old images 334 | $oldImages = FileHelper::findFiles(Yii::getAlias('@path/to/save/image'), [ 335 | 'only' => [ 336 | $this->id . '.*', 337 | 'thumb_' . $this->id . '.*', 338 | ], 339 | ]); 340 | for ($i = 0; $i != count($oldImages); $i++) { 341 | @unlink($oldImages[$i]); 342 | } 343 | 344 | //saving thumbnail 345 | $newSizeThumb = new Box($cropInfo['dWidth'], $cropInfo['dHeight']); 346 | $cropSizeThumb = new Box($cropInfo['width'], $cropInfo['height']); //frame size of crop 347 | $cropPointThumb = new Point($cropInfo['x'], $cropInfo['y']); 348 | $pathThumbImage = Yii::getAlias('@path/to/save/image') . '/thumb_' . $this->id . '.' . $this->image->getExtension(); 349 | 350 | $image->resize($newSizeThumb) 351 | ->crop($cropPointThumb, $cropSizeThumb) 352 | ->save($pathThumbImage, ['quality' => 100]); 353 | 354 | //saving original 355 | $this->image->saveAs(Yii::getAlias('@path/to/save/image') . $this->id . '.' . $this->image->getExtension()); 356 | ``` 357 | 358 | ## License 359 | 360 | yii2-widget-cropbox is released under the BSD 3-Clause License. 361 | 362 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bupy7/yii2-widget-cropbox", 3 | "description": "This is widget wrapper of https://github.com/bupy7/js-cropbox. This widget allows crop image before to upload to server and send informations about crop as JSON string.", 4 | "keywords": ["yii2", "extension", "widget", "crop", "image"], 5 | "homepage": "https://github.com/bupy7/yii2-widget-cropbox", 6 | "version": "5.1.1", 7 | "type": "yii2-extension", 8 | "license": "BSD-3-Clause", 9 | "authors": [ 10 | { 11 | "name": "Vasilij Belosludcev", 12 | "email": "bupy765@gmail.com", 13 | "homepage": "http://mihaly4.ru" 14 | } 15 | ], 16 | "minimum-stability": "stable", 17 | "require": { 18 | "yiisoft/yii2": "*", 19 | "yiisoft/yii2-bootstrap": "*", 20 | "bower-asset/js-cropbox": "~0.12.0" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "bupy7\\cropbox\\": "src", 25 | "bupy7\\cropbox\\tests\\": "tests" 26 | } 27 | }, 28 | "require-dev": { 29 | "phpunit/phpunit": ">=4.8 <6.0.0", 30 | "satooshi/php-coveralls": "^1.0.1", 31 | "friendsofphp/php-cs-fixer": "2.2.3" 32 | }, 33 | "extra": { 34 | "asset-installer-paths": { 35 | "npm-asset-library": "vendor/npm", 36 | "bower-asset-library": "vendor/bower" 37 | } 38 | }, 39 | "scripts": { 40 | "fixer": "php vendor/bin/php-cs-fixer fix", 41 | "test": "php vendor/bin/phpunit" 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | ./tests/functionals 11 | 12 | 13 | 14 | 15 | ./src 16 | 17 | ./src/messages 18 | ./src/views 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/CropboxWidget.php: -------------------------------------------------------------------------------- 1 | view); 70 | $this->registerTranslations(); 71 | $this->configuration(); 72 | } 73 | 74 | public function run() 75 | { 76 | $pluginOptions = Json::encode($this->pluginOptions); 77 | $this->view->registerJs("$('#{$this->id} .plugin').cropbox({$pluginOptions});", View::POS_READY); 78 | return $this->render($this->pathToView, [ 79 | 'hasModel' => $this->hasModel(), 80 | ]); 81 | } 82 | 83 | /** 84 | * Translates a message to the specified language. 85 | * 86 | * @param string $message the message to be translated. 87 | * @param array $params the parameters that will be used to replace the corresponding placeholders in the message. 88 | * @param string $language the language code (e.g. `en-US`, `en`). If this is null, the current of application 89 | * language. 90 | * @return string 91 | */ 92 | public static function t($message, $params = [], $language = null) 93 | { 94 | return Yii::t('bupy7/cropbox', $message, $params, $language); 95 | } 96 | 97 | /** 98 | * Registration of translation class. 99 | */ 100 | protected function registerTranslations() 101 | { 102 | Yii::$app->i18n->translations['bupy7/cropbox'] = [ 103 | 'class' => 'yii\i18n\PhpMessageSource', 104 | 'sourceLanguage' => 'en', 105 | 'basePath' => '@bupy7/cropbox/messages', 106 | 'fileMap' => [ 107 | 'bupy7/cropbox' => 'core.php', 108 | ], 109 | ]; 110 | } 111 | 112 | /** 113 | * Configuration the widget. 114 | */ 115 | protected function configuration() 116 | { 117 | $this->options = array_merge(['accept' => 'image/*'], $this->options); 118 | $croppedDataInput = $this->croppedDataAttribute; 119 | if ($this->hasModel()) { 120 | $croppedDataInput = Html::getInputName($this->model, $croppedDataInput); 121 | } else { 122 | $croppedDataInput = $this->croppedDataName; 123 | } 124 | $this->pluginOptions = array_merge([ 125 | 'selectors' => [ 126 | 'fileInput' => sprintf('#%s input[type="file"]', $this->id), 127 | 'btnCrop' => sprintf('#%s .btn-crop', $this->id), 128 | 'btnReset' => sprintf('#%s .btn-reset', $this->id), 129 | 'btnScaleIn' => sprintf('#%s .btn-scale-in', $this->id), 130 | 'btnScaleOut' => sprintf('#%s .btn-scale-out', $this->id), 131 | 'croppedContainer' => sprintf('#%s .cropped-images-cropbox', $this->id), 132 | 'croppedDataInput' => sprintf('#%s input[name="%s"]', $this->id, $croppedDataInput), 133 | 'messageContainer' => sprintf('#%s .message-container-cropbox', $this->id), 134 | ], 135 | ], $this->pluginOptions); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/assets/CropboxAsset.php: -------------------------------------------------------------------------------- 1 | 9 | * @since 5.0.0 10 | */ 11 | class CropboxAsset extends AssetBundle 12 | { 13 | public function init() 14 | { 15 | $this->sourcePath = '@bower/js-cropbox/build'; 16 | $this->css = ['cropbox' . (!YII_DEBUG ? '.min' : '') . '.css']; 17 | $this->js = ['cropbox' . (!YII_DEBUG ? '.min' : '') . '.js']; 18 | parent::init(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/assets/WidgetAsset.php: -------------------------------------------------------------------------------- 1 | 9 | * @since 5.0.0 10 | */ 11 | class WidgetAsset extends AssetBundle 12 | { 13 | public $sourcePath = '@bupy7/cropbox/resources'; 14 | public $depends = [ 15 | 'yii\web\JqueryAsset', 16 | 'yii\bootstrap\BootstrapAsset', 17 | 'bupy7\cropbox\assets\CropboxAsset', 18 | ]; 19 | 20 | /** 21 | * @since 5.0.1 22 | */ 23 | public function init() 24 | { 25 | $this->css = ['cropbox' . (!YII_DEBUG ? '.min' : '') . '.css']; 26 | $this->js = ['cropbox' . (!YII_DEBUG ? '.min' : '') . '.js']; 27 | parent::init(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/messages/es/core.php: -------------------------------------------------------------------------------- 1 | 'Recortar', 4 | 'Browse' => 'Seleccionar archivo', 5 | 'Show original' => 'Mostrar original', 6 | 'Height' => 'Alto', 7 | 'Width' => 'Ancho', 8 | 'Reset' => 'Reiniciar', 9 | ]; 10 | -------------------------------------------------------------------------------- /src/messages/hu/core.php: -------------------------------------------------------------------------------- 1 | 'Vágás', 4 | 'Browse' => 'Tallózás', 5 | 'Show original' => 'Eredeti megjelenítése', 6 | 'Height' => 'Magasság', 7 | 'Width' => 'Szélesség', 8 | 'Reset' => 'Visszaállítása' 9 | ]; 10 | -------------------------------------------------------------------------------- /src/messages/it/core.php: -------------------------------------------------------------------------------- 1 | 'Ritaglia', 4 | 'Browse' => 'Scegli immagine', 5 | 'Show original' => 'Mostra originale', 6 | 'Height' => 'Altezza', 7 | 'Width' => 'Larghezza', 8 | 'Reset' => 'Reset', 9 | ]; 10 | -------------------------------------------------------------------------------- /src/messages/nl/core.php: -------------------------------------------------------------------------------- 1 | 'Bijsnijden', 5 | 'Browse' => 'Bladeren', 6 | 'Show original' => 'Toon origineel', 7 | 'Height' => 'Hoogte', 8 | 'Width' => 'Breedte', 9 | 'Reset' => 'Reset', 10 | ]; 11 | -------------------------------------------------------------------------------- /src/messages/pt-BR/core.php: -------------------------------------------------------------------------------- 1 | 'Cortar', 4 | 'Browse' => 'Abrir', 5 | 'Show original' => 'Exibir original', 6 | 'Height' => 'Altura', 7 | 'Width' => 'Largura', 8 | 'Reset' => 'Reiniciar' 9 | ]; 10 | -------------------------------------------------------------------------------- /src/messages/ru/core.php: -------------------------------------------------------------------------------- 1 | 'Обрезать', 4 | 'Browse' => 'Обзор', 5 | 'Show original' => 'Показать оригинал', 6 | 'Height' => 'Высота', 7 | 'Width' => 'Ширина', 8 | 'Reset' => 'Сброс', 9 | ]; 10 | -------------------------------------------------------------------------------- /src/messages/tr/core.php: -------------------------------------------------------------------------------- 1 | 'Kırp', 4 | 'Browse' => 'Gözat', 5 | 'Show original' => 'Orjinali Göster', 6 | 'Height' => 'Yükseklik', 7 | 'Width' => 'Genişlik', 8 | 'Reset' => 'Reset', 9 | ]; 10 | -------------------------------------------------------------------------------- /src/resources/cropbox.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .cropbox .btn-file { 4 | position: relative; 5 | overflow: hidden; 6 | } 7 | .cropbox .btn-file input[type=file] { 8 | position: absolute; 9 | top: 0; 10 | right: 0; 11 | min-width: 100%; 12 | min-height: 100%; 13 | font-size: 100px; 14 | text-align: right; 15 | filter: alpha(opacity=0); 16 | opacity: 0; 17 | outline: none; 18 | background: white; 19 | cursor: inherit; 20 | display: block; 21 | } 22 | .cropped-images-cropbox { 23 | padding: 10px 0 5px 0; 24 | } 25 | .cropped-images-cropbox .img-thumbnail { 26 | margin-right: 5px; 27 | margin-bottom: 5px; 28 | } 29 | .workarea-cropbox, 30 | .bg-cropbox { 31 | height: 500px; 32 | min-height: 500px; 33 | width: 500px; 34 | min-width: 500px; 35 | } 36 | .message-container-cropbox { 37 | display: none; 38 | } 39 | -------------------------------------------------------------------------------- /src/resources/cropbox.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Vasily Belosludcev 3 | * @param {jQuery} $ 4 | * @param {Cropbox} Cropbox 5 | * @since 5.0.0 6 | */ 7 | (function($, Cropbox) { 8 | 'use strict'; 9 | /** 10 | * @param {object|string} options 11 | */ 12 | $.fn.cropbox = function(options) { 13 | // call public method 14 | if (typeof options === 'string') { 15 | var method = options.replace(/^[_]*/, ''); 16 | if (Cropbox.prototype[method]) { 17 | var cb = $(this).data('cropbox'); 18 | return cb[method].apply(cb, Array.prototype.slice.call(arguments, 1)); 19 | } 20 | // create new instance of Cropbox class 21 | } else if (typeof options === 'object' || ! options) { 22 | var selectors = options.selectors, 23 | messages = options.messages || []; 24 | delete options.selectors; 25 | delete options.messages; 26 | return this.each(function() { 27 | $(this).data('cropbox', new Cropbox(this, options)); 28 | attachEvents.call(this, selectors, messages); 29 | }); 30 | // throw an error 31 | } else { 32 | $.error('Method Cropbox::"' + options + '()" not exists.'); 33 | } 34 | }; 35 | 36 | /** 37 | * @param {Object} s 38 | * @param {Array} m 39 | */ 40 | function attachEvents(s, m) { 41 | var $th = $(this); 42 | // scaling 43 | $(s.btnScaleIn).on('click', function() { 44 | $th.cropbox('scale', 1.05); 45 | }); 46 | $(s.btnScaleOut).on('click', function() { 47 | $th.cropbox('scale', 0.95); 48 | }); 49 | $($th.cropbox('getMembrane')).on('wheel', function(event) { 50 | if (event.originalEvent.deltaY < 0) { 51 | $th.cropbox('scale', 1.01); 52 | } else { 53 | $th.cropbox('scale', 0.99); 54 | } 55 | if (event.preventDefault) { 56 | event.preventDefault(); 57 | } 58 | }); 59 | // image loading from a file 60 | $(s.fileInput).on('change', function() { 61 | var fileReader = new FileReader(); 62 | fileReader.readAsDataURL(this.files[0]); 63 | fileReader.onload = function(event) { 64 | $th.cropbox('load', event.target.result); 65 | }; 66 | }); 67 | // reset 68 | $(s.btnReset).on('click', function() { 69 | $th.cropbox('reset'); 70 | }); 71 | // crop 72 | $(s.btnCrop).on('click', function() { 73 | $th.cropbox('crop'); 74 | }); 75 | // the cropped event 76 | $th.on('cb:cropped', function(event) { 77 | var $img = $('', { 78 | src: event.detail.data.image, 79 | class: 'img-thumbnail' 80 | }); 81 | $(s.croppedContainer).append($img); 82 | $(s.croppedDataInput).val(JSON.stringify($th.cropbox('getData'))); 83 | }); 84 | // the reset event 85 | function resetHandler() { 86 | $(s.croppedContainer).html(''); 87 | $(s.croppedDataInput).val(''); 88 | }; 89 | $th.on('cb:reset', resetHandler); 90 | // the ready event 91 | $th.on('cb:ready', resetHandler); 92 | // the disabled/enabled event 93 | function disabledHandler() { 94 | $(s.btnScaleIn).attr('disabled', 'disabled'); 95 | $(s.btnScaleOut).attr('disabled', 'disabled'); 96 | $(s.btnCrop).attr('disabled', 'disabled'); 97 | }; 98 | disabledHandler(); 99 | $th.on('cb:disabledCtrls', disabledHandler); 100 | $th.on('cb:enabledCtrls', function() { 101 | $(s.btnScaleIn).removeAttr('disabled'); 102 | $(s.btnScaleOut).removeAttr('disabled'); 103 | $(s.btnCrop).removeAttr('disabled'); 104 | }); 105 | // messages 106 | function showMessage(reset) { 107 | var index = 0; 108 | if (typeof $th.data('mi-cropbox') !== 'undefined' && !(reset || false)) { 109 | index = $th.data('mi-cropbox'); 110 | } 111 | if (typeof m[index] !== 'undefined') { 112 | $(s.messageContainer).html(m[index]).show(); 113 | } else { 114 | $(s.messageContainer).hide(); 115 | } 116 | $th.data('mi-cropbox', ++index); 117 | } 118 | $th.on('cb:cropped', function() { 119 | showMessage(); 120 | }); 121 | $(s.fileInput).on('change', function() { 122 | showMessage(true); 123 | }); 124 | $th.on('cb:reset', function() { 125 | $(s.messageContainer).hide(); 126 | }); 127 | } 128 | })(jQuery, Cropbox); 129 | -------------------------------------------------------------------------------- /src/resources/cropbox.min.css: -------------------------------------------------------------------------------- 1 | 2 | @charset "UTF-8";.cropbox .btn-file{position:relative;overflow:hidden}.cropbox .btn-file input[type=file]{position:absolute;top:0;right:0;min-width:100%;min-height:100%;font-size:100px;text-align:right;filter:alpha(opacity=0);opacity:0;outline:0;background:white;cursor:inherit;display:block}.cropped-images-cropbox{padding:10px 0 5px 0}.cropped-images-cropbox .img-thumbnail{margin-right:5px;margin-bottom:5px}.workarea-cropbox,.bg-cropbox{height:500px;min-height:500px;width:500px;min-width:500px}.message-container-cropbox{display:none} -------------------------------------------------------------------------------- /src/resources/cropbox.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"use strict" 2 | function o(t,o){function n(){e(t.croppedContainer).html(""),e(t.croppedDataInput).val("")}function a(){e(t.btnScaleIn).attr("disabled","disabled"),e(t.btnScaleOut).attr("disabled","disabled"),e(t.btnCrop).attr("disabled","disabled")}function r(n){var a=0 3 | void 0===c.data("mi-cropbox")||n||(a=c.data("mi-cropbox")),void 0!==o[a]?e(t.messageContainer).html(o[a]).show():e(t.messageContainer).hide(),c.data("mi-cropbox",++a)}var c=e(this) 4 | e(t.btnScaleIn).on("click",function(){c.cropbox("scale",1.05)}),e(t.btnScaleOut).on("click",function(){c.cropbox("scale",.95)}),e(c.cropbox("getMembrane")).on("wheel",function(e){e.originalEvent.deltaY<0?c.cropbox("scale",1.01):c.cropbox("scale",.99),e.preventDefault&&e.preventDefault()}),e(t.fileInput).on("change",function(){var e=new FileReader 5 | e.readAsDataURL(this.files[0]),e.onload=function(e){c.cropbox("load",e.target.result)}}),e(t.btnReset).on("click",function(){c.cropbox("reset")}),e(t.btnCrop).on("click",function(){c.cropbox("crop")}),c.on("cb:cropped",function(o){var n=e("",{src:o.detail.data.image,"class":"img-thumbnail"}) 6 | e(t.croppedContainer).append(n),e(t.croppedDataInput).val(JSON.stringify(c.cropbox("getData")))}),c.on("cb:reset",n),c.on("cb:ready",n),a(),c.on("cb:disabledCtrls",a),c.on("cb:enabledCtrls",function(){e(t.btnScaleIn).removeAttr("disabled"),e(t.btnScaleOut).removeAttr("disabled"),e(t.btnCrop).removeAttr("disabled")}),c.on("cb:cropped",function(){r()}),e(t.fileInput).on("change",function(){r(!0)}),c.on("cb:reset",function(){e(t.messageContainer).hide()})}e.fn.cropbox=function(n){if("string"==typeof n){var a=n.replace(/^[_]*/,"") 7 | if(t.prototype[a]){var r=e(this).data("cropbox") 8 | return r[a].apply(r,Array.prototype.slice.call(arguments,1))}}else{if("object"==typeof n||!n){var c=n.selectors,i=n.messages||[] 9 | return delete n.selectors,delete n.messages,this.each(function(){e(this).data("cropbox",new t(this,n)),o.call(this,c,i)})}e.error('Method Cropbox::"'+n+'()" not exists.')}}}(jQuery,Cropbox) 10 | -------------------------------------------------------------------------------- /src/views/field.php: -------------------------------------------------------------------------------- 1 | 7 |
8 |
9 |
10 |
11 | ' . CropboxWidget::t('Browse'); 13 | if ($hasModel) { 14 | echo Html::activeFileInput($this->context->model, $this->context->attribute, $this->context->options); 15 | } else { 16 | echo Html::fileInput($this->context->name, $this->context->value, $this->context->options); 17 | } 18 | ?> 19 |
20 |
21 | ' . CropboxWidget::t('Crop'), [ 22 | 'class' => 'btn btn-success btn-crop', 23 | ]); ?> 24 | ' . CropboxWidget::t('Reset'), [ 25 | 'class' => 'btn btn-warning btn-reset', 26 | ]); ?> 27 | ', [ 28 | 'class' => 'btn btn-default btn-scale-out', 29 | ]); ?> 30 | ', [ 31 | 'class' => 'btn btn-default btn-scale-in', 32 | ]); ?> 33 |
34 |
35 |

36 | context->originalImageUrl)) { 38 | echo Html::a( 39 | ' ' . CropboxWidget::t('Show original'), 40 | $this->context->originalImageUrl, 41 | [ 42 | 'target' => '_blank', 43 | 'class' => 'btn btn-info', 44 | ] 45 | ); 46 | } 47 | ?> 48 |

49 | context->croppedImagesUrl; 51 | foreach ($croppedImagesUrl as $url) { 52 | echo Html::img($url, ['class' => 'img-thumbnail']); 53 | } 54 | ?> 55 |
56 | context->model, $this->context->croppedDataAttribute); 59 | } else { 60 | echo Html::hiddenInput($this->context->croppedDataName, $this->context->croppedDataValue); 61 | } 62 | ?> 63 |
-------------------------------------------------------------------------------- /tests/functionals/CropboxTest.php: -------------------------------------------------------------------------------- 1 | mockApplication(); 12 | $result = Yii::$app->runAction('cropbox/index'); 13 | $this->assertNotEmpty($result); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/functionals/TestCase.php: -------------------------------------------------------------------------------- 1 | destroyApplication(); 16 | } 17 | 18 | /** 19 | * @param array $config 20 | */ 21 | protected function mockApplication($config = []) 22 | { 23 | new Application(ArrayHelper::merge(require __DIR__ . '/assets/config/main.php', $config)); 24 | } 25 | 26 | /** 27 | * Destroys application in Yii::$app by setting it to null. 28 | */ 29 | protected function destroyApplication() 30 | { 31 | Yii::$app = null; 32 | Yii::$container = new Container; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/functionals/assets/assets/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /tests/functionals/assets/config/main.php: -------------------------------------------------------------------------------- 1 | 'app-test', 5 | 'basePath' => __DIR__ . '/..', 6 | 'vendorPath' => __DIR__ . '/../../../../vendor', 7 | 'aliases' => [ 8 | '@bupy7/cropbox' => __DIR__ . '/../../../../src', 9 | ], 10 | 'components' => [ 11 | 'request' => [ 12 | 'enableCsrfValidation' => false, 13 | 'cookieValidationKey' => 'wefJDF8sfdsfSDefwqdxj9oq', 14 | 'scriptFile' => __DIR__ . '/index.php', 15 | 'scriptUrl' => '/index.php', 16 | ], 17 | 'assetManager' => [ 18 | 'basePath' => '@app/assets', 19 | 'baseUrl' => '/', 20 | ], 21 | ] 22 | ]; 23 | -------------------------------------------------------------------------------- /tests/functionals/assets/controllers/CropboxController.php: -------------------------------------------------------------------------------- 1 | render('index', [ 17 | 'form' => $form, 18 | ]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/functionals/assets/forms/CropboxForm.php: -------------------------------------------------------------------------------- 1 | ['jpg', 'jpeg', 'png', 'gif'], 25 | 'mimeTypes' => ['image/jpeg', 'image/pjpeg', 'image/png', 'image/gif'], 26 | ], 27 | ['crop_info', 'safe'], 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/functionals/assets/views/cropbox/index.php: -------------------------------------------------------------------------------- 1 | ['enctype'=>'multipart/form-data'], 8 | 'action' => ['cropbox'], 9 | ]); 10 | 11 | echo $af->field($form, 'image')->widget(CropboxWidget::className(), [ 12 | 'croppedDataAttribute' => 'crop_info', 13 | ]); 14 | 15 | ActiveForm::end(); 16 | -------------------------------------------------------------------------------- /tests/functionals/assets/views/layouts/main.php: -------------------------------------------------------------------------------- 1 | beginPage(); 3 | $this->head(); 4 | $this->beginBody(); 5 | $this->endBody(); 6 | $this->endPage(); 7 | -------------------------------------------------------------------------------- /tests/functionals/bootstrap.php: -------------------------------------------------------------------------------- 1 |