├── .gitignore ├── CropImageUpload.php ├── CropImageUploadAsset.php ├── CropImageUploadBehavior.php ├── LICENSE ├── README.md ├── assets ├── css │ ├── Jcrop.gif │ ├── jquery.Jcrop.css │ └── jquery.Jcrop.min.css └── js │ ├── cropImageUpload.js │ ├── jquery.Jcrop.js │ ├── jquery.Jcrop.min.js │ └── jquery.color.js └── composer.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Include your project-specific ignores in this file 2 | # Read about how to use .gitignore: https://help.github.com/articles/ignoring-files 3 | # Compiled source # 4 | ################### 5 | *.com 6 | #*.class 7 | *.dll 8 | *.exe 9 | *.o 10 | *.so 11 | # Packages # 12 | ############ 13 | # it's better to unpack these files and commit the raw source 14 | # git has its own built in compression methods 15 | *.7z 16 | *.dmg 17 | *.gz 18 | *.iso 19 | *.jar 20 | *.rar 21 | *.tar 22 | *.zip 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | # OS generated files # 29 | ###################### 30 | .DS_Store 31 | .DS_Store? 32 | ._* 33 | .Spotlight-V100 34 | .Trashes 35 | ehthumbs.db 36 | Thumbs.db 37 | #Composer 38 | ######### 39 | composer.phar 40 | vendor 41 | composer.lock 42 | #Compass 43 | ######### 44 | .sass-cache 45 | # Node 46 | ######### 47 | lib-cov 48 | lcov.info 49 | *.seed 50 | *.csv 51 | *.dat 52 | *.out 53 | *.pid 54 | pids 55 | logs 56 | results 57 | build 58 | .grunt 59 | node_modules 60 | bower_components 61 | # PHPSTORM 62 | ######### 63 | .idea -------------------------------------------------------------------------------- /CropImageUpload.php: -------------------------------------------------------------------------------- 1 | 450, 'boxHeight' => 400]; 28 | 29 | /** 30 | * @var string crop ratio 31 | * format is width:height where width and height are both floats 32 | * if has model, will be got from CropImageBehavior 33 | */ 34 | public $ratio; 35 | 36 | /** 37 | * @var string attribute name storing crop value or crop value itself if no model 38 | * if has model, will be got from CropImageBehavior 39 | * crop value has topLeftX-topLeftY-width-height format where all variables are float 40 | * all coordinates are in percents of corresponded image dimension 41 | */ 42 | public $crop_field; 43 | 44 | /** 45 | * @var string crop value 46 | * if has model, will be got from $crop_field of model 47 | * crop value has topLeftX-topLeftY-width-height format where all variables are float 48 | * all coordinates are in percents of corresponded image dimension 49 | */ 50 | public $crop_value; 51 | 52 | /** 53 | * @var string css class of container that stores image crop 54 | */ 55 | public $crop_class = 'crop_medium'; 56 | 57 | 58 | 59 | /** 60 | * @var string url where uploaded files are stored 61 | * if empty and has model, will be got from CropImageBehavior 62 | */ 63 | public $url; 64 | 65 | 66 | /** 67 | * @inheritdoc 68 | */ 69 | public function run() 70 | { 71 | $jsOptions = [ 72 | 'clientOptions' => $this->clientOptions, 73 | 'crop_class' => $this->crop_class, 74 | ]; 75 | 76 | if ($this->hasModel()) { 77 | echo Html::activeInput('file', $this->model, $this->attribute, $this->options); 78 | 79 | $crops = null; 80 | foreach ($this->model->getBehaviors() as $beh) { 81 | if (!empty($beh->attribute) && $beh->attribute == $this->attribute && $beh instanceof CropImageUploadBehavior) { 82 | $crops = $beh->getConfigurations(); 83 | $this->url = $beh->url; 84 | break; 85 | } 86 | } 87 | 88 | if (!$crops) 89 | throw new InvalidConfigException("CropImageUploadBehavior is not found for {$this->attribute} attribute"); 90 | 91 | $jsOptions['crops'] = []; 92 | $input_name = Html::getInputName($this->model, $this->attribute); 93 | $input_id = Html::getInputId($this->model, $this->attribute); 94 | 95 | echo Html::hiddenInput($input_name . '[file]', Html::getAttributeValue($this->model, $this->attribute), ['id' => $input_id . '_image']); 96 | 97 | foreach ($crops as $ind => $crop) { 98 | $crop_id = $input_id . '_crop' . $ind; 99 | echo Html::hiddenInput($input_name . '[' . $ind . ']', $crop['value'] === false ? '-' : $crop['value'], ['id' => $crop_id]); 100 | 101 | $jsOptions['crops'][] = [ 102 | 'input_id' => $crop_id, 103 | 'ratio' => $crop['ratio'], 104 | 'image' => $crop['image'], 105 | ]; 106 | } 107 | 108 | } else { 109 | echo Html::fileInput($this->name, $this->value, $this->options); 110 | 111 | $crop_id = (isset($this->options['id']) ? $this->options['id'] : ($this->name . '_id')) . '_' . $this->crop_field;; 112 | echo Html::hiddenInput($this->crop_field, $this->crop_value, ['id' => $crop_id]); 113 | 114 | $jsOptions['crops'][] = [ 115 | 'input_id' => $crop_id, 116 | 'ratio' => $this->ratio, 117 | ]; 118 | } 119 | 120 | if ($this->url) 121 | $this->url = \Yii::getAlias($this->url); 122 | 123 | $jsOptions['url'] = $this->url; 124 | 125 | 126 | $this->registerPlugin($jsOptions); 127 | } 128 | 129 | /** 130 | * Registers jCrop 131 | */ 132 | protected function registerPlugin($options) 133 | { 134 | $view = $this->getView(); 135 | CropImageUploadAsset::register($view); 136 | 137 | $id = $this->options['id']; 138 | 139 | $view->registerJs("jQuery('#{$id}').cropImageUpload(".json_encode($options).");"); 140 | } 141 | } -------------------------------------------------------------------------------- /CropImageUploadAsset.php: -------------------------------------------------------------------------------- 1 | css[] = YII_DEBUG ? 'css/jquery.Jcrop.css' : 'css/jquery.Jcrop.min.css'; 25 | $this->js[] = YII_DEBUG ? 'js/jquery.Jcrop.js' : 'js/jquery.Jcrop.min.js'; 26 | $this->js[] = 'js/cropImageUpload.js'; 27 | } 28 | } -------------------------------------------------------------------------------- /CropImageUploadBehavior.php: -------------------------------------------------------------------------------- 1 | crops_internal === null) { 67 | /** @var BaseActiveRecord $model */ 68 | $model = $this->owner; 69 | 70 | if (!empty($this->crops)) { 71 | $this->crops_internal = $this->crops; 72 | } else { 73 | $o = []; 74 | foreach (['cropped_field', 'crop_field', 'crop_width', 'ratio'] as $f) { 75 | if ($this->$f) { 76 | $o[$f] = $this->$f; 77 | } 78 | } 79 | $this->crops_internal = [$o]; 80 | } 81 | 82 | foreach ($this->crops_internal as &$crop) { 83 | if (empty($crop['cropped_field'])) { 84 | $crop['value'] = false; 85 | $crop['image'] = $model->getOldAttribute($this->attribute); 86 | } else if (empty($crop['crop_field'])) { 87 | $crop['value'] = false; 88 | $crop['image'] = $model->getAttribute($crop['cropped_field']); 89 | } else { 90 | $crop['value'] = $model->getAttribute($crop['crop_field']); 91 | $crop['image'] = $model->getOldAttribute($this->attribute); 92 | } 93 | } 94 | } 95 | return $this->crops_internal; 96 | } 97 | 98 | /** 99 | * @inheritdoc 100 | */ 101 | public function beforeValidate() 102 | { 103 | /** @var BaseActiveRecord $model */ 104 | $model = $this->owner; 105 | 106 | if (in_array($model->scenario, $this->scenarios) && ($crops = $model->getAttribute($this->attribute)) && is_array($crops)) { 107 | 108 | $image_changed = (!$model->getOldAttribute($this->attribute) && $crops['file']) || ($model->getOldAttribute($this->attribute) != $crops['file']); 109 | 110 | $this->getConfigurations(); 111 | foreach ($this->crops_internal as $ind => &$crop) { 112 | $crop['value'] = $crops[$ind]; 113 | if ($crops[$ind] == '-') 114 | $crops[$ind] = ''; 115 | if (empty($crop['crop_field'])) { 116 | $crop['_changed'] = !empty($crops[$ind]); 117 | } else { 118 | $crop['_changed'] = $crops[$ind] != $model->getAttribute($crop['crop_field']); 119 | $model->setAttribute($crop['crop_field'], $crops[$ind]); 120 | } 121 | if ($image_changed) 122 | $crop['_changed'] = true; 123 | else if ($model->getOldAttribute($this->attribute) == null) 124 | $crop['_changed'] = false; 125 | } 126 | 127 | $model->setAttribute($this->attribute, $crops['file']); 128 | } 129 | 130 | parent::beforeValidate(); 131 | } 132 | 133 | /** 134 | * @inheritdoc 135 | */ 136 | public function beforeSave() 137 | { 138 | parent::beforeSave(); 139 | 140 | /** @var BaseActiveRecord $model */ 141 | $model = $this->owner; 142 | 143 | if (in_array($model->scenario, $this->scenarios)) { 144 | 145 | $original = $model->getAttribute($this->attribute); 146 | if (!$original) 147 | $original = $model->getOldAttribute($this->attribute); 148 | 149 | foreach ($this->getConfigurations() as $crop) { 150 | if (isset($crop['_changed']) && $crop['_changed'] && !empty($crop['cropped_field'])) { 151 | $this->delete($crop['cropped_field'], true); 152 | if (!empty($crop['cropped_field'])) 153 | $model->setAttribute($crop['cropped_field'], $this->getCropFileName($original)); 154 | } 155 | } 156 | } 157 | } 158 | /** 159 | * @inheritdoc 160 | */ 161 | public function afterSave() 162 | { 163 | parent::afterSave(); 164 | 165 | if (in_array($this->owner->scenario, $this->scenarios)) { 166 | $image = null; 167 | 168 | foreach ($this->getConfigurations() as $crop) { 169 | if (isset($crop['_changed']) && $crop['_changed']) { 170 | if (!$image) { 171 | $path = $this->getUploadPath($this->attribute); 172 | if (!$path) 173 | $path = $this->getUploadPath($this->attribute, true); 174 | $image = Image::getImagine()->open($path); 175 | } 176 | $this->createCrop($crop, $image->copy()); 177 | } 178 | } 179 | } 180 | } 181 | /** 182 | * this method crops the image 183 | * @param Array $crop crop config 184 | * @param \Imagine\Gd\Image $image 185 | */ 186 | protected function createCrop($crop, $image) 187 | { 188 | if (!empty($crop['cropped_field'])) { 189 | $save_path = $this->getUploadPath($crop['cropped_field']); 190 | } else { 191 | $save_path = $this->getUploadPath($this->attribute); 192 | } 193 | 194 | $sizes = explode('-', $crop['value']); 195 | 196 | $real_size = $image->getSize(); 197 | 198 | foreach ($sizes as $ind => $cr) { 199 | $sizes[$ind] = round($sizes[$ind]*($ind%2 == 0 ? $real_size->getWidth() : $real_size->getHeight())/100); 200 | } 201 | 202 | $crop_image = $image->crop(new Point($sizes[0], $sizes[1]), new Box($sizes[2]-$sizes[0], $sizes[3]-$sizes[1])); 203 | 204 | if (!empty($crop['crop_width'])) 205 | $crop_image = $crop_image->resize(new Box($crop['crop_width'], $crop['crop_width'] / $crop['ratio'])); 206 | 207 | $crop_image->save($save_path, isset($crop['save_options']) ? $crop['save_options'] : $this->save_options); 208 | } 209 | 210 | /** 211 | * @param $filename 212 | * @return string 213 | */ 214 | protected function getCropFileName($filename) 215 | { 216 | return uniqid(rand(0, 9999)).'_'. $filename; 217 | } 218 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, karpoff 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 yii2-crop-image-upload 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 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | cropped image upload extension for Yii2 2 | ====================== 3 | 4 | [![Latest Stable Version](https://poser.pugx.org/karpoff/yii2-crop-image-upload/v/stable.svg)](https://packagist.org/packages/karpoff/yii2-crop-image-upload) [![Total Downloads](https://poser.pugx.org/karpoff/yii2-crop-image-upload/downloads.svg)](https://packagist.org/packages/karpoff/yii2-crop-image-upload) [![Latest Unstable Version](https://poser.pugx.org/karpoff/yii2-crop-image-upload/v/unstable.svg)](https://packagist.org/packages/karpoff/yii2-crop-image-upload) [![License](https://poser.pugx.org/karpoff/yii2-crop-image-upload/license.svg)](https://packagist.org/packages/karpoff/yii2-crop-image-upload) 5 | 6 | This extension automatically uploads image and make crop. 7 | 8 | Installation 9 | ------------ 10 | 11 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 12 | 13 | Either run 14 | 15 | ``` 16 | php composer.phar require --prefer-dist karpoff/yii2-crop-image-upload "*" 17 | ``` 18 | 19 | or add 20 | 21 | ```json 22 | "karpoff/yii2-crop-image-upload": "*" 23 | ``` 24 | 25 | to the `require` section of your `composer.json` file. 26 | 27 | Usage 28 | ----- 29 | 30 | ### Upload image and create crop 31 | 32 | Attach the behavior in your model: 33 | 34 | ```php 35 | use karpoff\icrop\CropImageUploadBehavior; 36 | 37 | class Document extends ActiveRecord 38 | { 39 | /** 40 | * @inheritdoc 41 | */ 42 | public function rules() 43 | { 44 | return [ 45 | ['photo', 'file', 'extensions' => 'jpeg, gif, png', 'on' => ['insert', 'update']], 46 | ]; 47 | } 48 | 49 | /** 50 | * @inheritdoc 51 | */ 52 | function behaviors() 53 | { 54 | return [ 55 | [ 56 | 'class' => CropImageUploadBehavior::className(), 57 | 'attribute' => 'photo', 58 | 'scenarios' => ['insert', 'update'], 59 | 'path' => '@webroot/upload/docs', 60 | 'url' => '@web/upload/docs', 61 | 'ratio' => 1, 62 | 'crop_field' => 'photo_crop', 63 | 'cropped_field' => 'photo_cropped', 64 | ], 65 | ]; 66 | } 67 | } 68 | ``` 69 | 70 | Example view file: 71 | 72 | ```php 73 | 74 | 75 | ['enctype' => 'multipart/form-data']]); ?> 76 | field($model, 'photo')->widget(CropImageUpload::className()) ?> 77 |
78 | 'btn btn-primary']) ?> 79 |
80 | 81 | ``` 82 | -------------------------------------------------------------------------------- /assets/css/Jcrop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/karpoff/yii2-crop-image-upload/fd32a9c75539450a5e654f01004342aa47857c3f/assets/css/Jcrop.gif -------------------------------------------------------------------------------- /assets/css/jquery.Jcrop.css: -------------------------------------------------------------------------------- 1 | /* jquery.Jcrop.css v0.9.12 - MIT License */ 2 | /* 3 | The outer-most container in a typical Jcrop instance 4 | If you are having difficulty with formatting related to styles 5 | on a parent element, place any fixes here or in a like selector 6 | 7 | You can also style this element if you want to add a border, etc 8 | A better method for styling can be seen below with .jcrop-light 9 | (Add a class to the holder and style elements for that extended class) 10 | */ 11 | .jcrop-holder { 12 | direction: ltr; 13 | text-align: left; 14 | } 15 | /* Selection Border */ 16 | .jcrop-vline, 17 | .jcrop-hline { 18 | background: #ffffff url("Jcrop.gif"); 19 | font-size: 0; 20 | position: absolute; 21 | } 22 | .jcrop-vline { 23 | height: 100%; 24 | width: 1px !important; 25 | } 26 | .jcrop-vline.right { 27 | right: 0; 28 | } 29 | .jcrop-hline { 30 | height: 1px !important; 31 | width: 100%; 32 | } 33 | .jcrop-hline.bottom { 34 | bottom: 0; 35 | } 36 | /* Invisible click targets */ 37 | .jcrop-tracker { 38 | height: 100%; 39 | width: 100%; 40 | /* "turn off" link highlight */ 41 | -webkit-tap-highlight-color: transparent; 42 | /* disable callout, image save panel */ 43 | -webkit-touch-callout: none; 44 | /* disable cut copy paste */ 45 | -webkit-user-select: none; 46 | } 47 | /* Selection Handles */ 48 | .jcrop-handle { 49 | background-color: #333333; 50 | border: 1px #eeeeee solid; 51 | width: 7px; 52 | height: 7px; 53 | font-size: 1px; 54 | } 55 | .jcrop-handle.ord-n { 56 | left: 50%; 57 | margin-left: -4px; 58 | margin-top: -4px; 59 | top: 0; 60 | } 61 | .jcrop-handle.ord-s { 62 | bottom: 0; 63 | left: 50%; 64 | margin-bottom: -4px; 65 | margin-left: -4px; 66 | } 67 | .jcrop-handle.ord-e { 68 | margin-right: -4px; 69 | margin-top: -4px; 70 | right: 0; 71 | top: 50%; 72 | } 73 | .jcrop-handle.ord-w { 74 | left: 0; 75 | margin-left: -4px; 76 | margin-top: -4px; 77 | top: 50%; 78 | } 79 | .jcrop-handle.ord-nw { 80 | left: 0; 81 | margin-left: -4px; 82 | margin-top: -4px; 83 | top: 0; 84 | } 85 | .jcrop-handle.ord-ne { 86 | margin-right: -4px; 87 | margin-top: -4px; 88 | right: 0; 89 | top: 0; 90 | } 91 | .jcrop-handle.ord-se { 92 | bottom: 0; 93 | margin-bottom: -4px; 94 | margin-right: -4px; 95 | right: 0; 96 | } 97 | .jcrop-handle.ord-sw { 98 | bottom: 0; 99 | left: 0; 100 | margin-bottom: -4px; 101 | margin-left: -4px; 102 | } 103 | /* Dragbars */ 104 | .jcrop-dragbar.ord-n, 105 | .jcrop-dragbar.ord-s { 106 | height: 7px; 107 | width: 100%; 108 | } 109 | .jcrop-dragbar.ord-e, 110 | .jcrop-dragbar.ord-w { 111 | height: 100%; 112 | width: 7px; 113 | } 114 | .jcrop-dragbar.ord-n { 115 | margin-top: -4px; 116 | } 117 | .jcrop-dragbar.ord-s { 118 | bottom: 0; 119 | margin-bottom: -4px; 120 | } 121 | .jcrop-dragbar.ord-e { 122 | margin-right: -4px; 123 | right: 0; 124 | } 125 | .jcrop-dragbar.ord-w { 126 | margin-left: -4px; 127 | } 128 | /* The "jcrop-light" class/extension */ 129 | .jcrop-light .jcrop-vline, 130 | .jcrop-light .jcrop-hline { 131 | background: #ffffff; 132 | filter: alpha(opacity=70) !important; 133 | opacity: .70!important; 134 | } 135 | .jcrop-light .jcrop-handle { 136 | -moz-border-radius: 3px; 137 | -webkit-border-radius: 3px; 138 | background-color: #000000; 139 | border-color: #ffffff; 140 | border-radius: 3px; 141 | } 142 | /* The "jcrop-dark" class/extension */ 143 | .jcrop-dark .jcrop-vline, 144 | .jcrop-dark .jcrop-hline { 145 | background: #000000; 146 | filter: alpha(opacity=70) !important; 147 | opacity: 0.7 !important; 148 | } 149 | .jcrop-dark .jcrop-handle { 150 | -moz-border-radius: 3px; 151 | -webkit-border-radius: 3px; 152 | background-color: #ffffff; 153 | border-color: #000000; 154 | border-radius: 3px; 155 | } 156 | /* Simple macro to turn off the antlines */ 157 | .solid-line .jcrop-vline, 158 | .solid-line .jcrop-hline { 159 | background: #ffffff; 160 | } 161 | /* Fix for twitter bootstrap et al. */ 162 | .jcrop-holder img, 163 | img.jcrop-preview { 164 | max-width: none; 165 | } 166 | -------------------------------------------------------------------------------- /assets/css/jquery.Jcrop.min.css: -------------------------------------------------------------------------------- 1 | /* jquery.Jcrop.min.css v0.9.12 (build:20130126) */ 2 | .jcrop-holder{direction:ltr;text-align:left;} 3 | .jcrop-vline,.jcrop-hline{background:#FFF url(Jcrop.gif);font-size:0;position:absolute;} 4 | .jcrop-vline{height:100%;width:1px!important;} 5 | .jcrop-vline.right{right:0;} 6 | .jcrop-hline{height:1px!important;width:100%;} 7 | .jcrop-hline.bottom{bottom:0;} 8 | .jcrop-tracker{-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;-webkit-user-select:none;height:100%;width:100%;} 9 | .jcrop-handle{background-color:#333;border:1px #EEE solid;font-size:1px;height:7px;width:7px;} 10 | .jcrop-handle.ord-n{left:50%;margin-left:-4px;margin-top:-4px;top:0;} 11 | .jcrop-handle.ord-s{bottom:0;left:50%;margin-bottom:-4px;margin-left:-4px;} 12 | .jcrop-handle.ord-e{margin-right:-4px;margin-top:-4px;right:0;top:50%;} 13 | .jcrop-handle.ord-w{left:0;margin-left:-4px;margin-top:-4px;top:50%;} 14 | .jcrop-handle.ord-nw{left:0;margin-left:-4px;margin-top:-4px;top:0;} 15 | .jcrop-handle.ord-ne{margin-right:-4px;margin-top:-4px;right:0;top:0;} 16 | .jcrop-handle.ord-se{bottom:0;margin-bottom:-4px;margin-right:-4px;right:0;} 17 | .jcrop-handle.ord-sw{bottom:0;left:0;margin-bottom:-4px;margin-left:-4px;} 18 | .jcrop-dragbar.ord-n,.jcrop-dragbar.ord-s{height:7px;width:100%;} 19 | .jcrop-dragbar.ord-e,.jcrop-dragbar.ord-w{height:100%;width:7px;} 20 | .jcrop-dragbar.ord-n{margin-top:-4px;} 21 | .jcrop-dragbar.ord-s{bottom:0;margin-bottom:-4px;} 22 | .jcrop-dragbar.ord-e{margin-right:-4px;right:0;} 23 | .jcrop-dragbar.ord-w{margin-left:-4px;} 24 | .jcrop-light .jcrop-vline,.jcrop-light .jcrop-hline{background:#FFF;filter:alpha(opacity=70)!important;opacity:.70!important;} 25 | .jcrop-light .jcrop-handle{-moz-border-radius:3px;-webkit-border-radius:3px;background-color:#000;border-color:#FFF;border-radius:3px;} 26 | .jcrop-dark .jcrop-vline,.jcrop-dark .jcrop-hline{background:#000;filter:alpha(opacity=70)!important;opacity:.7!important;} 27 | .jcrop-dark .jcrop-handle{-moz-border-radius:3px;-webkit-border-radius:3px;background-color:#FFF;border-color:#000;border-radius:3px;} 28 | .solid-line .jcrop-vline,.solid-line .jcrop-hline{background:#FFF;} 29 | .jcrop-holder img,img.jcrop-preview{max-width:none;} 30 | -------------------------------------------------------------------------------- /assets/js/cropImageUpload.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 3 | $.fn.cropImageUpload = function (config) { 4 | var _obj = $(this); 5 | _obj.on('change', function() { 6 | var file = this.files[0]; 7 | var fr = new FileReader(); 8 | fr.onload = function (readerEvt) { 9 | _image_store.html(''); 10 | $('#' + _obj.attr('id') + '_image').val(Math.random().toString(36).replace(/[^a-z]+/g, '') + '.' + fr.result.split(';')[0].split('/')[1]); 11 | $.each(config.crops, function() { 12 | make_crops(this, fr.result); 13 | }); 14 | }; 15 | fr.readAsDataURL(file); 16 | return false; 17 | }); 18 | 19 | var _image_store = $('
', {'class': 'crop-image-upload-container'}).insertAfter(_obj); 20 | 21 | if (config.crop_class) 22 | _image_store.addClass(config.crop_class); 23 | 24 | var make_crops = function(crop, src) { 25 | if (crop.ratio && (crop.image || src)) { 26 | var cropImage = $('', { 27 | 'src': src ? src : ((config.url ? config.url : '') + '/' + crop.image) 28 | }).hide(); 29 | _image_store.append(cropImage); 30 | 31 | var cropStore = $('#' + crop.input_id); 32 | var cropValue = cropStore.val(); 33 | 34 | var cropImageW; 35 | var cropImageH; 36 | var cropTimeout; 37 | 38 | if (cropValue == '-') { 39 | cropValue = ''; 40 | cropStore.val(cropValue); 41 | if (!src) { 42 | cropImage.show(); 43 | return; 44 | } 45 | } 46 | 47 | if (cropValue == '') 48 | cropValue = ('0-0-100-100'); 49 | 50 | function percent (val, main) { return Number((val*100/main).toFixed(2)); } 51 | 52 | var storeCropParams = function(c) { 53 | cropStore.val(percent(c.x, cropImageW) + '-' + percent(c.y, cropImageH) + '-' + percent(c.x2, cropImageW) + '-' + percent(c.y2, cropImageH)); 54 | }; 55 | 56 | var cropParams = {}; 57 | if (config.clientOptions) 58 | $.extend(cropParams, config.clientOptions); 59 | cropParams.onSelect = storeCropParams; 60 | cropParams.onChange = storeCropParams; 61 | 62 | if (crop.ratio) { 63 | cropParams.aspectRatio = crop.ratio; 64 | } 65 | 66 | cropImage.Jcrop(cropParams, function(){ 67 | cropImageW = cropImage.width(); 68 | cropImageH = cropImage.height(); 69 | 70 | var pp = cropValue.split('-'); 71 | 72 | pp[0] = pp[0]*cropImageW/100; 73 | pp[1] = pp[1]*cropImageH/100; 74 | pp[2] = pp[2]*cropImageW/100; 75 | pp[3] = pp[3]*cropImageH/100; 76 | this.setSelect(pp); 77 | _image_store.show(); 78 | }); 79 | } 80 | }; 81 | 82 | $.each(config.crops, function() { 83 | make_crops(this); 84 | }); 85 | }; 86 | 87 | 88 | })(window.jQuery); 89 | -------------------------------------------------------------------------------- /assets/js/jquery.Jcrop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jquery.Jcrop.js v0.9.12 3 | * jQuery Image Cropping Plugin - released under MIT License 4 | * Author: Kelly Hallman 5 | * http://github.com/tapmodo/Jcrop 6 | * Copyright (c) 2008-2013 Tapmodo Interactive LLC {{{ 7 | * 8 | * Permission is hereby granted, free of charge, to any person 9 | * obtaining a copy of this software and associated documentation 10 | * files (the "Software"), to deal in the Software without 11 | * restriction, including without limitation the rights to use, 12 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the 14 | * Software is furnished to do so, subject to the following 15 | * conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | * OTHER DEALINGS IN THE SOFTWARE. 28 | * 29 | * }}} 30 | */ 31 | 32 | (function ($) { 33 | 34 | $.Jcrop = function (obj, opt) { 35 | var options = $.extend({}, $.Jcrop.defaults), 36 | docOffset, 37 | _ua = navigator.userAgent.toLowerCase(), 38 | is_msie = /msie/.test(_ua), 39 | ie6mode = /msie [1-6]\./.test(_ua); 40 | 41 | // Internal Methods {{{ 42 | function px(n) { 43 | return Math.round(n) + 'px'; 44 | } 45 | function cssClass(cl) { 46 | return options.baseClass + '-' + cl; 47 | } 48 | function supportsColorFade() { 49 | return $.fx.step.hasOwnProperty('backgroundColor'); 50 | } 51 | function getPos(obj) //{{{ 52 | { 53 | var pos = $(obj).offset(); 54 | return [pos.left, pos.top]; 55 | } 56 | //}}} 57 | function mouseAbs(e) //{{{ 58 | { 59 | return [(e.pageX - docOffset[0]), (e.pageY - docOffset[1])]; 60 | } 61 | //}}} 62 | function setOptions(opt) //{{{ 63 | { 64 | if (typeof(opt) !== 'object') opt = {}; 65 | options = $.extend(options, opt); 66 | 67 | $.each(['onChange','onSelect','onRelease','onDblClick'],function(i,e) { 68 | if (typeof(options[e]) !== 'function') options[e] = function () {}; 69 | }); 70 | } 71 | //}}} 72 | function startDragMode(mode, pos, touch) //{{{ 73 | { 74 | docOffset = getPos($img); 75 | Tracker.setCursor(mode === 'move' ? mode : mode + '-resize'); 76 | 77 | if (mode === 'move') { 78 | return Tracker.activateHandlers(createMover(pos), doneSelect, touch); 79 | } 80 | 81 | var fc = Coords.getFixed(); 82 | var opp = oppLockCorner(mode); 83 | var opc = Coords.getCorner(oppLockCorner(opp)); 84 | 85 | Coords.setPressed(Coords.getCorner(opp)); 86 | Coords.setCurrent(opc); 87 | 88 | Tracker.activateHandlers(dragmodeHandler(mode, fc), doneSelect, touch); 89 | } 90 | //}}} 91 | function dragmodeHandler(mode, f) //{{{ 92 | { 93 | return function (pos) { 94 | if (!options.aspectRatio) { 95 | switch (mode) { 96 | case 'e': 97 | pos[1] = f.y2; 98 | break; 99 | case 'w': 100 | pos[1] = f.y2; 101 | break; 102 | case 'n': 103 | pos[0] = f.x2; 104 | break; 105 | case 's': 106 | pos[0] = f.x2; 107 | break; 108 | } 109 | } else { 110 | switch (mode) { 111 | case 'e': 112 | pos[1] = f.y + 1; 113 | break; 114 | case 'w': 115 | pos[1] = f.y + 1; 116 | break; 117 | case 'n': 118 | pos[0] = f.x + 1; 119 | break; 120 | case 's': 121 | pos[0] = f.x + 1; 122 | break; 123 | } 124 | } 125 | Coords.setCurrent(pos); 126 | Selection.update(); 127 | }; 128 | } 129 | //}}} 130 | function createMover(pos) //{{{ 131 | { 132 | var lloc = pos; 133 | KeyManager.watchKeys(); 134 | 135 | return function (pos) { 136 | Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]); 137 | lloc = pos; 138 | 139 | Selection.update(); 140 | }; 141 | } 142 | //}}} 143 | function oppLockCorner(ord) //{{{ 144 | { 145 | switch (ord) { 146 | case 'n': 147 | return 'sw'; 148 | case 's': 149 | return 'nw'; 150 | case 'e': 151 | return 'nw'; 152 | case 'w': 153 | return 'ne'; 154 | case 'ne': 155 | return 'sw'; 156 | case 'nw': 157 | return 'se'; 158 | case 'se': 159 | return 'nw'; 160 | case 'sw': 161 | return 'ne'; 162 | } 163 | } 164 | //}}} 165 | function createDragger(ord) //{{{ 166 | { 167 | return function (e) { 168 | if (options.disabled) { 169 | return false; 170 | } 171 | if ((ord === 'move') && !options.allowMove) { 172 | return false; 173 | } 174 | 175 | // Fix position of crop area when dragged the very first time. 176 | // Necessary when crop image is in a hidden element when page is loaded. 177 | docOffset = getPos($img); 178 | 179 | btndown = true; 180 | startDragMode(ord, mouseAbs(e)); 181 | e.stopPropagation(); 182 | e.preventDefault(); 183 | return false; 184 | }; 185 | } 186 | //}}} 187 | function presize($obj, w, h) //{{{ 188 | { 189 | var nw = $obj.width(), 190 | nh = $obj.height(); 191 | if ((nw > w) && w > 0) { 192 | nw = w; 193 | nh = (w / $obj.width()) * $obj.height(); 194 | } 195 | if ((nh > h) && h > 0) { 196 | nh = h; 197 | nw = (h / $obj.height()) * $obj.width(); 198 | } 199 | xscale = $obj.width() / nw; 200 | yscale = $obj.height() / nh; 201 | $obj.width(nw).height(nh); 202 | } 203 | //}}} 204 | function unscale(c) //{{{ 205 | { 206 | return { 207 | x: c.x * xscale, 208 | y: c.y * yscale, 209 | x2: c.x2 * xscale, 210 | y2: c.y2 * yscale, 211 | w: c.w * xscale, 212 | h: c.h * yscale 213 | }; 214 | } 215 | //}}} 216 | function doneSelect(pos) //{{{ 217 | { 218 | var c = Coords.getFixed(); 219 | if ((c.w > options.minSelect[0]) && (c.h > options.minSelect[1])) { 220 | Selection.enableHandles(); 221 | Selection.done(); 222 | } else { 223 | Selection.release(); 224 | } 225 | Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default'); 226 | } 227 | //}}} 228 | function newSelection(e) //{{{ 229 | { 230 | if (options.disabled) { 231 | return false; 232 | } 233 | if (!options.allowSelect) { 234 | return false; 235 | } 236 | btndown = true; 237 | docOffset = getPos($img); 238 | Selection.disableHandles(); 239 | Tracker.setCursor('crosshair'); 240 | var pos = mouseAbs(e); 241 | Coords.setPressed(pos); 242 | Selection.update(); 243 | Tracker.activateHandlers(selectDrag, doneSelect, e.type.substring(0,5)==='touch'); 244 | KeyManager.watchKeys(); 245 | 246 | e.stopPropagation(); 247 | e.preventDefault(); 248 | return false; 249 | } 250 | //}}} 251 | function selectDrag(pos) //{{{ 252 | { 253 | Coords.setCurrent(pos); 254 | Selection.update(); 255 | } 256 | //}}} 257 | function newTracker() //{{{ 258 | { 259 | var trk = $('
').addClass(cssClass('tracker')); 260 | if (is_msie) { 261 | trk.css({ 262 | opacity: 0, 263 | backgroundColor: 'white' 264 | }); 265 | } 266 | return trk; 267 | } 268 | //}}} 269 | 270 | // }}} 271 | // Initialization {{{ 272 | // Sanitize some options {{{ 273 | if (typeof(obj) !== 'object') { 274 | obj = $(obj)[0]; 275 | } 276 | if (typeof(opt) !== 'object') { 277 | opt = {}; 278 | } 279 | // }}} 280 | setOptions(opt); 281 | // Initialize some jQuery objects {{{ 282 | // The values are SET on the image(s) for the interface 283 | // If the original image has any of these set, they will be reset 284 | // However, if you destroy() the Jcrop instance the original image's 285 | // character in the DOM will be as you left it. 286 | var img_css = { 287 | border: 'none', 288 | visibility: 'visible', 289 | margin: 0, 290 | padding: 0, 291 | position: 'absolute', 292 | top: 0, 293 | left: 0 294 | }; 295 | 296 | var $origimg = $(obj), 297 | img_mode = true; 298 | 299 | if (obj.tagName == 'IMG') { 300 | // Fix size of crop image. 301 | // Necessary when crop image is within a hidden element when page is loaded. 302 | if ($origimg[0].width != 0 && $origimg[0].height != 0) { 303 | // Obtain dimensions from contained img element. 304 | $origimg.width($origimg[0].width); 305 | $origimg.height($origimg[0].height); 306 | } else { 307 | // Obtain dimensions from temporary image in case the original is not loaded yet (e.g. IE 7.0). 308 | var tempImage = new Image(); 309 | tempImage.src = $origimg[0].src; 310 | $origimg.width(tempImage.width); 311 | $origimg.height(tempImage.height); 312 | } 313 | 314 | var $img = $origimg.clone().removeAttr('id').css(img_css).show(); 315 | 316 | $img.width($origimg.width()); 317 | $img.height($origimg.height()); 318 | $origimg.after($img).hide(); 319 | 320 | } else { 321 | $img = $origimg.css(img_css).show(); 322 | img_mode = false; 323 | if (options.shade === null) { options.shade = true; } 324 | } 325 | 326 | presize($img, options.boxWidth, options.boxHeight); 327 | 328 | var boundx = $img.width(), 329 | boundy = $img.height(), 330 | 331 | 332 | $div = $('
').width(boundx).height(boundy).addClass(cssClass('holder')).css({ 333 | position: 'relative', 334 | backgroundColor: options.bgColor 335 | }).insertAfter($origimg).append($img); 336 | 337 | if (options.addClass) { 338 | $div.addClass(options.addClass); 339 | } 340 | 341 | var $img2 = $('
'), 342 | 343 | $img_holder = $('
') 344 | .width('100%').height('100%').css({ 345 | zIndex: 310, 346 | position: 'absolute', 347 | overflow: 'hidden' 348 | }), 349 | 350 | $hdl_holder = $('
') 351 | .width('100%').height('100%').css('zIndex', 320), 352 | 353 | $sel = $('
') 354 | .css({ 355 | position: 'absolute', 356 | zIndex: 600 357 | }).dblclick(function(){ 358 | var c = Coords.getFixed(); 359 | options.onDblClick.call(api,c); 360 | }).insertBefore($img).append($img_holder, $hdl_holder); 361 | 362 | if (img_mode) { 363 | 364 | $img2 = $('') 365 | .attr('src', $img.attr('src')).css(img_css).width(boundx).height(boundy), 366 | 367 | $img_holder.append($img2); 368 | 369 | } 370 | 371 | if (ie6mode) { 372 | $sel.css({ 373 | overflowY: 'hidden' 374 | }); 375 | } 376 | 377 | var bound = options.boundary; 378 | var $trk = newTracker().width(boundx + (bound * 2)).height(boundy + (bound * 2)).css({ 379 | position: 'absolute', 380 | top: px(-bound), 381 | left: px(-bound), 382 | zIndex: 290 383 | }).mousedown(newSelection); 384 | 385 | /* }}} */ 386 | // Set more variables {{{ 387 | var bgcolor = options.bgColor, 388 | bgopacity = options.bgOpacity, 389 | xlimit, ylimit, xmin, ymin, xscale, yscale, enabled = true, 390 | btndown, animating, shift_down; 391 | 392 | docOffset = getPos($img); 393 | // }}} 394 | // }}} 395 | // Internal Modules {{{ 396 | // Touch Module {{{ 397 | var Touch = (function () { 398 | // Touch support detection function adapted (under MIT License) 399 | // from code by Jeffrey Sambells - http://github.com/iamamused/ 400 | function hasTouchSupport() { 401 | var support = {}, events = ['touchstart', 'touchmove', 'touchend'], 402 | el = document.createElement('div'), i; 403 | 404 | try { 405 | for(i=0; i x1 + ox) { 491 | ox -= ox + x1; 492 | } 493 | if (0 > y1 + oy) { 494 | oy -= oy + y1; 495 | } 496 | 497 | if (boundy < y2 + oy) { 498 | oy += boundy - (y2 + oy); 499 | } 500 | if (boundx < x2 + ox) { 501 | ox += boundx - (x2 + ox); 502 | } 503 | 504 | x1 += ox; 505 | x2 += ox; 506 | y1 += oy; 507 | y2 += oy; 508 | } 509 | //}}} 510 | function getCorner(ord) //{{{ 511 | { 512 | var c = getFixed(); 513 | switch (ord) { 514 | case 'ne': 515 | return [c.x2, c.y]; 516 | case 'nw': 517 | return [c.x, c.y]; 518 | case 'se': 519 | return [c.x2, c.y2]; 520 | case 'sw': 521 | return [c.x, c.y2]; 522 | } 523 | } 524 | //}}} 525 | function getFixed() //{{{ 526 | { 527 | if (!options.aspectRatio) { 528 | return getRect(); 529 | } 530 | // This function could use some optimization I think... 531 | var aspect = options.aspectRatio, 532 | min_x = options.minSize[0] / xscale, 533 | 534 | 535 | //min_y = options.minSize[1]/yscale, 536 | max_x = options.maxSize[0] / xscale, 537 | max_y = options.maxSize[1] / yscale, 538 | rw = x2 - x1, 539 | rh = y2 - y1, 540 | rwa = Math.abs(rw), 541 | rha = Math.abs(rh), 542 | real_ratio = rwa / rha, 543 | xx, yy, w, h; 544 | 545 | if (max_x === 0) { 546 | max_x = boundx * 10; 547 | } 548 | if (max_y === 0) { 549 | max_y = boundy * 10; 550 | } 551 | if (real_ratio < aspect) { 552 | yy = y2; 553 | w = rha * aspect; 554 | xx = rw < 0 ? x1 - w : w + x1; 555 | 556 | if (xx < 0) { 557 | xx = 0; 558 | h = Math.abs((xx - x1) / aspect); 559 | yy = rh < 0 ? y1 - h : h + y1; 560 | } else if (xx > boundx) { 561 | xx = boundx; 562 | h = Math.abs((xx - x1) / aspect); 563 | yy = rh < 0 ? y1 - h : h + y1; 564 | } 565 | } else { 566 | xx = x2; 567 | h = rwa / aspect; 568 | yy = rh < 0 ? y1 - h : y1 + h; 569 | if (yy < 0) { 570 | yy = 0; 571 | w = Math.abs((yy - y1) * aspect); 572 | xx = rw < 0 ? x1 - w : w + x1; 573 | } else if (yy > boundy) { 574 | yy = boundy; 575 | w = Math.abs(yy - y1) * aspect; 576 | xx = rw < 0 ? x1 - w : w + x1; 577 | } 578 | } 579 | 580 | // Magic %-) 581 | if (xx > x1) { // right side 582 | if (xx - x1 < min_x) { 583 | xx = x1 + min_x; 584 | } else if (xx - x1 > max_x) { 585 | xx = x1 + max_x; 586 | } 587 | if (yy > y1) { 588 | yy = y1 + (xx - x1) / aspect; 589 | } else { 590 | yy = y1 - (xx - x1) / aspect; 591 | } 592 | } else if (xx < x1) { // left side 593 | if (x1 - xx < min_x) { 594 | xx = x1 - min_x; 595 | } else if (x1 - xx > max_x) { 596 | xx = x1 - max_x; 597 | } 598 | if (yy > y1) { 599 | yy = y1 + (x1 - xx) / aspect; 600 | } else { 601 | yy = y1 - (x1 - xx) / aspect; 602 | } 603 | } 604 | 605 | if (xx < 0) { 606 | x1 -= xx; 607 | xx = 0; 608 | } else if (xx > boundx) { 609 | x1 -= xx - boundx; 610 | xx = boundx; 611 | } 612 | 613 | if (yy < 0) { 614 | y1 -= yy; 615 | yy = 0; 616 | } else if (yy > boundy) { 617 | y1 -= yy - boundy; 618 | yy = boundy; 619 | } 620 | 621 | return makeObj(flipCoords(x1, y1, xx, yy)); 622 | } 623 | //}}} 624 | function rebound(p) //{{{ 625 | { 626 | if (p[0] < 0) p[0] = 0; 627 | if (p[1] < 0) p[1] = 0; 628 | 629 | if (p[0] > boundx) p[0] = boundx; 630 | if (p[1] > boundy) p[1] = boundy; 631 | 632 | return [Math.round(p[0]), Math.round(p[1])]; 633 | } 634 | //}}} 635 | function flipCoords(x1, y1, x2, y2) //{{{ 636 | { 637 | var xa = x1, 638 | xb = x2, 639 | ya = y1, 640 | yb = y2; 641 | if (x2 < x1) { 642 | xa = x2; 643 | xb = x1; 644 | } 645 | if (y2 < y1) { 646 | ya = y2; 647 | yb = y1; 648 | } 649 | return [xa, ya, xb, yb]; 650 | } 651 | //}}} 652 | function getRect() //{{{ 653 | { 654 | var xsize = x2 - x1, 655 | ysize = y2 - y1, 656 | delta; 657 | 658 | if (xlimit && (Math.abs(xsize) > xlimit)) { 659 | x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit); 660 | } 661 | if (ylimit && (Math.abs(ysize) > ylimit)) { 662 | y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit); 663 | } 664 | 665 | if (ymin / yscale && (Math.abs(ysize) < ymin / yscale)) { 666 | y2 = (ysize > 0) ? (y1 + ymin / yscale) : (y1 - ymin / yscale); 667 | } 668 | if (xmin / xscale && (Math.abs(xsize) < xmin / xscale)) { 669 | x2 = (xsize > 0) ? (x1 + xmin / xscale) : (x1 - xmin / xscale); 670 | } 671 | 672 | if (x1 < 0) { 673 | x2 -= x1; 674 | x1 -= x1; 675 | } 676 | if (y1 < 0) { 677 | y2 -= y1; 678 | y1 -= y1; 679 | } 680 | if (x2 < 0) { 681 | x1 -= x2; 682 | x2 -= x2; 683 | } 684 | if (y2 < 0) { 685 | y1 -= y2; 686 | y2 -= y2; 687 | } 688 | if (x2 > boundx) { 689 | delta = x2 - boundx; 690 | x1 -= delta; 691 | x2 -= delta; 692 | } 693 | if (y2 > boundy) { 694 | delta = y2 - boundy; 695 | y1 -= delta; 696 | y2 -= delta; 697 | } 698 | if (x1 > boundx) { 699 | delta = x1 - boundy; 700 | y2 -= delta; 701 | y1 -= delta; 702 | } 703 | if (y1 > boundy) { 704 | delta = y1 - boundy; 705 | y2 -= delta; 706 | y1 -= delta; 707 | } 708 | 709 | return makeObj(flipCoords(x1, y1, x2, y2)); 710 | } 711 | //}}} 712 | function makeObj(a) //{{{ 713 | { 714 | return { 715 | x: a[0], 716 | y: a[1], 717 | x2: a[2], 718 | y2: a[3], 719 | w: a[2] - a[0], 720 | h: a[3] - a[1] 721 | }; 722 | } 723 | //}}} 724 | 725 | return { 726 | flipCoords: flipCoords, 727 | setPressed: setPressed, 728 | setCurrent: setCurrent, 729 | getOffset: getOffset, 730 | moveOffset: moveOffset, 731 | getCorner: getCorner, 732 | getFixed: getFixed 733 | }; 734 | }()); 735 | 736 | //}}} 737 | // Shade Module {{{ 738 | var Shade = (function() { 739 | var enabled = false, 740 | holder = $('
').css({ 741 | position: 'absolute', 742 | zIndex: 240, 743 | opacity: 0 744 | }), 745 | shades = { 746 | top: createShade(), 747 | left: createShade().height(boundy), 748 | right: createShade().height(boundy), 749 | bottom: createShade() 750 | }; 751 | 752 | function resizeShades(w,h) { 753 | shades.left.css({ height: px(h) }); 754 | shades.right.css({ height: px(h) }); 755 | } 756 | function updateAuto() 757 | { 758 | return updateShade(Coords.getFixed()); 759 | } 760 | function updateShade(c) 761 | { 762 | shades.top.css({ 763 | left: px(c.x), 764 | width: px(c.w), 765 | height: px(c.y) 766 | }); 767 | shades.bottom.css({ 768 | top: px(c.y2), 769 | left: px(c.x), 770 | width: px(c.w), 771 | height: px(boundy-c.y2) 772 | }); 773 | shades.right.css({ 774 | left: px(c.x2), 775 | width: px(boundx-c.x2) 776 | }); 777 | shades.left.css({ 778 | width: px(c.x) 779 | }); 780 | } 781 | function createShade() { 782 | return $('
').css({ 783 | position: 'absolute', 784 | backgroundColor: options.shadeColor||options.bgColor 785 | }).appendTo(holder); 786 | } 787 | function enableShade() { 788 | if (!enabled) { 789 | enabled = true; 790 | holder.insertBefore($img); 791 | updateAuto(); 792 | Selection.setBgOpacity(1,0,1); 793 | $img2.hide(); 794 | 795 | setBgColor(options.shadeColor||options.bgColor,1); 796 | if (Selection.isAwake()) 797 | { 798 | setOpacity(options.bgOpacity,1); 799 | } 800 | else setOpacity(1,1); 801 | } 802 | } 803 | function setBgColor(color,now) { 804 | colorChangeMacro(getShades(),color,now); 805 | } 806 | function disableShade() { 807 | if (enabled) { 808 | holder.remove(); 809 | $img2.show(); 810 | enabled = false; 811 | if (Selection.isAwake()) { 812 | Selection.setBgOpacity(options.bgOpacity,1,1); 813 | } else { 814 | Selection.setBgOpacity(1,1,1); 815 | Selection.disableHandles(); 816 | } 817 | colorChangeMacro($div,0,1); 818 | } 819 | } 820 | function setOpacity(opacity,now) { 821 | if (enabled) { 822 | if (options.bgFade && !now) { 823 | holder.animate({ 824 | opacity: 1-opacity 825 | },{ 826 | queue: false, 827 | duration: options.fadeTime 828 | }); 829 | } 830 | else holder.css({opacity:1-opacity}); 831 | } 832 | } 833 | function refreshAll() { 834 | options.shade ? enableShade() : disableShade(); 835 | if (Selection.isAwake()) setOpacity(options.bgOpacity); 836 | } 837 | function getShades() { 838 | return holder.children(); 839 | } 840 | 841 | return { 842 | update: updateAuto, 843 | updateRaw: updateShade, 844 | getShades: getShades, 845 | setBgColor: setBgColor, 846 | enable: enableShade, 847 | disable: disableShade, 848 | resize: resizeShades, 849 | refresh: refreshAll, 850 | opacity: setOpacity 851 | }; 852 | }()); 853 | // }}} 854 | // Selection Module {{{ 855 | var Selection = (function () { 856 | var awake, 857 | hdep = 370, 858 | borders = {}, 859 | handle = {}, 860 | dragbar = {}, 861 | seehandles = false; 862 | 863 | // Private Methods 864 | function insertBorder(type) //{{{ 865 | { 866 | var jq = $('
').css({ 867 | position: 'absolute', 868 | opacity: options.borderOpacity 869 | }).addClass(cssClass(type)); 870 | $img_holder.append(jq); 871 | return jq; 872 | } 873 | //}}} 874 | function dragDiv(ord, zi) //{{{ 875 | { 876 | var jq = $('
').mousedown(createDragger(ord)).css({ 877 | cursor: ord + '-resize', 878 | position: 'absolute', 879 | zIndex: zi 880 | }).addClass('ord-'+ord); 881 | 882 | if (Touch.support) { 883 | jq.bind('touchstart.jcrop', Touch.createDragger(ord)); 884 | } 885 | 886 | $hdl_holder.append(jq); 887 | return jq; 888 | } 889 | //}}} 890 | function insertHandle(ord) //{{{ 891 | { 892 | var hs = options.handleSize, 893 | 894 | div = dragDiv(ord, hdep++).css({ 895 | opacity: options.handleOpacity 896 | }).addClass(cssClass('handle')); 897 | 898 | if (hs) { div.width(hs).height(hs); } 899 | 900 | return div; 901 | } 902 | //}}} 903 | function insertDragbar(ord) //{{{ 904 | { 905 | return dragDiv(ord, hdep++).addClass('jcrop-dragbar'); 906 | } 907 | //}}} 908 | function createDragbars(li) //{{{ 909 | { 910 | var i; 911 | for (i = 0; i < li.length; i++) { 912 | dragbar[li[i]] = insertDragbar(li[i]); 913 | } 914 | } 915 | //}}} 916 | function createBorders(li) //{{{ 917 | { 918 | var cl,i; 919 | for (i = 0; i < li.length; i++) { 920 | switch(li[i]){ 921 | case'n': cl='hline'; break; 922 | case's': cl='hline bottom'; break; 923 | case'e': cl='vline right'; break; 924 | case'w': cl='vline'; break; 925 | } 926 | borders[li[i]] = insertBorder(cl); 927 | } 928 | } 929 | //}}} 930 | function createHandles(li) //{{{ 931 | { 932 | var i; 933 | for (i = 0; i < li.length; i++) { 934 | handle[li[i]] = insertHandle(li[i]); 935 | } 936 | } 937 | //}}} 938 | function moveto(x, y) //{{{ 939 | { 940 | if (!options.shade) { 941 | $img2.css({ 942 | top: px(-y), 943 | left: px(-x) 944 | }); 945 | } 946 | $sel.css({ 947 | top: px(y), 948 | left: px(x) 949 | }); 950 | } 951 | //}}} 952 | function resize(w, h) //{{{ 953 | { 954 | $sel.width(Math.round(w)).height(Math.round(h)); 955 | } 956 | //}}} 957 | function refresh() //{{{ 958 | { 959 | var c = Coords.getFixed(); 960 | 961 | Coords.setPressed([c.x, c.y]); 962 | Coords.setCurrent([c.x2, c.y2]); 963 | 964 | updateVisible(); 965 | } 966 | //}}} 967 | 968 | // Internal Methods 969 | function updateVisible(select) //{{{ 970 | { 971 | if (awake) { 972 | return update(select); 973 | } 974 | } 975 | //}}} 976 | function update(select) //{{{ 977 | { 978 | var c = Coords.getFixed(); 979 | 980 | resize(c.w, c.h); 981 | moveto(c.x, c.y); 982 | if (options.shade) Shade.updateRaw(c); 983 | 984 | awake || show(); 985 | 986 | if (select) { 987 | options.onSelect.call(api, unscale(c)); 988 | } else { 989 | options.onChange.call(api, unscale(c)); 990 | } 991 | } 992 | //}}} 993 | function setBgOpacity(opacity,force,now) //{{{ 994 | { 995 | if (!awake && !force) return; 996 | if (options.bgFade && !now) { 997 | $img.animate({ 998 | opacity: opacity 999 | },{ 1000 | queue: false, 1001 | duration: options.fadeTime 1002 | }); 1003 | } else { 1004 | $img.css('opacity', opacity); 1005 | } 1006 | } 1007 | //}}} 1008 | function show() //{{{ 1009 | { 1010 | $sel.show(); 1011 | 1012 | if (options.shade) Shade.opacity(bgopacity); 1013 | else setBgOpacity(bgopacity,true); 1014 | 1015 | awake = true; 1016 | } 1017 | //}}} 1018 | function release() //{{{ 1019 | { 1020 | disableHandles(); 1021 | $sel.hide(); 1022 | 1023 | if (options.shade) Shade.opacity(1); 1024 | else setBgOpacity(1); 1025 | 1026 | awake = false; 1027 | options.onRelease.call(api); 1028 | } 1029 | //}}} 1030 | function showHandles() //{{{ 1031 | { 1032 | if (seehandles) { 1033 | $hdl_holder.show(); 1034 | } 1035 | } 1036 | //}}} 1037 | function enableHandles() //{{{ 1038 | { 1039 | seehandles = true; 1040 | if (options.allowResize) { 1041 | $hdl_holder.show(); 1042 | return true; 1043 | } 1044 | } 1045 | //}}} 1046 | function disableHandles() //{{{ 1047 | { 1048 | seehandles = false; 1049 | $hdl_holder.hide(); 1050 | } 1051 | //}}} 1052 | function animMode(v) //{{{ 1053 | { 1054 | if (v) { 1055 | animating = true; 1056 | disableHandles(); 1057 | } else { 1058 | animating = false; 1059 | enableHandles(); 1060 | } 1061 | } 1062 | //}}} 1063 | function done() //{{{ 1064 | { 1065 | animMode(false); 1066 | refresh(); 1067 | } 1068 | //}}} 1069 | // Insert draggable elements {{{ 1070 | // Insert border divs for outline 1071 | 1072 | if (options.dragEdges && $.isArray(options.createDragbars)) 1073 | createDragbars(options.createDragbars); 1074 | 1075 | if ($.isArray(options.createHandles)) 1076 | createHandles(options.createHandles); 1077 | 1078 | if (options.drawBorders && $.isArray(options.createBorders)) 1079 | createBorders(options.createBorders); 1080 | 1081 | //}}} 1082 | 1083 | // This is a hack for iOS5 to support drag/move touch functionality 1084 | $(document).bind('touchstart.jcrop-ios',function(e) { 1085 | if ($(e.currentTarget).hasClass('jcrop-tracker')) e.stopPropagation(); 1086 | }); 1087 | 1088 | var $track = newTracker().mousedown(createDragger('move')).css({ 1089 | cursor: 'move', 1090 | position: 'absolute', 1091 | zIndex: 360 1092 | }); 1093 | 1094 | if (Touch.support) { 1095 | $track.bind('touchstart.jcrop', Touch.createDragger('move')); 1096 | } 1097 | 1098 | $img_holder.append($track); 1099 | disableHandles(); 1100 | 1101 | return { 1102 | updateVisible: updateVisible, 1103 | update: update, 1104 | release: release, 1105 | refresh: refresh, 1106 | isAwake: function () { 1107 | return awake; 1108 | }, 1109 | setCursor: function (cursor) { 1110 | $track.css('cursor', cursor); 1111 | }, 1112 | enableHandles: enableHandles, 1113 | enableOnly: function () { 1114 | seehandles = true; 1115 | }, 1116 | showHandles: showHandles, 1117 | disableHandles: disableHandles, 1118 | animMode: animMode, 1119 | setBgOpacity: setBgOpacity, 1120 | done: done 1121 | }; 1122 | }()); 1123 | 1124 | //}}} 1125 | // Tracker Module {{{ 1126 | var Tracker = (function () { 1127 | var onMove = function () {}, 1128 | onDone = function () {}, 1129 | trackDoc = options.trackDocument; 1130 | 1131 | function toFront(touch) //{{{ 1132 | { 1133 | $trk.css({ 1134 | zIndex: 450 1135 | }); 1136 | 1137 | if (touch) 1138 | $(document) 1139 | .bind('touchmove.jcrop', trackTouchMove) 1140 | .bind('touchend.jcrop', trackTouchEnd); 1141 | 1142 | else if (trackDoc) 1143 | $(document) 1144 | .bind('mousemove.jcrop',trackMove) 1145 | .bind('mouseup.jcrop',trackUp); 1146 | } 1147 | //}}} 1148 | function toBack() //{{{ 1149 | { 1150 | $trk.css({ 1151 | zIndex: 290 1152 | }); 1153 | $(document).unbind('.jcrop'); 1154 | } 1155 | //}}} 1156 | function trackMove(e) //{{{ 1157 | { 1158 | onMove(mouseAbs(e)); 1159 | return false; 1160 | } 1161 | //}}} 1162 | function trackUp(e) //{{{ 1163 | { 1164 | e.preventDefault(); 1165 | e.stopPropagation(); 1166 | 1167 | if (btndown) { 1168 | btndown = false; 1169 | 1170 | onDone(mouseAbs(e)); 1171 | 1172 | if (Selection.isAwake()) { 1173 | options.onSelect.call(api, unscale(Coords.getFixed())); 1174 | } 1175 | 1176 | toBack(); 1177 | onMove = function () {}; 1178 | onDone = function () {}; 1179 | } 1180 | 1181 | return false; 1182 | } 1183 | //}}} 1184 | function activateHandlers(move, done, touch) //{{{ 1185 | { 1186 | btndown = true; 1187 | onMove = move; 1188 | onDone = done; 1189 | toFront(touch); 1190 | return false; 1191 | } 1192 | //}}} 1193 | function trackTouchMove(e) //{{{ 1194 | { 1195 | onMove(mouseAbs(Touch.cfilter(e))); 1196 | return false; 1197 | } 1198 | //}}} 1199 | function trackTouchEnd(e) //{{{ 1200 | { 1201 | return trackUp(Touch.cfilter(e)); 1202 | } 1203 | //}}} 1204 | function setCursor(t) //{{{ 1205 | { 1206 | $trk.css('cursor', t); 1207 | } 1208 | //}}} 1209 | 1210 | if (!trackDoc) { 1211 | $trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp); 1212 | } 1213 | 1214 | $img.before($trk); 1215 | return { 1216 | activateHandlers: activateHandlers, 1217 | setCursor: setCursor 1218 | }; 1219 | }()); 1220 | //}}} 1221 | // KeyManager Module {{{ 1222 | var KeyManager = (function () { 1223 | var $keymgr = $('').css({ 1224 | position: 'fixed', 1225 | left: '-120px', 1226 | width: '12px' 1227 | }).addClass('jcrop-keymgr'), 1228 | 1229 | $keywrap = $('
').css({ 1230 | position: 'absolute', 1231 | overflow: 'hidden' 1232 | }).append($keymgr); 1233 | 1234 | function watchKeys() //{{{ 1235 | { 1236 | if (options.keySupport) { 1237 | $keymgr.show(); 1238 | $keymgr.focus(); 1239 | } 1240 | } 1241 | //}}} 1242 | function onBlur(e) //{{{ 1243 | { 1244 | $keymgr.hide(); 1245 | } 1246 | //}}} 1247 | function doNudge(e, x, y) //{{{ 1248 | { 1249 | if (options.allowMove) { 1250 | Coords.moveOffset([x, y]); 1251 | Selection.updateVisible(true); 1252 | } 1253 | e.preventDefault(); 1254 | e.stopPropagation(); 1255 | } 1256 | //}}} 1257 | function parseKey(e) //{{{ 1258 | { 1259 | if (e.ctrlKey || e.metaKey) { 1260 | return true; 1261 | } 1262 | shift_down = e.shiftKey ? true : false; 1263 | var nudge = shift_down ? 10 : 1; 1264 | 1265 | switch (e.keyCode) { 1266 | case 37: 1267 | doNudge(e, -nudge, 0); 1268 | break; 1269 | case 39: 1270 | doNudge(e, nudge, 0); 1271 | break; 1272 | case 38: 1273 | doNudge(e, 0, -nudge); 1274 | break; 1275 | case 40: 1276 | doNudge(e, 0, nudge); 1277 | break; 1278 | case 27: 1279 | if (options.allowSelect) Selection.release(); 1280 | break; 1281 | case 9: 1282 | return true; 1283 | } 1284 | 1285 | return false; 1286 | } 1287 | //}}} 1288 | 1289 | if (options.keySupport) { 1290 | $keymgr.keydown(parseKey).blur(onBlur); 1291 | if (ie6mode || !options.fixedSupport) { 1292 | $keymgr.css({ 1293 | position: 'absolute', 1294 | left: '-20px' 1295 | }); 1296 | $keywrap.append($keymgr).insertBefore($img); 1297 | } else { 1298 | $keymgr.insertBefore($img); 1299 | } 1300 | } 1301 | 1302 | 1303 | return { 1304 | watchKeys: watchKeys 1305 | }; 1306 | }()); 1307 | //}}} 1308 | // }}} 1309 | // API methods {{{ 1310 | function setClass(cname) //{{{ 1311 | { 1312 | $div.removeClass().addClass(cssClass('holder')).addClass(cname); 1313 | } 1314 | //}}} 1315 | function animateTo(a, callback) //{{{ 1316 | { 1317 | var x1 = a[0] / xscale, 1318 | y1 = a[1] / yscale, 1319 | x2 = a[2] / xscale, 1320 | y2 = a[3] / yscale; 1321 | 1322 | if (animating) { 1323 | return; 1324 | } 1325 | 1326 | var animto = Coords.flipCoords(x1, y1, x2, y2), 1327 | c = Coords.getFixed(), 1328 | initcr = [c.x, c.y, c.x2, c.y2], 1329 | animat = initcr, 1330 | interv = options.animationDelay, 1331 | ix1 = animto[0] - initcr[0], 1332 | iy1 = animto[1] - initcr[1], 1333 | ix2 = animto[2] - initcr[2], 1334 | iy2 = animto[3] - initcr[3], 1335 | pcent = 0, 1336 | velocity = options.swingSpeed; 1337 | 1338 | x1 = animat[0]; 1339 | y1 = animat[1]; 1340 | x2 = animat[2]; 1341 | y2 = animat[3]; 1342 | 1343 | Selection.animMode(true); 1344 | var anim_timer; 1345 | 1346 | function queueAnimator() { 1347 | window.setTimeout(animator, interv); 1348 | } 1349 | var animator = (function () { 1350 | return function () { 1351 | pcent += (100 - pcent) / velocity; 1352 | 1353 | animat[0] = Math.round(x1 + ((pcent / 100) * ix1)); 1354 | animat[1] = Math.round(y1 + ((pcent / 100) * iy1)); 1355 | animat[2] = Math.round(x2 + ((pcent / 100) * ix2)); 1356 | animat[3] = Math.round(y2 + ((pcent / 100) * iy2)); 1357 | 1358 | if (pcent >= 99.8) { 1359 | pcent = 100; 1360 | } 1361 | if (pcent < 100) { 1362 | setSelectRaw(animat); 1363 | queueAnimator(); 1364 | } else { 1365 | Selection.done(); 1366 | Selection.animMode(false); 1367 | if (typeof(callback) === 'function') { 1368 | callback.call(api); 1369 | } 1370 | } 1371 | }; 1372 | }()); 1373 | queueAnimator(); 1374 | } 1375 | //}}} 1376 | function setSelect(rect) //{{{ 1377 | { 1378 | setSelectRaw([rect[0] / xscale, rect[1] / yscale, rect[2] / xscale, rect[3] / yscale]); 1379 | options.onSelect.call(api, unscale(Coords.getFixed())); 1380 | Selection.enableHandles(); 1381 | } 1382 | //}}} 1383 | function setSelectRaw(l) //{{{ 1384 | { 1385 | Coords.setPressed([l[0], l[1]]); 1386 | Coords.setCurrent([l[2], l[3]]); 1387 | Selection.update(); 1388 | } 1389 | //}}} 1390 | function tellSelect() //{{{ 1391 | { 1392 | return unscale(Coords.getFixed()); 1393 | } 1394 | //}}} 1395 | function tellScaled() //{{{ 1396 | { 1397 | return Coords.getFixed(); 1398 | } 1399 | //}}} 1400 | function setOptionsNew(opt) //{{{ 1401 | { 1402 | setOptions(opt); 1403 | interfaceUpdate(); 1404 | } 1405 | //}}} 1406 | function disableCrop() //{{{ 1407 | { 1408 | options.disabled = true; 1409 | Selection.disableHandles(); 1410 | Selection.setCursor('default'); 1411 | Tracker.setCursor('default'); 1412 | } 1413 | //}}} 1414 | function enableCrop() //{{{ 1415 | { 1416 | options.disabled = false; 1417 | interfaceUpdate(); 1418 | } 1419 | //}}} 1420 | function cancelCrop() //{{{ 1421 | { 1422 | Selection.done(); 1423 | Tracker.activateHandlers(null, null); 1424 | } 1425 | //}}} 1426 | function destroy() //{{{ 1427 | { 1428 | $div.remove(); 1429 | $origimg.show(); 1430 | $origimg.css('visibility','visible'); 1431 | $(obj).removeData('Jcrop'); 1432 | } 1433 | //}}} 1434 | function setImage(src, callback) //{{{ 1435 | { 1436 | Selection.release(); 1437 | disableCrop(); 1438 | var img = new Image(); 1439 | img.onload = function () { 1440 | var iw = img.width; 1441 | var ih = img.height; 1442 | var bw = options.boxWidth; 1443 | var bh = options.boxHeight; 1444 | $img.width(iw).height(ih); 1445 | $img.attr('src', src); 1446 | $img2.attr('src', src); 1447 | presize($img, bw, bh); 1448 | boundx = $img.width(); 1449 | boundy = $img.height(); 1450 | $img2.width(boundx).height(boundy); 1451 | $trk.width(boundx + (bound * 2)).height(boundy + (bound * 2)); 1452 | $div.width(boundx).height(boundy); 1453 | Shade.resize(boundx,boundy); 1454 | enableCrop(); 1455 | 1456 | if (typeof(callback) === 'function') { 1457 | callback.call(api); 1458 | } 1459 | }; 1460 | img.src = src; 1461 | } 1462 | //}}} 1463 | function colorChangeMacro($obj,color,now) { 1464 | var mycolor = color || options.bgColor; 1465 | if (options.bgFade && supportsColorFade() && options.fadeTime && !now) { 1466 | $obj.animate({ 1467 | backgroundColor: mycolor 1468 | }, { 1469 | queue: false, 1470 | duration: options.fadeTime 1471 | }); 1472 | } else { 1473 | $obj.css('backgroundColor', mycolor); 1474 | } 1475 | } 1476 | function interfaceUpdate(alt) //{{{ 1477 | // This method tweaks the interface based on options object. 1478 | // Called when options are changed and at end of initialization. 1479 | { 1480 | if (options.allowResize) { 1481 | if (alt) { 1482 | Selection.enableOnly(); 1483 | } else { 1484 | Selection.enableHandles(); 1485 | } 1486 | } else { 1487 | Selection.disableHandles(); 1488 | } 1489 | 1490 | Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default'); 1491 | Selection.setCursor(options.allowMove ? 'move' : 'default'); 1492 | 1493 | if (options.hasOwnProperty('trueSize')) { 1494 | xscale = options.trueSize[0] / boundx; 1495 | yscale = options.trueSize[1] / boundy; 1496 | } 1497 | 1498 | if (options.hasOwnProperty('setSelect')) { 1499 | setSelect(options.setSelect); 1500 | Selection.done(); 1501 | delete(options.setSelect); 1502 | } 1503 | 1504 | Shade.refresh(); 1505 | 1506 | if (options.bgColor != bgcolor) { 1507 | colorChangeMacro( 1508 | options.shade? Shade.getShades(): $div, 1509 | options.shade? 1510 | (options.shadeColor || options.bgColor): 1511 | options.bgColor 1512 | ); 1513 | bgcolor = options.bgColor; 1514 | } 1515 | 1516 | if (bgopacity != options.bgOpacity) { 1517 | bgopacity = options.bgOpacity; 1518 | if (options.shade) Shade.refresh(); 1519 | else Selection.setBgOpacity(bgopacity); 1520 | } 1521 | 1522 | xlimit = options.maxSize[0] || 0; 1523 | ylimit = options.maxSize[1] || 0; 1524 | xmin = options.minSize[0] || 0; 1525 | ymin = options.minSize[1] || 0; 1526 | 1527 | if (options.hasOwnProperty('outerImage')) { 1528 | $img.attr('src', options.outerImage); 1529 | delete(options.outerImage); 1530 | } 1531 | 1532 | Selection.refresh(); 1533 | } 1534 | //}}} 1535 | //}}} 1536 | 1537 | if (Touch.support) $trk.bind('touchstart.jcrop', Touch.newSelection); 1538 | 1539 | $hdl_holder.hide(); 1540 | interfaceUpdate(true); 1541 | 1542 | var api = { 1543 | setImage: setImage, 1544 | animateTo: animateTo, 1545 | setSelect: setSelect, 1546 | setOptions: setOptionsNew, 1547 | tellSelect: tellSelect, 1548 | tellScaled: tellScaled, 1549 | setClass: setClass, 1550 | 1551 | disable: disableCrop, 1552 | enable: enableCrop, 1553 | cancel: cancelCrop, 1554 | release: Selection.release, 1555 | destroy: destroy, 1556 | 1557 | focus: KeyManager.watchKeys, 1558 | 1559 | getBounds: function () { 1560 | return [boundx * xscale, boundy * yscale]; 1561 | }, 1562 | getWidgetSize: function () { 1563 | return [boundx, boundy]; 1564 | }, 1565 | getScaleFactor: function () { 1566 | return [xscale, yscale]; 1567 | }, 1568 | getOptions: function() { 1569 | // careful: internal values are returned 1570 | return options; 1571 | }, 1572 | 1573 | ui: { 1574 | holder: $div, 1575 | selection: $sel 1576 | } 1577 | }; 1578 | 1579 | if (is_msie) $div.bind('selectstart', function () { return false; }); 1580 | 1581 | $origimg.data('Jcrop', api); 1582 | return api; 1583 | }; 1584 | $.fn.Jcrop = function (options, callback) //{{{ 1585 | { 1586 | var api; 1587 | // Iterate over each object, attach Jcrop 1588 | this.each(function () { 1589 | // If we've already attached to this object 1590 | if ($(this).data('Jcrop')) { 1591 | // The API can be requested this way (undocumented) 1592 | if (options === 'api') return $(this).data('Jcrop'); 1593 | // Otherwise, we just reset the options... 1594 | else $(this).data('Jcrop').setOptions(options); 1595 | } 1596 | // If we haven't been attached, preload and attach 1597 | else { 1598 | if (this.tagName == 'IMG') 1599 | $.Jcrop.Loader(this,function(){ 1600 | $(this).css({display:'block',visibility:'hidden'}); 1601 | api = $.Jcrop(this, options); 1602 | if ($.isFunction(callback)) callback.call(api); 1603 | }); 1604 | else { 1605 | $(this).css({display:'block',visibility:'hidden'}); 1606 | api = $.Jcrop(this, options); 1607 | if ($.isFunction(callback)) callback.call(api); 1608 | } 1609 | } 1610 | }); 1611 | 1612 | // Return "this" so the object is chainable (jQuery-style) 1613 | return this; 1614 | }; 1615 | //}}} 1616 | // $.Jcrop.Loader - basic image loader {{{ 1617 | 1618 | $.Jcrop.Loader = function(imgobj,success,error){ 1619 | var $img = $(imgobj), img = $img[0]; 1620 | 1621 | function completeCheck(){ 1622 | if (img.complete) { 1623 | $img.unbind('.jcloader'); 1624 | if ($.isFunction(success)) success.call(img); 1625 | } 1626 | else window.setTimeout(completeCheck,50); 1627 | } 1628 | 1629 | $img 1630 | .bind('load.jcloader',completeCheck) 1631 | .bind('error.jcloader',function(e){ 1632 | $img.unbind('.jcloader'); 1633 | if ($.isFunction(error)) error.call(img); 1634 | }); 1635 | 1636 | if (img.complete && $.isFunction(success)){ 1637 | $img.unbind('.jcloader'); 1638 | success.call(img); 1639 | } 1640 | }; 1641 | 1642 | //}}} 1643 | // Global Defaults {{{ 1644 | $.Jcrop.defaults = { 1645 | 1646 | // Basic Settings 1647 | allowSelect: true, 1648 | allowMove: true, 1649 | allowResize: true, 1650 | 1651 | trackDocument: true, 1652 | 1653 | // Styling Options 1654 | baseClass: 'jcrop', 1655 | addClass: null, 1656 | bgColor: 'black', 1657 | bgOpacity: 0.6, 1658 | bgFade: false, 1659 | borderOpacity: 0.4, 1660 | handleOpacity: 0.5, 1661 | handleSize: null, 1662 | 1663 | aspectRatio: 0, 1664 | keySupport: true, 1665 | createHandles: ['n','s','e','w','nw','ne','se','sw'], 1666 | createDragbars: ['n','s','e','w'], 1667 | createBorders: ['n','s','e','w'], 1668 | drawBorders: true, 1669 | dragEdges: true, 1670 | fixedSupport: true, 1671 | touchSupport: null, 1672 | 1673 | shade: null, 1674 | 1675 | boxWidth: 0, 1676 | boxHeight: 0, 1677 | boundary: 2, 1678 | fadeTime: 400, 1679 | animationDelay: 20, 1680 | swingSpeed: 3, 1681 | 1682 | minSelect: [0, 0], 1683 | maxSize: [0, 0], 1684 | minSize: [0, 0], 1685 | 1686 | // Callbacks / Event Handlers 1687 | onChange: function () {}, 1688 | onSelect: function () {}, 1689 | onDblClick: function () {}, 1690 | onRelease: function () {} 1691 | }; 1692 | 1693 | // }}} 1694 | }(jQuery)); 1695 | -------------------------------------------------------------------------------- /assets/js/jquery.Jcrop.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jquery.Jcrop.min.js v0.9.12 (build:20130202) 3 | * jQuery Image Cropping Plugin - released under MIT License 4 | * Copyright (c) 2008-2013 Tapmodo Interactive LLC 5 | * https://github.com/tapmodo/Jcrop 6 | */ 7 | (function(a){a.Jcrop=function(b,c){function i(a){return Math.round(a)+"px"}function j(a){return d.baseClass+"-"+a}function k(){return a.fx.step.hasOwnProperty("backgroundColor")}function l(b){var c=a(b).offset();return[c.left,c.top]}function m(a){return[a.pageX-e[0],a.pageY-e[1]]}function n(b){typeof b!="object"&&(b={}),d=a.extend(d,b),a.each(["onChange","onSelect","onRelease","onDblClick"],function(a,b){typeof d[b]!="function"&&(d[b]=function(){})})}function o(a,b,c){e=l(D),bc.setCursor(a==="move"?a:a+"-resize");if(a==="move")return bc.activateHandlers(q(b),v,c);var d=_.getFixed(),f=r(a),g=_.getCorner(r(f));_.setPressed(_.getCorner(f)),_.setCurrent(g),bc.activateHandlers(p(a,d),v,c)}function p(a,b){return function(c){if(!d.aspectRatio)switch(a){case"e":c[1]=b.y2;break;case"w":c[1]=b.y2;break;case"n":c[0]=b.x2;break;case"s":c[0]=b.x2}else switch(a){case"e":c[1]=b.y+1;break;case"w":c[1]=b.y+1;break;case"n":c[0]=b.x+1;break;case"s":c[0]=b.x+1}_.setCurrent(c),bb.update()}}function q(a){var b=a;return bd.watchKeys 8 | (),function(a){_.moveOffset([a[0]-b[0],a[1]-b[1]]),b=a,bb.update()}}function r(a){switch(a){case"n":return"sw";case"s":return"nw";case"e":return"nw";case"w":return"ne";case"ne":return"sw";case"nw":return"se";case"se":return"nw";case"sw":return"ne"}}function s(a){return function(b){return d.disabled?!1:a==="move"&&!d.allowMove?!1:(e=l(D),W=!0,o(a,m(b)),b.stopPropagation(),b.preventDefault(),!1)}}function t(a,b,c){var d=a.width(),e=a.height();d>b&&b>0&&(d=b,e=b/a.width()*a.height()),e>c&&c>0&&(e=c,d=c/a.height()*a.width()),T=a.width()/d,U=a.height()/e,a.width(d).height(e)}function u(a){return{x:a.x*T,y:a.y*U,x2:a.x2*T,y2:a.y2*U,w:a.w*T,h:a.h*U}}function v(a){var b=_.getFixed();b.w>d.minSelect[0]&&b.h>d.minSelect[1]?(bb.enableHandles(),bb.done()):bb.release(),bc.setCursor(d.allowSelect?"crosshair":"default")}function w(a){if(d.disabled)return!1;if(!d.allowSelect)return!1;W=!0,e=l(D),bb.disableHandles(),bc.setCursor("crosshair");var b=m(a);return _.setPressed(b),bb.update(),bc.activateHandlers(x,v,a.type.substring 9 | (0,5)==="touch"),bd.watchKeys(),a.stopPropagation(),a.preventDefault(),!1}function x(a){_.setCurrent(a),bb.update()}function y(){var b=a("
").addClass(j("tracker"));return g&&b.css({opacity:0,backgroundColor:"white"}),b}function be(a){G.removeClass().addClass(j("holder")).addClass(a)}function bf(a,b){function t(){window.setTimeout(u,l)}var c=a[0]/T,e=a[1]/U,f=a[2]/T,g=a[3]/U;if(X)return;var h=_.flipCoords(c,e,f,g),i=_.getFixed(),j=[i.x,i.y,i.x2,i.y2],k=j,l=d.animationDelay,m=h[0]-j[0],n=h[1]-j[1],o=h[2]-j[2],p=h[3]-j[3],q=0,r=d.swingSpeed;c=k[0],e=k[1],f=k[2],g=k[3],bb.animMode(!0);var s,u=function(){return function(){q+=(100-q)/r,k[0]=Math.round(c+q/100*m),k[1]=Math.round(e+q/100*n),k[2]=Math.round(f+q/100*o),k[3]=Math.round(g+q/100*p),q>=99.8&&(q=100),q<100?(bh(k),t()):(bb.done(),bb.animMode(!1),typeof b=="function"&&b.call(bs))}}();t()}function bg(a){bh([a[0]/T,a[1]/U,a[2]/T,a[3]/U]),d.onSelect.call(bs,u(_.getFixed())),bb.enableHandles()}function bh(a){_.setPressed([a[0],a[1]]),_.setCurrent([a[2], 10 | a[3]]),bb.update()}function bi(){return u(_.getFixed())}function bj(){return _.getFixed()}function bk(a){n(a),br()}function bl(){d.disabled=!0,bb.disableHandles(),bb.setCursor("default"),bc.setCursor("default")}function bm(){d.disabled=!1,br()}function bn(){bb.done(),bc.activateHandlers(null,null)}function bo(){G.remove(),A.show(),A.css("visibility","visible"),a(b).removeData("Jcrop")}function bp(a,b){bb.release(),bl();var c=new Image;c.onload=function(){var e=c.width,f=c.height,g=d.boxWidth,h=d.boxHeight;D.width(e).height(f),D.attr("src",a),H.attr("src",a),t(D,g,h),E=D.width(),F=D.height(),H.width(E).height(F),M.width(E+L*2).height(F+L*2),G.width(E).height(F),ba.resize(E,F),bm(),typeof b=="function"&&b.call(bs)},c.src=a}function bq(a,b,c){var e=b||d.bgColor;d.bgFade&&k()&&d.fadeTime&&!c?a.animate({backgroundColor:e},{queue:!1,duration:d.fadeTime}):a.css("backgroundColor",e)}function br(a){d.allowResize?a?bb.enableOnly():bb.enableHandles():bb.disableHandles(),bc.setCursor(d.allowSelect?"crosshair":"default"),bb 11 | .setCursor(d.allowMove?"move":"default"),d.hasOwnProperty("trueSize")&&(T=d.trueSize[0]/E,U=d.trueSize[1]/F),d.hasOwnProperty("setSelect")&&(bg(d.setSelect),bb.done(),delete d.setSelect),ba.refresh(),d.bgColor!=N&&(bq(d.shade?ba.getShades():G,d.shade?d.shadeColor||d.bgColor:d.bgColor),N=d.bgColor),O!=d.bgOpacity&&(O=d.bgOpacity,d.shade?ba.refresh():bb.setBgOpacity(O)),P=d.maxSize[0]||0,Q=d.maxSize[1]||0,R=d.minSize[0]||0,S=d.minSize[1]||0,d.hasOwnProperty("outerImage")&&(D.attr("src",d.outerImage),delete d.outerImage),bb.refresh()}var d=a.extend({},a.Jcrop.defaults),e,f=navigator.userAgent.toLowerCase(),g=/msie/.test(f),h=/msie [1-6]\./.test(f);typeof b!="object"&&(b=a(b)[0]),typeof c!="object"&&(c={}),n(c);var z={border:"none",visibility:"visible",margin:0,padding:0,position:"absolute",top:0,left:0},A=a(b),B=!0;if(b.tagName=="IMG"){if(A[0].width!=0&&A[0].height!=0)A.width(A[0].width),A.height(A[0].height);else{var C=new Image;C.src=A[0].src,A.width(C.width),A.height(C.height)}var D=A.clone().removeAttr("id"). 12 | css(z).show();D.width(A.width()),D.height(A.height()),A.after(D).hide()}else D=A.css(z).show(),B=!1,d.shade===null&&(d.shade=!0);t(D,d.boxWidth,d.boxHeight);var E=D.width(),F=D.height(),G=a("
").width(E).height(F).addClass(j("holder")).css({position:"relative",backgroundColor:d.bgColor}).insertAfter(A).append(D);d.addClass&&G.addClass(d.addClass);var H=a("
"),I=a("
").width("100%").height("100%").css({zIndex:310,position:"absolute",overflow:"hidden"}),J=a("
").width("100%").height("100%").css("zIndex",320),K=a("
").css({position:"absolute",zIndex:600}).dblclick(function(){var a=_.getFixed();d.onDblClick.call(bs,a)}).insertBefore(D).append(I,J);B&&(H=a("").attr("src",D.attr("src")).css(z).width(E).height(F),I.append(H)),h&&K.css({overflowY:"hidden"});var L=d.boundary,M=y().width(E+L*2).height(F+L*2).css({position:"absolute",top:i(-L),left:i(-L),zIndex:290}).mousedown(w),N=d.bgColor,O=d.bgOpacity,P,Q,R,S,T,U,V=!0,W,X,Y;e=l(D);var Z=function(){function a(){var a={},b=["touchstart" 13 | ,"touchmove","touchend"],c=document.createElement("div"),d;try{for(d=0;da+f&&(f-=f+a),0>b+g&&(g-=g+b),FE&&(r=E,u=Math.abs((r-a)/f),s=k<0?b-u:u+b)):(r=c,u=l/f,s=k<0?b-u:b+u,s<0?(s=0,t=Math.abs((s-b)*f),r=j<0?a-t:t+a):s>F&&(s=F,t=Math.abs(s-b)*f,r=j<0?a-t:t+a)),r>a?(r-ah&&(r=a+h),s>b?s=b+(r-a)/f:s=b-(r-a)/f):rh&&(r=a-h),s>b?s=b+(a-r)/f:s=b-(a-r)/f),r<0?(a-=r,r=0):r>E&&(a-=r-E,r=E),s<0?(b-=s,s=0):s>F&&(b-=s-F,s=F),q(o(a,b,r,s))}function n(a){return a[0]<0&&(a[0]=0),a[1]<0&&(a[1]=0),a[0]>E&&(a[0]=E),a[1]>F&&(a[1]=F),[Math.round(a[0]),Math.round(a[1])]}function o(a,b,c,d){var e=a,f=c,g=b,h=d;return cP&&(c=d>0?a+P:a-P),Q&&Math.abs 15 | (f)>Q&&(e=f>0?b+Q:b-Q),S/U&&Math.abs(f)0?b+S/U:b-S/U),R/T&&Math.abs(d)0?a+R/T:a-R/T),a<0&&(c-=a,a-=a),b<0&&(e-=b,b-=b),c<0&&(a-=c,c-=c),e<0&&(b-=e,e-=e),c>E&&(g=c-E,a-=g,c-=g),e>F&&(g=e-F,b-=g,e-=g),a>E&&(g=a-F,e-=g,b-=g),b>F&&(g=b-F,e-=g,b-=g),q(o(a,b,c,e))}function q(a){return{x:a[0],y:a[1],x2:a[2],y2:a[3],w:a[2]-a[0],h:a[3]-a[1]}}var a=0,b=0,c=0,e=0,f,g;return{flipCoords:o,setPressed:h,setCurrent:i,getOffset:j,moveOffset:k,getCorner:l,getFixed:m}}(),ba=function(){function f(a,b){e.left.css({height:i(b)}),e.right.css({height:i(b)})}function g(){return h(_.getFixed())}function h(a){e.top.css({left:i(a.x),width:i(a.w),height:i(a.y)}),e.bottom.css({top:i(a.y2),left:i(a.x),width:i(a.w),height:i(F-a.y2)}),e.right.css({left:i(a.x2),width:i(E-a.x2)}),e.left.css({width:i(a.x)})}function j(){return a("
").css({position:"absolute",backgroundColor:d.shadeColor||d.bgColor}).appendTo(c)}function k(){b||(b=!0,c.insertBefore(D),g(),bb.setBgOpacity(1,0,1),H.hide(),l(d.shadeColor||d.bgColor,1),bb. 16 | isAwake()?n(d.bgOpacity,1):n(1,1))}function l(a,b){bq(p(),a,b)}function m(){b&&(c.remove(),H.show(),b=!1,bb.isAwake()?bb.setBgOpacity(d.bgOpacity,1,1):(bb.setBgOpacity(1,1,1),bb.disableHandles()),bq(G,0,1))}function n(a,e){b&&(d.bgFade&&!e?c.animate({opacity:1-a},{queue:!1,duration:d.fadeTime}):c.css({opacity:1-a}))}function o(){d.shade?k():m(),bb.isAwake()&&n(d.bgOpacity)}function p(){return c.children()}var b=!1,c=a("
").css({position:"absolute",zIndex:240,opacity:0}),e={top:j(),left:j().height(F),right:j().height(F),bottom:j()};return{update:g,updateRaw:h,getShades:p,setBgColor:l,enable:k,disable:m,resize:f,refresh:o,opacity:n}}(),bb=function(){function k(b){var c=a("
").css({position:"absolute",opacity:d.borderOpacity}).addClass(j(b));return I.append(c),c}function l(b,c){var d=a("
").mousedown(s(b)).css({cursor:b+"-resize",position:"absolute",zIndex:c}).addClass("ord-"+b);return Z.support&&d.bind("touchstart.jcrop",Z.createDragger(b)),J.append(d),d}function m(a){var b=d.handleSize,e=l(a,c++ 17 | ).css({opacity:d.handleOpacity}).addClass(j("handle"));return b&&e.width(b).height(b),e}function n(a){return l(a,c++).addClass("jcrop-dragbar")}function o(a){var b;for(b=0;b').css({position:"fixed",left:"-120px",width:"12px"}).addClass("jcrop-keymgr"),c=a("
").css({position:"absolute",overflow:"hidden"}).append(b);return d.keySupport&&(b.keydown(i).blur(f),h||!d.fixedSupport?(b.css({position:"absolute",left:"-20px"}),c.append(b).insertBefore(D)):b.insertBefore(D)),{watchKeys:e}}();Z.support&&M.bind("touchstart.jcrop",Z.newSelection),J.hide(),br(!0);var bs={setImage:bp,animateTo:bf,setSelect:bg,setOptions:bk,tellSelect:bi,tellScaled:bj,setClass:be,disable:bl,enable:bm,cancel:bn,release:bb.release,destroy:bo,focus:bd.watchKeys,getBounds:function(){return[E*T,F*U]},getWidgetSize:function(){return[E,F]},getScaleFactor:function(){return[T,U]},getOptions:function(){return d},ui:{holder:G,selection:K}};return g&&G.bind("selectstart",function(){return!1}),A.data("Jcrop",bs),bs},a.fn.Jcrop=function(b,c){var d;return this.each(function(){if(a(this).data("Jcrop")){if( 21 | b==="api")return a(this).data("Jcrop");a(this).data("Jcrop").setOptions(b)}else this.tagName=="IMG"?a.Jcrop.Loader(this,function(){a(this).css({display:"block",visibility:"hidden"}),d=a.Jcrop(this,b),a.isFunction(c)&&c.call(d)}):(a(this).css({display:"block",visibility:"hidden"}),d=a.Jcrop(this,b),a.isFunction(c)&&c.call(d))}),this},a.Jcrop.Loader=function(b,c,d){function g(){f.complete?(e.unbind(".jcloader"),a.isFunction(c)&&c.call(f)):window.setTimeout(g,50)}var e=a(b),f=e[0];e.bind("load.jcloader",g).bind("error.jcloader",function(b){e.unbind(".jcloader"),a.isFunction(d)&&d.call(f)}),f.complete&&a.isFunction(c)&&(e.unbind(".jcloader"),c.call(f))},a.Jcrop.defaults={allowSelect:!0,allowMove:!0,allowResize:!0,trackDocument:!0,baseClass:"jcrop",addClass:null,bgColor:"black",bgOpacity:.6,bgFade:!1,borderOpacity:.4,handleOpacity:.5,handleSize:null,aspectRatio:0,keySupport:!0,createHandles:["n","s","e","w","nw","ne","se","sw"],createDragbars:["n","s","e","w"],createBorders:["n","s","e","w"],drawBorders:!0,dragEdges 22 | :!0,fixedSupport:!0,touchSupport:null,shade:null,boxWidth:0,boxHeight:0,boundary:2,fadeTime:400,animationDelay:20,swingSpeed:3,minSelect:[0,0],maxSize:[0,0],minSize:[0,0],onChange:function(){},onSelect:function(){},onDblClick:function(){},onRelease:function(){}}})(jQuery); -------------------------------------------------------------------------------- /assets/js/jquery.color.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Color Animations v2.0pre 3 | * http://jquery.org/ 4 | * 5 | * Copyright 2011 John Resig 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | * http://jquery.org/license 8 | */ 9 | 10 | (function( jQuery, undefined ){ 11 | var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color outlineColor".split(" "), 12 | 13 | // plusequals test for += 100 -= 100 14 | rplusequals = /^([\-+])=\s*(\d+\.?\d*)/, 15 | // a set of RE's that can match strings and generate color tuples. 16 | stringParsers = [{ 17 | re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/, 18 | parse: function( execResult ) { 19 | return [ 20 | execResult[ 1 ], 21 | execResult[ 2 ], 22 | execResult[ 3 ], 23 | execResult[ 4 ] 24 | ]; 25 | } 26 | }, { 27 | re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/, 28 | parse: function( execResult ) { 29 | return [ 30 | 2.55 * execResult[1], 31 | 2.55 * execResult[2], 32 | 2.55 * execResult[3], 33 | execResult[ 4 ] 34 | ]; 35 | } 36 | }, { 37 | re: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/, 38 | parse: function( execResult ) { 39 | return [ 40 | parseInt( execResult[ 1 ], 16 ), 41 | parseInt( execResult[ 2 ], 16 ), 42 | parseInt( execResult[ 3 ], 16 ) 43 | ]; 44 | } 45 | }, { 46 | re: /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/, 47 | parse: function( execResult ) { 48 | return [ 49 | parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ), 50 | parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ), 51 | parseInt( execResult[ 3 ] + execResult[ 3 ], 16 ) 52 | ]; 53 | } 54 | }, { 55 | re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/, 56 | space: "hsla", 57 | parse: function( execResult ) { 58 | return [ 59 | execResult[1], 60 | execResult[2] / 100, 61 | execResult[3] / 100, 62 | execResult[4] 63 | ]; 64 | } 65 | }], 66 | 67 | // jQuery.Color( ) 68 | color = jQuery.Color = function( color, green, blue, alpha ) { 69 | return new jQuery.Color.fn.parse( color, green, blue, alpha ); 70 | }, 71 | spaces = { 72 | rgba: { 73 | cache: "_rgba", 74 | props: { 75 | red: { 76 | idx: 0, 77 | type: "byte", 78 | empty: true 79 | }, 80 | green: { 81 | idx: 1, 82 | type: "byte", 83 | empty: true 84 | }, 85 | blue: { 86 | idx: 2, 87 | type: "byte", 88 | empty: true 89 | }, 90 | alpha: { 91 | idx: 3, 92 | type: "percent", 93 | def: 1 94 | } 95 | } 96 | }, 97 | hsla: { 98 | cache: "_hsla", 99 | props: { 100 | hue: { 101 | idx: 0, 102 | type: "degrees", 103 | empty: true 104 | }, 105 | saturation: { 106 | idx: 1, 107 | type: "percent", 108 | empty: true 109 | }, 110 | lightness: { 111 | idx: 2, 112 | type: "percent", 113 | empty: true 114 | } 115 | } 116 | } 117 | }, 118 | propTypes = { 119 | "byte": { 120 | floor: true, 121 | min: 0, 122 | max: 255 123 | }, 124 | "percent": { 125 | min: 0, 126 | max: 1 127 | }, 128 | "degrees": { 129 | mod: 360, 130 | floor: true 131 | } 132 | }, 133 | rgbaspace = spaces.rgba.props, 134 | support = color.support = {}, 135 | 136 | // colors = jQuery.Color.names 137 | colors, 138 | 139 | // local aliases of functions called often 140 | each = jQuery.each; 141 | 142 | spaces.hsla.props.alpha = rgbaspace.alpha; 143 | 144 | function clamp( value, prop, alwaysAllowEmpty ) { 145 | var type = propTypes[ prop.type ] || {}, 146 | allowEmpty = prop.empty || alwaysAllowEmpty; 147 | 148 | if ( allowEmpty && value == null ) { 149 | return null; 150 | } 151 | if ( prop.def && value == null ) { 152 | return prop.def; 153 | } 154 | if ( type.floor ) { 155 | value = ~~value; 156 | } else { 157 | value = parseFloat( value ); 158 | } 159 | if ( value == null || isNaN( value ) ) { 160 | return prop.def; 161 | } 162 | if ( type.mod ) { 163 | value = value % type.mod; 164 | // -10 -> 350 165 | return value < 0 ? type.mod + value : value; 166 | } 167 | 168 | // for now all property types without mod have min and max 169 | return type.min > value ? type.min : type.max < value ? type.max : value; 170 | } 171 | 172 | function stringParse( string ) { 173 | var inst = color(), 174 | rgba = inst._rgba = []; 175 | 176 | string = string.toLowerCase(); 177 | 178 | each( stringParsers, function( i, parser ) { 179 | var match = parser.re.exec( string ), 180 | values = match && parser.parse( match ), 181 | parsed, 182 | spaceName = parser.space || "rgba", 183 | cache = spaces[ spaceName ].cache; 184 | 185 | 186 | if ( values ) { 187 | parsed = inst[ spaceName ]( values ); 188 | 189 | // if this was an rgba parse the assignment might happen twice 190 | // oh well.... 191 | inst[ cache ] = parsed[ cache ]; 192 | rgba = inst._rgba = parsed._rgba; 193 | 194 | // exit each( stringParsers ) here because we matched 195 | return false; 196 | } 197 | }); 198 | 199 | // Found a stringParser that handled it 200 | if ( rgba.length !== 0 ) { 201 | 202 | // if this came from a parsed string, force "transparent" when alpha is 0 203 | // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0) 204 | if ( Math.max.apply( Math, rgba ) === 0 ) { 205 | jQuery.extend( rgba, colors.transparent ); 206 | } 207 | return inst; 208 | } 209 | 210 | // named colors / default - filter back through parse function 211 | if ( string = colors[ string ] ) { 212 | return string; 213 | } 214 | } 215 | 216 | color.fn = color.prototype = { 217 | constructor: color, 218 | parse: function( red, green, blue, alpha ) { 219 | if ( red === undefined ) { 220 | this._rgba = [ null, null, null, null ]; 221 | return this; 222 | } 223 | if ( red instanceof jQuery || red.nodeType ) { 224 | red = red instanceof jQuery ? red.css( green ) : jQuery( red ).css( green ); 225 | green = undefined; 226 | } 227 | 228 | var inst = this, 229 | type = jQuery.type( red ), 230 | rgba = this._rgba = [], 231 | source; 232 | 233 | // more than 1 argument specified - assume ( red, green, blue, alpha ) 234 | if ( green !== undefined ) { 235 | red = [ red, green, blue, alpha ]; 236 | type = "array"; 237 | } 238 | 239 | if ( type === "string" ) { 240 | return this.parse( stringParse( red ) || colors._default ); 241 | } 242 | 243 | if ( type === "array" ) { 244 | each( rgbaspace, function( key, prop ) { 245 | rgba[ prop.idx ] = clamp( red[ prop.idx ], prop ); 246 | }); 247 | return this; 248 | } 249 | 250 | if ( type === "object" ) { 251 | if ( red instanceof color ) { 252 | each( spaces, function( spaceName, space ) { 253 | if ( red[ space.cache ] ) { 254 | inst[ space.cache ] = red[ space.cache ].slice(); 255 | } 256 | }); 257 | } else { 258 | each( spaces, function( spaceName, space ) { 259 | each( space.props, function( key, prop ) { 260 | var cache = space.cache; 261 | 262 | // if the cache doesn't exist, and we know how to convert 263 | if ( !inst[ cache ] && space.to ) { 264 | 265 | // if the value was null, we don't need to copy it 266 | // if the key was alpha, we don't need to copy it either 267 | if ( red[ key ] == null || key === "alpha") { 268 | return; 269 | } 270 | inst[ cache ] = space.to( inst._rgba ); 271 | } 272 | 273 | // this is the only case where we allow nulls for ALL properties. 274 | // call clamp with alwaysAllowEmpty 275 | inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true ); 276 | }); 277 | }); 278 | } 279 | return this; 280 | } 281 | }, 282 | is: function( compare ) { 283 | var is = color( compare ), 284 | same = true, 285 | myself = this; 286 | 287 | each( spaces, function( _, space ) { 288 | var isCache = is[ space.cache ], 289 | localCache; 290 | if (isCache) { 291 | localCache = myself[ space.cache ] || space.to && space.to( myself._rgba ) || []; 292 | each( space.props, function( _, prop ) { 293 | if ( isCache[ prop.idx ] != null ) { 294 | same = ( isCache[ prop.idx ] === localCache[ prop.idx ] ); 295 | return same; 296 | } 297 | }); 298 | } 299 | return same; 300 | }); 301 | return same; 302 | }, 303 | _space: function() { 304 | var used = [], 305 | inst = this; 306 | each( spaces, function( spaceName, space ) { 307 | if ( inst[ space.cache ] ) { 308 | used.push( spaceName ); 309 | } 310 | }); 311 | return used.pop(); 312 | }, 313 | transition: function( other, distance ) { 314 | var end = color( other ), 315 | spaceName = end._space(), 316 | space = spaces[ spaceName ], 317 | start = this[ space.cache ] || space.to( this._rgba ), 318 | result = start.slice(); 319 | 320 | end = end[ space.cache ]; 321 | each( space.props, function( key, prop ) { 322 | var index = prop.idx, 323 | startValue = start[ index ], 324 | endValue = end[ index ], 325 | type = propTypes[ prop.type ] || {}; 326 | 327 | // if null, don't override start value 328 | if ( endValue === null ) { 329 | return; 330 | } 331 | // if null - use end 332 | if ( startValue === null ) { 333 | result[ index ] = endValue; 334 | } else { 335 | if ( type.mod ) { 336 | if ( endValue - startValue > type.mod / 2 ) { 337 | startValue += type.mod; 338 | } else if ( startValue - endValue > type.mod / 2 ) { 339 | startValue -= type.mod; 340 | } 341 | } 342 | result[ prop.idx ] = clamp( ( endValue - startValue ) * distance + startValue, prop ); 343 | } 344 | }); 345 | return this[ spaceName ]( result ); 346 | }, 347 | blend: function( opaque ) { 348 | // if we are already opaque - return ourself 349 | if ( this._rgba[ 3 ] === 1 ) { 350 | return this; 351 | } 352 | 353 | var rgb = this._rgba.slice(), 354 | a = rgb.pop(), 355 | blend = color( opaque )._rgba; 356 | 357 | return color( jQuery.map( rgb, function( v, i ) { 358 | return ( 1 - a ) * blend[ i ] + a * v; 359 | })); 360 | }, 361 | toRgbaString: function() { 362 | var prefix = "rgba(", 363 | rgba = jQuery.map( this._rgba, function( v, i ) { 364 | return v == null ? ( i > 2 ? 1 : 0 ) : v; 365 | }); 366 | 367 | if ( rgba[ 3 ] === 1 ) { 368 | rgba.pop(); 369 | prefix = "rgb("; 370 | } 371 | 372 | return prefix + rgba.join(",") + ")"; 373 | }, 374 | toHslaString: function() { 375 | var prefix = "hsla(", 376 | hsla = jQuery.map( this.hsla(), function( v, i ) { 377 | if ( v == null ) { 378 | v = i > 2 ? 1 : 0; 379 | } 380 | 381 | // catch 1 and 2 382 | if ( i && i < 3 ) { 383 | v = Math.round( v * 100 ) + "%"; 384 | } 385 | return v; 386 | }); 387 | 388 | if ( hsla[ 3 ] === 1 ) { 389 | hsla.pop(); 390 | prefix = "hsl("; 391 | } 392 | return prefix + hsla.join(",") + ")"; 393 | }, 394 | toHexString: function( includeAlpha ) { 395 | var rgba = this._rgba.slice(), 396 | alpha = rgba.pop(); 397 | 398 | if ( includeAlpha ) { 399 | rgba.push( ~~( alpha * 255 ) ); 400 | } 401 | 402 | return "#" + jQuery.map( rgba, function( v, i ) { 403 | 404 | // default to 0 when nulls exist 405 | v = ( v || 0 ).toString( 16 ); 406 | return v.length === 1 ? "0" + v : v; 407 | }).join(""); 408 | }, 409 | toString: function() { 410 | return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString(); 411 | } 412 | }; 413 | color.fn.parse.prototype = color.fn; 414 | 415 | // hsla conversions adapted from: 416 | // http://www.google.com/codesearch/p#OAMlx_jo-ck/src/third_party/WebKit/Source/WebCore/inspector/front-end/Color.js&d=7&l=193 417 | 418 | function hue2rgb( p, q, h ) { 419 | h = ( h + 1 ) % 1; 420 | if ( h * 6 < 1 ) { 421 | return p + (q - p) * 6 * h; 422 | } 423 | if ( h * 2 < 1) { 424 | return q; 425 | } 426 | if ( h * 3 < 2 ) { 427 | return p + (q - p) * ((2/3) - h) * 6; 428 | } 429 | return p; 430 | } 431 | 432 | spaces.hsla.to = function ( rgba ) { 433 | if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) { 434 | return [ null, null, null, rgba[ 3 ] ]; 435 | } 436 | var r = rgba[ 0 ] / 255, 437 | g = rgba[ 1 ] / 255, 438 | b = rgba[ 2 ] / 255, 439 | a = rgba[ 3 ], 440 | max = Math.max( r, g, b ), 441 | min = Math.min( r, g, b ), 442 | diff = max - min, 443 | add = max + min, 444 | l = add * 0.5, 445 | h, s; 446 | 447 | if ( min === max ) { 448 | h = 0; 449 | } else if ( r === max ) { 450 | h = ( 60 * ( g - b ) / diff ) + 360; 451 | } else if ( g === max ) { 452 | h = ( 60 * ( b - r ) / diff ) + 120; 453 | } else { 454 | h = ( 60 * ( r - g ) / diff ) + 240; 455 | } 456 | 457 | if ( l === 0 || l === 1 ) { 458 | s = l; 459 | } else if ( l <= 0.5 ) { 460 | s = diff / add; 461 | } else { 462 | s = diff / ( 2 - add ); 463 | } 464 | return [ Math.round(h) % 360, s, l, a == null ? 1 : a ]; 465 | }; 466 | 467 | spaces.hsla.from = function ( hsla ) { 468 | if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) { 469 | return [ null, null, null, hsla[ 3 ] ]; 470 | } 471 | var h = hsla[ 0 ] / 360, 472 | s = hsla[ 1 ], 473 | l = hsla[ 2 ], 474 | a = hsla[ 3 ], 475 | q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s, 476 | p = 2 * l - q, 477 | r, g, b; 478 | 479 | return [ 480 | Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ), 481 | Math.round( hue2rgb( p, q, h ) * 255 ), 482 | Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ), 483 | a 484 | ]; 485 | }; 486 | 487 | 488 | each( spaces, function( spaceName, space ) { 489 | var props = space.props, 490 | cache = space.cache, 491 | to = space.to, 492 | from = space.from; 493 | 494 | // makes rgba() and hsla() 495 | color.fn[ spaceName ] = function( value ) { 496 | 497 | // generate a cache for this space if it doesn't exist 498 | if ( to && !this[ cache ] ) { 499 | this[ cache ] = to( this._rgba ); 500 | } 501 | if ( value === undefined ) { 502 | return this[ cache ].slice(); 503 | } 504 | 505 | var type = jQuery.type( value ), 506 | arr = ( type === "array" || type === "object" ) ? value : arguments, 507 | local = this[ cache ].slice(), 508 | ret; 509 | 510 | each( props, function( key, prop ) { 511 | var val = arr[ type === "object" ? key : prop.idx ]; 512 | if ( val == null ) { 513 | val = local[ prop.idx ]; 514 | } 515 | local[ prop.idx ] = clamp( val, prop ); 516 | }); 517 | 518 | if ( from ) { 519 | ret = color( from( local ) ); 520 | ret[ cache ] = local; 521 | return ret; 522 | } else { 523 | return color( local ); 524 | } 525 | }; 526 | 527 | // makes red() green() blue() alpha() hue() saturation() lightness() 528 | each( props, function( key, prop ) { 529 | // alpha is included in more than one space 530 | if ( color.fn[ key ] ) { 531 | return; 532 | } 533 | color.fn[ key ] = function( value ) { 534 | var vtype = jQuery.type( value ), 535 | fn = ( key === 'alpha' ? ( this._hsla ? 'hsla' : 'rgba' ) : spaceName ), 536 | local = this[ fn ](), 537 | cur = local[ prop.idx ], 538 | match; 539 | 540 | if ( vtype === "undefined" ) { 541 | return cur; 542 | } 543 | 544 | if ( vtype === "function" ) { 545 | value = value.call( this, cur ); 546 | vtype = jQuery.type( value ); 547 | } 548 | if ( value == null && prop.empty ) { 549 | return this; 550 | } 551 | if ( vtype === "string" ) { 552 | match = rplusequals.exec( value ); 553 | if ( match ) { 554 | value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 ); 555 | } 556 | } 557 | local[ prop.idx ] = value; 558 | return this[ fn ]( local ); 559 | }; 560 | }); 561 | }); 562 | 563 | // add .fx.step functions 564 | each( stepHooks, function( i, hook ) { 565 | jQuery.cssHooks[ hook ] = { 566 | set: function( elem, value ) { 567 | var parsed, backgroundColor, curElem; 568 | 569 | if ( jQuery.type( value ) !== 'string' || ( parsed = stringParse( value ) ) ) 570 | { 571 | value = color( parsed || value ); 572 | if ( !support.rgba && value._rgba[ 3 ] !== 1 ) { 573 | curElem = hook === "backgroundColor" ? elem.parentNode : elem; 574 | do { 575 | backgroundColor = jQuery.curCSS( curElem, "backgroundColor" ); 576 | } while ( 577 | ( backgroundColor === "" || backgroundColor === "transparent" ) && 578 | ( curElem = curElem.parentNode ) && 579 | curElem.style 580 | ); 581 | 582 | value = value.blend( backgroundColor && backgroundColor !== "transparent" ? 583 | backgroundColor : 584 | "_default" ); 585 | } 586 | 587 | value = value.toRgbaString(); 588 | } 589 | elem.style[ hook ] = value; 590 | } 591 | }; 592 | jQuery.fx.step[ hook ] = function( fx ) { 593 | if ( !fx.colorInit ) { 594 | fx.start = color( fx.elem, hook ); 595 | fx.end = color( fx.end ); 596 | fx.colorInit = true; 597 | } 598 | jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) ); 599 | }; 600 | }); 601 | 602 | // detect rgba support 603 | jQuery(function() { 604 | var div = document.createElement( "div" ), 605 | div_style = div.style; 606 | 607 | div_style.cssText = "background-color:rgba(1,1,1,.5)"; 608 | support.rgba = div_style.backgroundColor.indexOf( "rgba" ) > -1; 609 | }); 610 | 611 | // Some named colors to work with 612 | // From Interface by Stefan Petre 613 | // http://interface.eyecon.ro/ 614 | colors = jQuery.Color.names = { 615 | aqua: "#00ffff", 616 | azure: "#f0ffff", 617 | beige: "#f5f5dc", 618 | black: "#000000", 619 | blue: "#0000ff", 620 | brown: "#a52a2a", 621 | cyan: "#00ffff", 622 | darkblue: "#00008b", 623 | darkcyan: "#008b8b", 624 | darkgrey: "#a9a9a9", 625 | darkgreen: "#006400", 626 | darkkhaki: "#bdb76b", 627 | darkmagenta: "#8b008b", 628 | darkolivegreen: "#556b2f", 629 | darkorange: "#ff8c00", 630 | darkorchid: "#9932cc", 631 | darkred: "#8b0000", 632 | darksalmon: "#e9967a", 633 | darkviolet: "#9400d3", 634 | fuchsia: "#ff00ff", 635 | gold: "#ffd700", 636 | green: "#008000", 637 | indigo: "#4b0082", 638 | khaki: "#f0e68c", 639 | lightblue: "#add8e6", 640 | lightcyan: "#e0ffff", 641 | lightgreen: "#90ee90", 642 | lightgrey: "#d3d3d3", 643 | lightpink: "#ffb6c1", 644 | lightyellow: "#ffffe0", 645 | lime: "#00ff00", 646 | magenta: "#ff00ff", 647 | maroon: "#800000", 648 | navy: "#000080", 649 | olive: "#808000", 650 | orange: "#ffa500", 651 | pink: "#ffc0cb", 652 | purple: "#800080", 653 | violet: "#800080", 654 | red: "#ff0000", 655 | silver: "#c0c0c0", 656 | white: "#ffffff", 657 | yellow: "#ffff00", 658 | transparent: [ null, null, null, 0 ], 659 | _default: "#ffffff" 660 | }; 661 | })( jQuery ); 662 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "karpoff/yii2-crop-image-upload", 3 | "description": "Yii 2 Crop image upload widget", 4 | "homepage": "https://github.com/karpoff/yii2-crop-image-upload", 5 | "license": "BSD-3-Clause", 6 | "type": "yii2-extension", 7 | "version": "0.3.0", 8 | "keywords": ["yii2", "crop", "image", "upload"], 9 | "authors": [ 10 | { 11 | "name": "Anton Karpov", 12 | "email": "karpoff@bk.ru" 13 | } 14 | ], 15 | "autoload": { 16 | "psr-4": { 17 | "karpoff\\icrop\\": "" 18 | } 19 | }, 20 | "require": { 21 | "yiisoft/yii2": "*", 22 | "yiisoft/yii2-imagine": "*", 23 | "mohorev/yii2-upload-behavior": "*" 24 | } 25 | } --------------------------------------------------------------------------------