├── .gitattributes ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── LICENSE ├── README.md ├── bower.json ├── croppie.css ├── croppie.js ├── croppie.min.js ├── demo ├── cat.jpg ├── demo-1.jpg ├── demo-2.jpg ├── demo-3.jpg ├── demo.css ├── demo.js ├── hero.png ├── prism.css └── prism.js ├── deploy.js ├── index.html ├── package-lock.json ├── package.json └── test └── unit ├── Croppie.js ├── mocha.opts └── stubs ├── DOMTokenList.js ├── EventTarget.js ├── HTMLElement.js ├── document.js └── window.js /.gitattributes: -------------------------------------------------------------------------------- 1 | text eol=lf 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | 4 | ## Actual Behavior 5 | 6 | 7 | ## Steps to Reproduce the Problem 8 | 9 | 1. 10 | 1. 11 | 1. 12 | 13 | ## Example Link 14 | Please recreate your issue using [JSbin](https://www.jsbin.com), [JSFiddle](https://jsfiddle.net), or [Codepen](https://codepen.io). 15 | * Link: 16 | 17 | 18 | ## Specifications 19 | 20 | - Browser: 21 | - Version: 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | npm-debug.log 3 | node_modules/ 4 | bower_components 5 | /nbproject/private/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Foliotek Inc 2 | ------------------------------- 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 6 | persons to whom the Software is furnished to do so, subject to the following conditions: 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 8 | Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 11 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 13 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Croppie - A Javascript Image Cropper 2 | 3 | 4 | ## To Install 5 | Bower: `bower install croppie` 6 | 7 | Npm: `npm install croppie` 8 | 9 | Download: 10 | [croppie.js](croppie.js) & [croppie.css](croppie.css) 11 | 12 | ## Adding croppie to your site 13 | ```html 14 | 15 | 16 | ``` 17 | 18 | ## CDN 19 | cdnjs.com provides croppie via cdn https://cdnjs.com/libraries/croppie 20 | ``` 21 | https://cdnjs.cloudflare.com/ajax/libs/croppie/{version}/croppie.min.css 22 | https://cdnjs.cloudflare.com/ajax/libs/croppie/{version}/croppie.min.js 23 | ``` 24 | 25 | 26 | ## Documentation 27 | [Documentation](http://foliotek.github.io/Croppie#documentation) 28 | 29 | ## Related Libraries 30 | * [croppie-dart](https://gitlab.com/michel.werren/croppie-dart) 31 | * [ngCroppie](https://github.com/allenRoyston/ngCroppie) 32 | * [angular-croppie](https://github.com/lpsBetty/angular-croppie) 33 | * [django-croppie](https://github.com/dima-kov/django-croppie) 34 | * [vue-croppie](https://github.com/jofftiquez/vue-croppie) 35 | 36 | ## Contributing 37 | First, thanks for contributing. This project is difficult to maintain with one person. Here's a "checklist" of things to remember when contributing to croppie. 38 | * Don't forget to update the documentation. 39 | * If you're adding a new option/event/method, try adding to an example on the documentation. Or create a new example, if you feel the need. 40 | * We don't have tests for Croppie :( (if you want to create tests I'd be forever grateful), so please try to test the functionality you're changing on the demo page. I've tried to add as many use-cases as I can think of on there. Compare the functionality in your branch to the one on the official page. If they all still work, then great! 41 | 42 | If you're looking for a simple server to load the demo page, I use https://github.com/tapio/live-server. 43 | 44 | #### Minifying 45 | `uglifyjs croppie.js -c -m -r '$,require,exports' -o croppie.min.js` 46 | 47 | #### Releasing a new version 48 | For the most part, you shouldn't worry about these steps unless you're the one handling the release. Please don't bump the release and don't minify/uglify in a PR. That just creates merge conflicts when merging. Those steps will be performed when the release is created. 49 | 1. Bump version in croppie.js 50 | 2. Minify/Uglify 51 | 3. Commit 52 | 4. npm version [new version] 53 | 5. `git push && git push --tags` 54 | 6. `npm publish` 55 | 7. Draft a new release with new tag on https://github.com/Foliotek/Croppie/releases 56 | 8. Deploy to gh-pages `npm run deploy` 57 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Croppie", 3 | "homepage": "https://github.com/Foliotek/Croppie", 4 | "authors": [ 5 | "Dustin Smith ", 6 | "Ethan Calvert " 7 | ], 8 | "description": "A javascript image cropper", 9 | "main": [ 10 | "croppie.css", 11 | "croppie.js" 12 | ], 13 | "moduleType": [], 14 | "keywords": [ 15 | "image", 16 | "cropper", 17 | "croppie" 18 | ], 19 | "license": "MIT", 20 | "ignore": [ 21 | "**/.*", 22 | "node_modules", 23 | "bower_components", 24 | "demo", 25 | "test", 26 | "tests", 27 | "index.html" 28 | ], 29 | "dependencies": {}, 30 | "devDependencies": { 31 | "sweetalert": "~1.1.3", 32 | "jquery": "~2.1.4", 33 | "exif-js": "~2.1.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /croppie.css: -------------------------------------------------------------------------------- 1 | .croppie-container { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | 6 | .croppie-container .cr-image { 7 | z-index: -1; 8 | position: absolute; 9 | top: 0; 10 | left: 0; 11 | transform-origin: 0 0; 12 | max-height: none; 13 | max-width: none; 14 | } 15 | 16 | .croppie-container .cr-boundary { 17 | position: relative; 18 | overflow: hidden; 19 | margin: 0 auto; 20 | z-index: 1; 21 | width: 100%; 22 | height: 100%; 23 | } 24 | 25 | .croppie-container .cr-viewport, 26 | .croppie-container .cr-resizer { 27 | position: absolute; 28 | border: 2px solid #fff; 29 | margin: auto; 30 | top: 0; 31 | bottom: 0; 32 | right: 0; 33 | left: 0; 34 | box-shadow: 0 0 2000px 2000px rgba(0, 0, 0, 0.5); 35 | z-index: 0; 36 | } 37 | 38 | .croppie-container .cr-resizer { 39 | z-index: 2; 40 | box-shadow: none; 41 | pointer-events: none; 42 | } 43 | 44 | .croppie-container .cr-resizer-vertical, 45 | .croppie-container .cr-resizer-horisontal { 46 | position: absolute; 47 | pointer-events: all; 48 | } 49 | 50 | .croppie-container .cr-resizer-vertical::after, 51 | .croppie-container .cr-resizer-horisontal::after { 52 | display: block; 53 | position: absolute; 54 | box-sizing: border-box; 55 | border: 1px solid black; 56 | background: #fff; 57 | width: 10px; 58 | height: 10px; 59 | content: ''; 60 | } 61 | 62 | .croppie-container .cr-resizer-vertical { 63 | bottom: -5px; 64 | cursor: row-resize; 65 | width: 100%; 66 | height: 10px; 67 | } 68 | 69 | .croppie-container .cr-resizer-vertical::after { 70 | left: 50%; 71 | margin-left: -5px; 72 | } 73 | 74 | .croppie-container .cr-resizer-horisontal { 75 | right: -5px; 76 | cursor: col-resize; 77 | width: 10px; 78 | height: 100%; 79 | } 80 | 81 | .croppie-container .cr-resizer-horisontal::after { 82 | top: 50%; 83 | margin-top: -5px; 84 | } 85 | 86 | .croppie-container .cr-original-image { 87 | display: none; 88 | } 89 | 90 | .croppie-container .cr-vp-circle { 91 | border-radius: 50%; 92 | } 93 | 94 | .croppie-container .cr-overlay { 95 | z-index: 1; 96 | position: absolute; 97 | cursor: move; 98 | touch-action: none; 99 | } 100 | 101 | .croppie-container .cr-slider-wrap { 102 | width: 75%; 103 | margin: 15px auto; 104 | text-align: center; 105 | } 106 | 107 | .croppie-result { 108 | position: relative; 109 | overflow: hidden; 110 | } 111 | 112 | .croppie-result img { 113 | position: absolute; 114 | } 115 | 116 | .croppie-container .cr-image, 117 | .croppie-container .cr-overlay, 118 | .croppie-container .cr-viewport { 119 | -webkit-transform: translateZ(0); 120 | -moz-transform: translateZ(0); 121 | -ms-transform: translateZ(0); 122 | transform: translateZ(0); 123 | } 124 | 125 | /*************************************/ 126 | /***** STYLING RANGE INPUT ***********/ 127 | /*************************************/ 128 | /*http://brennaobrien.com/blog/2014/05/style-input-type-range-in-every-browser.html */ 129 | /*************************************/ 130 | 131 | .cr-slider { 132 | -webkit-appearance: none; 133 | /*removes default webkit styles*/ 134 | /*border: 1px solid white; *//*fix for FF unable to apply focus style bug */ 135 | width: 300px; 136 | /*required for proper track sizing in FF*/ 137 | max-width: 100%; 138 | padding-top: 8px; 139 | padding-bottom: 8px; 140 | background-color: transparent; 141 | } 142 | 143 | .cr-slider::-webkit-slider-runnable-track { 144 | width: 100%; 145 | height: 3px; 146 | background: rgba(0, 0, 0, 0.5); 147 | border: 0; 148 | border-radius: 3px; 149 | } 150 | 151 | .cr-slider::-webkit-slider-thumb { 152 | -webkit-appearance: none; 153 | border: none; 154 | height: 16px; 155 | width: 16px; 156 | border-radius: 50%; 157 | background: #ddd; 158 | margin-top: -6px; 159 | } 160 | 161 | .cr-slider:focus { 162 | outline: none; 163 | } 164 | /* 165 | .cr-slider:focus::-webkit-slider-runnable-track { 166 | background: #ccc; 167 | } 168 | */ 169 | 170 | .cr-slider::-moz-range-track { 171 | width: 100%; 172 | height: 3px; 173 | background: rgba(0, 0, 0, 0.5); 174 | border: 0; 175 | border-radius: 3px; 176 | } 177 | 178 | .cr-slider::-moz-range-thumb { 179 | border: none; 180 | height: 16px; 181 | width: 16px; 182 | border-radius: 50%; 183 | background: #ddd; 184 | margin-top: -6px; 185 | } 186 | 187 | /*hide the outline behind the border*/ 188 | .cr-slider:-moz-focusring { 189 | outline: 1px solid white; 190 | outline-offset: -1px; 191 | } 192 | 193 | .cr-slider::-ms-track { 194 | width: 100%; 195 | height: 5px; 196 | background: transparent; 197 | /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */ 198 | border-color: transparent;/*leave room for the larger thumb to overflow with a transparent border */ 199 | border-width: 6px 0; 200 | color: transparent;/*remove default tick marks*/ 201 | } 202 | .cr-slider::-ms-fill-lower { 203 | background: rgba(0, 0, 0, 0.5); 204 | border-radius: 10px; 205 | } 206 | .cr-slider::-ms-fill-upper { 207 | background: rgba(0, 0, 0, 0.5); 208 | border-radius: 10px; 209 | } 210 | .cr-slider::-ms-thumb { 211 | border: none; 212 | height: 16px; 213 | width: 16px; 214 | border-radius: 50%; 215 | background: #ddd; 216 | margin-top:1px; 217 | } 218 | .cr-slider:focus::-ms-fill-lower { 219 | background: rgba(0, 0, 0, 0.5); 220 | } 221 | .cr-slider:focus::-ms-fill-upper { 222 | background: rgba(0, 0, 0, 0.5); 223 | } 224 | /*******************************************/ 225 | 226 | /***********************************/ 227 | /* Rotation Tools */ 228 | /***********************************/ 229 | .cr-rotate-controls { 230 | position: absolute; 231 | bottom: 5px; 232 | left: 5px; 233 | z-index: 1; 234 | } 235 | .cr-rotate-controls button { 236 | border: 0; 237 | background: none; 238 | } 239 | .cr-rotate-controls i:before { 240 | display: inline-block; 241 | font-style: normal; 242 | font-weight: 900; 243 | font-size: 22px; 244 | } 245 | .cr-rotate-l i:before { 246 | content: '↺'; 247 | } 248 | .cr-rotate-r i:before { 249 | content: '↻'; 250 | } 251 | -------------------------------------------------------------------------------- /croppie.js: -------------------------------------------------------------------------------- 1 | /************************* 2 | * Croppie 3 | * Copyright 2019 4 | * Foliotek 5 | * Version: 2.6.5 6 | *************************/ 7 | (function (root, factory) { 8 | if (typeof define === 'function' && define.amd) { 9 | // AMD. Register as an anonymous module. 10 | define(factory); 11 | } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { 12 | // CommonJS 13 | module.exports = factory(); 14 | } else { 15 | // Browser globals 16 | root.Croppie = factory(); 17 | } 18 | }(typeof self !== 'undefined' ? self : this, function () { 19 | 20 | /* Polyfills */ 21 | if (typeof Promise !== 'function') { 22 | /*! promise-polyfill 3.1.0 */ 23 | !function(a){function b(a,b){return function(){a.apply(b,arguments)}}function c(a){if("object"!==typeof this)throw new TypeError("Promises must be constructed via new");if("function"!==typeof a)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],i(a,b(e,this),b(f,this))}function d(a){var b=this;return null===this._state?void this._deferreds.push(a):void k(function(){var c=b._state?a.onFulfilled:a.onRejected;if(null===c)return void(b._state?a.resolve:a.reject)(b._value);var d;try{d=c(b._value)}catch(e){return void a.reject(e)}a.resolve(d)})}function e(a){try{if(a===this)throw new TypeError("A promise cannot be resolved with itself.");if(a&&("object"===typeof a||"function"===typeof a)){var c=a.then;if("function"===typeof c)return void i(b(c,a),b(e,this),b(f,this))}this._state=!0,this._value=a,g.call(this)}catch(d){f.call(this,d)}}function f(a){this._state=!1,this._value=a,g.call(this)}function g(){for(var a=0,b=this._deferreds.length;b>a;a++)d.call(this,this._deferreds[a]);this._deferreds=null}function h(a,b,c,d){this.onFulfilled="function"===typeof a?a:null,this.onRejected="function"===typeof b?b:null,this.resolve=c,this.reject=d}function i(a,b,c){var d=!1;try{a(function(a){d||(d=!0,b(a))},function(a){d||(d=!0,c(a))})}catch(e){if(d)return;d=!0,c(e)}}var j=setTimeout,k="function"===typeof setImmediate&&setImmediate||function(a){j(a,1)},l=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)};c.prototype["catch"]=function(a){return this.then(null,a)},c.prototype.then=function(a,b){var e=this;return new c(function(c,f){d.call(e,new h(a,b,c,f))})},c.all=function(){var a=Array.prototype.slice.call(1===arguments.length&&l(arguments[0])?arguments[0]:arguments);return new c(function(b,c){function d(f,g){try{if(g&&("object"===typeof g||"function"===typeof g)){var h=g.then;if("function"===typeof h)return void h.call(g,function(a){d(f,a)},c)}a[f]=g,0===--e&&b(a)}catch(i){c(i)}}if(0===a.length)return b([]);for(var e=a.length,f=0;fd;d++)a[d].then(b,c)})},c._setImmediateFn=function(a){k=a},"undefined"!==typeof module&&module.exports?module.exports=c:a.Promise||(a.Promise=c)}(this); 24 | } 25 | 26 | if (typeof window !== 'undefined' && typeof window.CustomEvent !== "function") { 27 | (function(){ 28 | function CustomEvent ( event, params ) { 29 | params = params || { bubbles: false, cancelable: false, detail: undefined }; 30 | var evt = document.createEvent( 'CustomEvent' ); 31 | evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ); 32 | return evt; 33 | } 34 | CustomEvent.prototype = window.Event.prototype; 35 | window.CustomEvent = CustomEvent; 36 | }()); 37 | } 38 | 39 | if (typeof HTMLCanvasElement !== 'undefined' && !HTMLCanvasElement.prototype.toBlob) { 40 | Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', { 41 | value: function (callback, type, quality) { 42 | var binStr = atob( this.toDataURL(type, quality).split(',')[1] ), 43 | len = binStr.length, 44 | arr = new Uint8Array(len); 45 | 46 | for (var i=0; i -1 ? EXIF_NORM : EXIF_FLIP, 86 | index = arr.indexOf(ornt), 87 | offset = (rotate / 90) % arr.length;// 180 = 2%4 = 2 shift exif by 2 indexes 88 | 89 | return arr[(arr.length + index + (offset % arr.length)) % arr.length]; 90 | } 91 | 92 | // Credits to : Andrew Dupont - http://andrewdupont.net/2009/08/28/deep-extending-objects-in-javascript/ 93 | function deepExtend(destination, source) { 94 | destination = destination || {}; 95 | for (var property in source) { 96 | if (source[property] && source[property].constructor && source[property].constructor === Object) { 97 | destination[property] = destination[property] || {}; 98 | deepExtend(destination[property], source[property]); 99 | } else { 100 | destination[property] = source[property]; 101 | } 102 | } 103 | return destination; 104 | } 105 | 106 | function clone(object) { 107 | return deepExtend({}, object); 108 | } 109 | 110 | function debounce(func, wait, immediate) { 111 | var timeout; 112 | return function () { 113 | var context = this, args = arguments; 114 | var later = function () { 115 | timeout = null; 116 | if (!immediate) func.apply(context, args); 117 | }; 118 | var callNow = immediate && !timeout; 119 | clearTimeout(timeout); 120 | timeout = setTimeout(later, wait); 121 | if (callNow) func.apply(context, args); 122 | }; 123 | } 124 | 125 | function dispatchChange(element) { 126 | if ("createEvent" in document) { 127 | var evt = document.createEvent("HTMLEvents"); 128 | evt.initEvent("change", false, true); 129 | element.dispatchEvent(evt); 130 | } 131 | else { 132 | element.fireEvent("onchange"); 133 | } 134 | } 135 | 136 | //http://jsperf.com/vanilla-css 137 | function css(el, styles, val) { 138 | if (typeof (styles) === 'string') { 139 | var tmp = styles; 140 | styles = {}; 141 | styles[tmp] = val; 142 | } 143 | 144 | for (var prop in styles) { 145 | el.style[prop] = styles[prop]; 146 | } 147 | } 148 | 149 | function addClass(el, c) { 150 | if (el.classList) { 151 | el.classList.add(c); 152 | } 153 | else { 154 | el.className += ' ' + c; 155 | } 156 | } 157 | 158 | function removeClass(el, c) { 159 | if (el.classList) { 160 | el.classList.remove(c); 161 | } 162 | else { 163 | el.className = el.className.replace(c, ''); 164 | } 165 | } 166 | 167 | function setAttributes(el, attrs) { 168 | for (var key in attrs) { 169 | el.setAttribute(key, attrs[key]); 170 | } 171 | } 172 | 173 | function num(v) { 174 | return parseInt(v, 10); 175 | } 176 | 177 | /* Utilities */ 178 | function loadImage(src, doExif) { 179 | if (!src) { throw 'Source image missing'; } 180 | 181 | var img = new Image(); 182 | img.style.opacity = '0'; 183 | return new Promise(function (resolve, reject) { 184 | function _resolve() { 185 | img.style.opacity = '1'; 186 | setTimeout(function () { 187 | resolve(img); 188 | }, 1); 189 | } 190 | 191 | img.removeAttribute('crossOrigin'); 192 | if (src.match(/^https?:\/\/|^\/\//)) { 193 | img.setAttribute('crossOrigin', 'anonymous'); 194 | } 195 | 196 | img.onload = function () { 197 | if (doExif) { 198 | EXIF.getData(img, function () { 199 | _resolve(); 200 | }); 201 | } 202 | else { 203 | _resolve(); 204 | } 205 | }; 206 | img.onerror = function (ev) { 207 | img.style.opacity = 1; 208 | setTimeout(function () { 209 | reject(ev); 210 | }, 1); 211 | }; 212 | img.src = src; 213 | }); 214 | } 215 | 216 | function naturalImageDimensions(img, ornt) { 217 | var w = img.naturalWidth; 218 | var h = img.naturalHeight; 219 | var orient = ornt || getExifOrientation(img); 220 | if (orient && orient >= 5) { 221 | var x= w; 222 | w = h; 223 | h = x; 224 | } 225 | return { width: w, height: h }; 226 | } 227 | 228 | /* CSS Transform Prototype */ 229 | var TRANSLATE_OPTS = { 230 | 'translate3d': { 231 | suffix: ', 0px' 232 | }, 233 | 'translate': { 234 | suffix: '' 235 | } 236 | }; 237 | var Transform = function (x, y, scale) { 238 | this.x = parseFloat(x); 239 | this.y = parseFloat(y); 240 | this.scale = parseFloat(scale); 241 | }; 242 | 243 | Transform.parse = function (v) { 244 | if (v.style) { 245 | return Transform.parse(v.style[CSS_TRANSFORM]); 246 | } 247 | else if (v.indexOf('matrix') > -1 || v.indexOf('none') > -1) { 248 | return Transform.fromMatrix(v); 249 | } 250 | else { 251 | return Transform.fromString(v); 252 | } 253 | }; 254 | 255 | Transform.fromMatrix = function (v) { 256 | var vals = v.substring(7).split(','); 257 | if (!vals.length || v === 'none') { 258 | vals = [1, 0, 0, 1, 0, 0]; 259 | } 260 | 261 | return new Transform(num(vals[4]), num(vals[5]), parseFloat(vals[0])); 262 | }; 263 | 264 | Transform.fromString = function (v) { 265 | var values = v.split(') '), 266 | translate = values[0].substring(Croppie.globals.translate.length + 1).split(','), 267 | scale = values.length > 1 ? values[1].substring(6) : 1, 268 | x = translate.length > 1 ? translate[0] : 0, 269 | y = translate.length > 1 ? translate[1] : 0; 270 | 271 | return new Transform(x, y, scale); 272 | }; 273 | 274 | Transform.prototype.toString = function () { 275 | var suffix = TRANSLATE_OPTS[Croppie.globals.translate].suffix || ''; 276 | return Croppie.globals.translate + '(' + this.x + 'px, ' + this.y + 'px' + suffix + ') scale(' + this.scale + ')'; 277 | }; 278 | 279 | var TransformOrigin = function (el) { 280 | if (!el || !el.style[CSS_TRANS_ORG]) { 281 | this.x = 0; 282 | this.y = 0; 283 | return; 284 | } 285 | var css = el.style[CSS_TRANS_ORG].split(' '); 286 | this.x = parseFloat(css[0]); 287 | this.y = parseFloat(css[1]); 288 | }; 289 | 290 | TransformOrigin.prototype.toString = function () { 291 | return this.x + 'px ' + this.y + 'px'; 292 | }; 293 | 294 | function getExifOrientation (img) { 295 | return img.exifdata && img.exifdata.Orientation ? num(img.exifdata.Orientation) : 1; 296 | } 297 | 298 | function drawCanvas(canvas, img, orientation) { 299 | var width = img.width, 300 | height = img.height, 301 | ctx = canvas.getContext('2d'); 302 | 303 | canvas.width = img.width; 304 | canvas.height = img.height; 305 | 306 | ctx.save(); 307 | switch (orientation) { 308 | case 2: 309 | ctx.translate(width, 0); 310 | ctx.scale(-1, 1); 311 | break; 312 | 313 | case 3: 314 | ctx.translate(width, height); 315 | ctx.rotate(180*Math.PI/180); 316 | break; 317 | 318 | case 4: 319 | ctx.translate(0, height); 320 | ctx.scale(1, -1); 321 | break; 322 | 323 | case 5: 324 | canvas.width = height; 325 | canvas.height = width; 326 | ctx.rotate(90*Math.PI/180); 327 | ctx.scale(1, -1); 328 | break; 329 | 330 | case 6: 331 | canvas.width = height; 332 | canvas.height = width; 333 | ctx.rotate(90*Math.PI/180); 334 | ctx.translate(0, -height); 335 | break; 336 | 337 | case 7: 338 | canvas.width = height; 339 | canvas.height = width; 340 | ctx.rotate(-90*Math.PI/180); 341 | ctx.translate(-width, height); 342 | ctx.scale(1, -1); 343 | break; 344 | 345 | case 8: 346 | canvas.width = height; 347 | canvas.height = width; 348 | ctx.translate(0, width); 349 | ctx.rotate(-90*Math.PI/180); 350 | break; 351 | } 352 | ctx.drawImage(img, 0,0, width, height); 353 | ctx.restore(); 354 | } 355 | 356 | /* Private Methods */ 357 | function _create() { 358 | var self = this, 359 | contClass = 'croppie-container', 360 | customViewportClass = self.options.viewport.type ? 'cr-vp-' + self.options.viewport.type : null, 361 | boundary, img, viewport, overlay, bw, bh; 362 | 363 | self.options.useCanvas = self.options.enableOrientation || _hasExif.call(self); 364 | // Properties on class 365 | self.data = {}; 366 | self.elements = {}; 367 | 368 | boundary = self.elements.boundary = document.createElement('div'); 369 | viewport = self.elements.viewport = document.createElement('div'); 370 | img = self.elements.img = document.createElement('img'); 371 | overlay = self.elements.overlay = document.createElement('div'); 372 | 373 | if (self.options.useCanvas) { 374 | self.elements.canvas = document.createElement('canvas'); 375 | self.elements.preview = self.elements.canvas; 376 | } 377 | else { 378 | self.elements.preview = img; 379 | } 380 | 381 | addClass(boundary, 'cr-boundary'); 382 | boundary.setAttribute('aria-dropeffect', 'none'); 383 | bw = self.options.boundary.width; 384 | bh = self.options.boundary.height; 385 | css(boundary, { 386 | width: (bw + (isNaN(bw) ? '' : 'px')), 387 | height: (bh + (isNaN(bh) ? '' : 'px')) 388 | }); 389 | 390 | addClass(viewport, 'cr-viewport'); 391 | if (customViewportClass) { 392 | addClass(viewport, customViewportClass); 393 | } 394 | css(viewport, { 395 | width: self.options.viewport.width + 'px', 396 | height: self.options.viewport.height + 'px' 397 | }); 398 | viewport.setAttribute('tabindex', 0); 399 | 400 | addClass(self.elements.preview, 'cr-image'); 401 | setAttributes(self.elements.preview, { 'alt': 'preview', 'aria-grabbed': 'false' }); 402 | addClass(overlay, 'cr-overlay'); 403 | 404 | self.element.appendChild(boundary); 405 | boundary.appendChild(self.elements.preview); 406 | boundary.appendChild(viewport); 407 | boundary.appendChild(overlay); 408 | 409 | addClass(self.element, contClass); 410 | if (self.options.customClass) { 411 | addClass(self.element, self.options.customClass); 412 | } 413 | 414 | _initDraggable.call(this); 415 | 416 | if (self.options.enableZoom) { 417 | _initializeZoom.call(self); 418 | } 419 | 420 | // if (self.options.enableOrientation) { 421 | // _initRotationControls.call(self); 422 | // } 423 | 424 | if (self.options.enableResize) { 425 | _initializeResize.call(self); 426 | } 427 | } 428 | 429 | // function _initRotationControls () { 430 | // var self = this, 431 | // wrap, btnLeft, btnRight, iLeft, iRight; 432 | 433 | // wrap = document.createElement('div'); 434 | // self.elements.orientationBtnLeft = btnLeft = document.createElement('button'); 435 | // self.elements.orientationBtnRight = btnRight = document.createElement('button'); 436 | 437 | // wrap.appendChild(btnLeft); 438 | // wrap.appendChild(btnRight); 439 | 440 | // iLeft = document.createElement('i'); 441 | // iRight = document.createElement('i'); 442 | // btnLeft.appendChild(iLeft); 443 | // btnRight.appendChild(iRight); 444 | 445 | // addClass(wrap, 'cr-rotate-controls'); 446 | // addClass(btnLeft, 'cr-rotate-l'); 447 | // addClass(btnRight, 'cr-rotate-r'); 448 | 449 | // self.elements.boundary.appendChild(wrap); 450 | 451 | // btnLeft.addEventListener('click', function () { 452 | // self.rotate(-90); 453 | // }); 454 | // btnRight.addEventListener('click', function () { 455 | // self.rotate(90); 456 | // }); 457 | // } 458 | 459 | function _hasExif() { 460 | return this.options.enableExif && window.EXIF; 461 | } 462 | 463 | function _initializeResize () { 464 | var self = this; 465 | var wrap = document.createElement('div'); 466 | var isDragging = false; 467 | var direction; 468 | var originalX; 469 | var originalY; 470 | var minSize = 50; 471 | var maxWidth; 472 | var maxHeight; 473 | var vr; 474 | var hr; 475 | 476 | addClass(wrap, 'cr-resizer'); 477 | css(wrap, { 478 | width: this.options.viewport.width + 'px', 479 | height: this.options.viewport.height + 'px' 480 | }); 481 | 482 | if (this.options.resizeControls.height) { 483 | vr = document.createElement('div'); 484 | addClass(vr, 'cr-resizer-vertical'); 485 | wrap.appendChild(vr); 486 | } 487 | 488 | if (this.options.resizeControls.width) { 489 | hr = document.createElement('div'); 490 | addClass(hr, 'cr-resizer-horisontal'); 491 | wrap.appendChild(hr); 492 | } 493 | 494 | function mouseDown(ev) { 495 | if (ev.button !== undefined && ev.button !== 0) return; 496 | 497 | ev.preventDefault(); 498 | if (isDragging) { 499 | return; 500 | } 501 | 502 | var overlayRect = self.elements.overlay.getBoundingClientRect(); 503 | 504 | isDragging = true; 505 | originalX = ev.pageX; 506 | originalY = ev.pageY; 507 | direction = ev.currentTarget.className.indexOf('vertical') !== -1 ? 'v' : 'h'; 508 | maxWidth = overlayRect.width; 509 | maxHeight = overlayRect.height; 510 | 511 | if (ev.touches) { 512 | var touches = ev.touches[0]; 513 | originalX = touches.pageX; 514 | originalY = touches.pageY; 515 | } 516 | 517 | window.addEventListener('mousemove', mouseMove); 518 | window.addEventListener('touchmove', mouseMove); 519 | window.addEventListener('mouseup', mouseUp); 520 | window.addEventListener('touchend', mouseUp); 521 | document.body.style[CSS_USERSELECT] = 'none'; 522 | } 523 | 524 | function mouseMove(ev) { 525 | var pageX = ev.pageX; 526 | var pageY = ev.pageY; 527 | 528 | ev.preventDefault(); 529 | 530 | if (ev.touches) { 531 | var touches = ev.touches[0]; 532 | pageX = touches.pageX; 533 | pageY = touches.pageY; 534 | } 535 | 536 | var deltaX = pageX - originalX; 537 | var deltaY = pageY - originalY; 538 | var newHeight = self.options.viewport.height + deltaY; 539 | var newWidth = self.options.viewport.width + deltaX; 540 | 541 | if (direction === 'v' && newHeight >= minSize && newHeight <= maxHeight) { 542 | css(wrap, { 543 | height: newHeight + 'px' 544 | }); 545 | 546 | self.options.boundary.height += deltaY; 547 | css(self.elements.boundary, { 548 | height: self.options.boundary.height + 'px' 549 | }); 550 | 551 | self.options.viewport.height += deltaY; 552 | css(self.elements.viewport, { 553 | height: self.options.viewport.height + 'px' 554 | }); 555 | } 556 | else if (direction === 'h' && newWidth >= minSize && newWidth <= maxWidth) { 557 | css(wrap, { 558 | width: newWidth + 'px' 559 | }); 560 | 561 | self.options.boundary.width += deltaX; 562 | css(self.elements.boundary, { 563 | width: self.options.boundary.width + 'px' 564 | }); 565 | 566 | self.options.viewport.width += deltaX; 567 | css(self.elements.viewport, { 568 | width: self.options.viewport.width + 'px' 569 | }); 570 | } 571 | 572 | _updateOverlay.call(self); 573 | _updateZoomLimits.call(self); 574 | _updateCenterPoint.call(self); 575 | _triggerUpdate.call(self); 576 | originalY = pageY; 577 | originalX = pageX; 578 | } 579 | 580 | function mouseUp() { 581 | isDragging = false; 582 | window.removeEventListener('mousemove', mouseMove); 583 | window.removeEventListener('touchmove', mouseMove); 584 | window.removeEventListener('mouseup', mouseUp); 585 | window.removeEventListener('touchend', mouseUp); 586 | document.body.style[CSS_USERSELECT] = ''; 587 | } 588 | 589 | if (vr) { 590 | vr.addEventListener('mousedown', mouseDown); 591 | vr.addEventListener('touchstart', mouseDown); 592 | } 593 | 594 | if (hr) { 595 | hr.addEventListener('mousedown', mouseDown); 596 | hr.addEventListener('touchstart', mouseDown); 597 | } 598 | 599 | this.elements.boundary.appendChild(wrap); 600 | } 601 | 602 | function _setZoomerVal(v) { 603 | if (this.options.enableZoom) { 604 | var z = this.elements.zoomer, 605 | val = fix(v, 4); 606 | 607 | z.value = Math.max(parseFloat(z.min), Math.min(parseFloat(z.max), val)).toString(); 608 | } 609 | } 610 | 611 | function _initializeZoom() { 612 | var self = this, 613 | wrap = self.elements.zoomerWrap = document.createElement('div'), 614 | zoomer = self.elements.zoomer = document.createElement('input'); 615 | 616 | addClass(wrap, 'cr-slider-wrap'); 617 | addClass(zoomer, 'cr-slider'); 618 | zoomer.type = 'range'; 619 | zoomer.step = '0.0001'; 620 | zoomer.value = '1'; 621 | zoomer.style.display = self.options.showZoomer ? '' : 'none'; 622 | zoomer.setAttribute('aria-label', 'zoom'); 623 | 624 | self.element.appendChild(wrap); 625 | wrap.appendChild(zoomer); 626 | 627 | self._currentZoom = 1; 628 | 629 | function change() { 630 | _onZoom.call(self, { 631 | value: parseFloat(zoomer.value), 632 | origin: new TransformOrigin(self.elements.preview), 633 | viewportRect: self.elements.viewport.getBoundingClientRect(), 634 | transform: Transform.parse(self.elements.preview) 635 | }); 636 | } 637 | 638 | function scroll(ev) { 639 | var delta, targetZoom; 640 | 641 | if(self.options.mouseWheelZoom === 'ctrl' && ev.ctrlKey !== true){ 642 | return 0; 643 | } else if (ev.wheelDelta) { 644 | delta = ev.wheelDelta / 1200; //wheelDelta min: -120 max: 120 // max x 10 x 2 645 | } else if (ev.deltaY) { 646 | delta = ev.deltaY / 1060; //deltaY min: -53 max: 53 // max x 10 x 2 647 | } else if (ev.detail) { 648 | delta = ev.detail / -60; //delta min: -3 max: 3 // max x 10 x 2 649 | } else { 650 | delta = 0; 651 | } 652 | 653 | targetZoom = self._currentZoom + (delta * self._currentZoom); 654 | 655 | ev.preventDefault(); 656 | _setZoomerVal.call(self, targetZoom); 657 | change.call(self); 658 | } 659 | 660 | self.elements.zoomer.addEventListener('input', change);// this is being fired twice on keypress 661 | self.elements.zoomer.addEventListener('change', change); 662 | 663 | if (self.options.mouseWheelZoom) { 664 | self.elements.boundary.addEventListener('mousewheel', scroll); 665 | self.elements.boundary.addEventListener('DOMMouseScroll', scroll); 666 | } 667 | } 668 | 669 | function _onZoom(ui) { 670 | var self = this, 671 | transform = ui ? ui.transform : Transform.parse(self.elements.preview), 672 | vpRect = ui ? ui.viewportRect : self.elements.viewport.getBoundingClientRect(), 673 | origin = ui ? ui.origin : new TransformOrigin(self.elements.preview); 674 | 675 | function applyCss() { 676 | var transCss = {}; 677 | transCss[CSS_TRANSFORM] = transform.toString(); 678 | transCss[CSS_TRANS_ORG] = origin.toString(); 679 | css(self.elements.preview, transCss); 680 | } 681 | 682 | self._currentZoom = ui ? ui.value : self._currentZoom; 683 | transform.scale = self._currentZoom; 684 | self.elements.zoomer.setAttribute('aria-valuenow', self._currentZoom); 685 | applyCss(); 686 | 687 | if (self.options.enforceBoundary) { 688 | var boundaries = _getVirtualBoundaries.call(self, vpRect), 689 | transBoundaries = boundaries.translate, 690 | oBoundaries = boundaries.origin; 691 | 692 | if (transform.x >= transBoundaries.maxX) { 693 | origin.x = oBoundaries.minX; 694 | transform.x = transBoundaries.maxX; 695 | } 696 | 697 | if (transform.x <= transBoundaries.minX) { 698 | origin.x = oBoundaries.maxX; 699 | transform.x = transBoundaries.minX; 700 | } 701 | 702 | if (transform.y >= transBoundaries.maxY) { 703 | origin.y = oBoundaries.minY; 704 | transform.y = transBoundaries.maxY; 705 | } 706 | 707 | if (transform.y <= transBoundaries.minY) { 708 | origin.y = oBoundaries.maxY; 709 | transform.y = transBoundaries.minY; 710 | } 711 | } 712 | applyCss(); 713 | _debouncedOverlay.call(self); 714 | _triggerUpdate.call(self); 715 | } 716 | 717 | function _getVirtualBoundaries(viewport) { 718 | var self = this, 719 | scale = self._currentZoom, 720 | vpWidth = viewport.width, 721 | vpHeight = viewport.height, 722 | centerFromBoundaryX = self.elements.boundary.clientWidth / 2, 723 | centerFromBoundaryY = self.elements.boundary.clientHeight / 2, 724 | imgRect = self.elements.preview.getBoundingClientRect(), 725 | curImgWidth = imgRect.width, 726 | curImgHeight = imgRect.height, 727 | halfWidth = vpWidth / 2, 728 | halfHeight = vpHeight / 2; 729 | 730 | var maxX = ((halfWidth / scale) - centerFromBoundaryX) * -1; 731 | var minX = maxX - ((curImgWidth * (1 / scale)) - (vpWidth * (1 / scale))); 732 | 733 | var maxY = ((halfHeight / scale) - centerFromBoundaryY) * -1; 734 | var minY = maxY - ((curImgHeight * (1 / scale)) - (vpHeight * (1 / scale))); 735 | 736 | var originMinX = (1 / scale) * halfWidth; 737 | var originMaxX = (curImgWidth * (1 / scale)) - originMinX; 738 | 739 | var originMinY = (1 / scale) * halfHeight; 740 | var originMaxY = (curImgHeight * (1 / scale)) - originMinY; 741 | 742 | return { 743 | translate: { 744 | maxX: maxX, 745 | minX: minX, 746 | maxY: maxY, 747 | minY: minY 748 | }, 749 | origin: { 750 | maxX: originMaxX, 751 | minX: originMinX, 752 | maxY: originMaxY, 753 | minY: originMinY 754 | } 755 | }; 756 | } 757 | 758 | function _updateCenterPoint(rotate) { 759 | var self = this, 760 | scale = self._currentZoom, 761 | data = self.elements.preview.getBoundingClientRect(), 762 | vpData = self.elements.viewport.getBoundingClientRect(), 763 | transform = Transform.parse(self.elements.preview.style[CSS_TRANSFORM]), 764 | pc = new TransformOrigin(self.elements.preview), 765 | top = (vpData.top - data.top) + (vpData.height / 2), 766 | left = (vpData.left - data.left) + (vpData.width / 2), 767 | center = {}, 768 | adj = {}; 769 | 770 | if (rotate) { 771 | var cx = pc.x; 772 | var cy = pc.y; 773 | var tx = transform.x; 774 | var ty = transform.y; 775 | 776 | center.y = cx; 777 | center.x = cy; 778 | transform.y = tx; 779 | transform.x = ty; 780 | } 781 | else { 782 | center.y = top / scale; 783 | center.x = left / scale; 784 | 785 | adj.y = (center.y - pc.y) * (1 - scale); 786 | adj.x = (center.x - pc.x) * (1 - scale); 787 | 788 | transform.x -= adj.x; 789 | transform.y -= adj.y; 790 | } 791 | 792 | var newCss = {}; 793 | newCss[CSS_TRANS_ORG] = center.x + 'px ' + center.y + 'px'; 794 | newCss[CSS_TRANSFORM] = transform.toString(); 795 | css(self.elements.preview, newCss); 796 | } 797 | 798 | function _initDraggable() { 799 | var self = this, 800 | isDragging = false, 801 | originalX, 802 | originalY, 803 | originalDistance, 804 | vpRect, 805 | transform; 806 | 807 | function assignTransformCoordinates(deltaX, deltaY) { 808 | var imgRect = self.elements.preview.getBoundingClientRect(), 809 | top = transform.y + deltaY, 810 | left = transform.x + deltaX; 811 | 812 | if (self.options.enforceBoundary) { 813 | if (vpRect.top > imgRect.top + deltaY && vpRect.bottom < imgRect.bottom + deltaY) { 814 | transform.y = top; 815 | } 816 | 817 | if (vpRect.left > imgRect.left + deltaX && vpRect.right < imgRect.right + deltaX) { 818 | transform.x = left; 819 | } 820 | } 821 | else { 822 | transform.y = top; 823 | transform.x = left; 824 | } 825 | } 826 | 827 | function toggleGrabState(isDragging) { 828 | self.elements.preview.setAttribute('aria-grabbed', isDragging); 829 | self.elements.boundary.setAttribute('aria-dropeffect', isDragging? 'move': 'none'); 830 | } 831 | 832 | function keyDown(ev) { 833 | var LEFT_ARROW = 37, 834 | UP_ARROW = 38, 835 | RIGHT_ARROW = 39, 836 | DOWN_ARROW = 40; 837 | 838 | if (ev.shiftKey && (ev.keyCode === UP_ARROW || ev.keyCode === DOWN_ARROW)) { 839 | var zoom; 840 | if (ev.keyCode === UP_ARROW) { 841 | zoom = parseFloat(self.elements.zoomer.value) + parseFloat(self.elements.zoomer.step) 842 | } 843 | else { 844 | zoom = parseFloat(self.elements.zoomer.value) - parseFloat(self.elements.zoomer.step) 845 | } 846 | self.setZoom(zoom); 847 | } 848 | else if (self.options.enableKeyMovement && (ev.keyCode >= 37 && ev.keyCode <= 40)) { 849 | ev.preventDefault(); 850 | var movement = parseKeyDown(ev.keyCode); 851 | 852 | transform = Transform.parse(self.elements.preview); 853 | document.body.style[CSS_USERSELECT] = 'none'; 854 | vpRect = self.elements.viewport.getBoundingClientRect(); 855 | keyMove(movement); 856 | } 857 | 858 | function parseKeyDown(key) { 859 | switch (key) { 860 | case LEFT_ARROW: 861 | return [1, 0]; 862 | case UP_ARROW: 863 | return [0, 1]; 864 | case RIGHT_ARROW: 865 | return [-1, 0]; 866 | case DOWN_ARROW: 867 | return [0, -1]; 868 | } 869 | } 870 | } 871 | 872 | function keyMove(movement) { 873 | var deltaX = movement[0], 874 | deltaY = movement[1], 875 | newCss = {}; 876 | 877 | assignTransformCoordinates(deltaX, deltaY); 878 | 879 | newCss[CSS_TRANSFORM] = transform.toString(); 880 | css(self.elements.preview, newCss); 881 | _updateOverlay.call(self); 882 | document.body.style[CSS_USERSELECT] = ''; 883 | _updateCenterPoint.call(self); 884 | _triggerUpdate.call(self); 885 | originalDistance = 0; 886 | } 887 | 888 | function mouseDown(ev) { 889 | if (ev.button !== undefined && ev.button !== 0) return; 890 | 891 | ev.preventDefault(); 892 | if (isDragging) return; 893 | isDragging = true; 894 | originalX = ev.pageX; 895 | originalY = ev.pageY; 896 | 897 | if (ev.touches) { 898 | var touches = ev.touches[0]; 899 | originalX = touches.pageX; 900 | originalY = touches.pageY; 901 | } 902 | toggleGrabState(isDragging); 903 | transform = Transform.parse(self.elements.preview); 904 | window.addEventListener('mousemove', mouseMove); 905 | window.addEventListener('touchmove', mouseMove); 906 | window.addEventListener('mouseup', mouseUp); 907 | window.addEventListener('touchend', mouseUp); 908 | document.body.style[CSS_USERSELECT] = 'none'; 909 | vpRect = self.elements.viewport.getBoundingClientRect(); 910 | } 911 | 912 | function mouseMove(ev) { 913 | ev.preventDefault(); 914 | var pageX = ev.pageX, 915 | pageY = ev.pageY; 916 | 917 | if (ev.touches) { 918 | var touches = ev.touches[0]; 919 | pageX = touches.pageX; 920 | pageY = touches.pageY; 921 | } 922 | 923 | var deltaX = pageX - originalX, 924 | deltaY = pageY - originalY, 925 | newCss = {}; 926 | 927 | if (ev.type === 'touchmove') { 928 | if (ev.touches.length > 1) { 929 | var touch1 = ev.touches[0]; 930 | var touch2 = ev.touches[1]; 931 | var dist = Math.sqrt((touch1.pageX - touch2.pageX) * (touch1.pageX - touch2.pageX) + (touch1.pageY - touch2.pageY) * (touch1.pageY - touch2.pageY)); 932 | 933 | if (!originalDistance) { 934 | originalDistance = dist / self._currentZoom; 935 | } 936 | 937 | var scale = dist / originalDistance; 938 | 939 | _setZoomerVal.call(self, scale); 940 | dispatchChange(self.elements.zoomer); 941 | return; 942 | } 943 | } 944 | 945 | assignTransformCoordinates(deltaX, deltaY); 946 | 947 | newCss[CSS_TRANSFORM] = transform.toString(); 948 | css(self.elements.preview, newCss); 949 | _updateOverlay.call(self); 950 | originalY = pageY; 951 | originalX = pageX; 952 | } 953 | 954 | function mouseUp() { 955 | isDragging = false; 956 | toggleGrabState(isDragging); 957 | window.removeEventListener('mousemove', mouseMove); 958 | window.removeEventListener('touchmove', mouseMove); 959 | window.removeEventListener('mouseup', mouseUp); 960 | window.removeEventListener('touchend', mouseUp); 961 | document.body.style[CSS_USERSELECT] = ''; 962 | _updateCenterPoint.call(self); 963 | _triggerUpdate.call(self); 964 | originalDistance = 0; 965 | } 966 | 967 | self.elements.overlay.addEventListener('mousedown', mouseDown); 968 | self.elements.viewport.addEventListener('keydown', keyDown); 969 | self.elements.overlay.addEventListener('touchstart', mouseDown); 970 | } 971 | 972 | function _updateOverlay() { 973 | if (!this.elements) return; // since this is debounced, it can be fired after destroy 974 | var self = this, 975 | boundRect = self.elements.boundary.getBoundingClientRect(), 976 | imgData = self.elements.preview.getBoundingClientRect(); 977 | 978 | css(self.elements.overlay, { 979 | width: imgData.width + 'px', 980 | height: imgData.height + 'px', 981 | top: (imgData.top - boundRect.top) + 'px', 982 | left: (imgData.left - boundRect.left) + 'px' 983 | }); 984 | } 985 | var _debouncedOverlay = debounce(_updateOverlay, 500); 986 | 987 | function _triggerUpdate() { 988 | var self = this, 989 | data = self.get(); 990 | 991 | if (!_isVisible.call(self)) { 992 | return; 993 | } 994 | 995 | self.options.update.call(self, data); 996 | if (self.$ && typeof Prototype === 'undefined') { 997 | self.$(self.element).trigger('update.croppie', data); 998 | } 999 | else { 1000 | var ev; 1001 | if (window.CustomEvent) { 1002 | ev = new CustomEvent('update', { detail: data }); 1003 | } else { 1004 | ev = document.createEvent('CustomEvent'); 1005 | ev.initCustomEvent('update', true, true, data); 1006 | } 1007 | 1008 | self.element.dispatchEvent(ev); 1009 | } 1010 | } 1011 | 1012 | function _isVisible() { 1013 | return this.elements.preview.offsetHeight > 0 && this.elements.preview.offsetWidth > 0; 1014 | } 1015 | 1016 | function _updatePropertiesFromImage() { 1017 | var self = this, 1018 | initialZoom = 1, 1019 | cssReset = {}, 1020 | img = self.elements.preview, 1021 | imgData, 1022 | transformReset = new Transform(0, 0, initialZoom), 1023 | originReset = new TransformOrigin(), 1024 | isVisible = _isVisible.call(self); 1025 | 1026 | if (!isVisible || self.data.bound) {// if the croppie isn't visible or it doesn't need binding 1027 | return; 1028 | } 1029 | 1030 | self.data.bound = true; 1031 | cssReset[CSS_TRANSFORM] = transformReset.toString(); 1032 | cssReset[CSS_TRANS_ORG] = originReset.toString(); 1033 | cssReset['opacity'] = 1; 1034 | css(img, cssReset); 1035 | 1036 | imgData = self.elements.preview.getBoundingClientRect(); 1037 | 1038 | self._originalImageWidth = imgData.width; 1039 | self._originalImageHeight = imgData.height; 1040 | self.data.orientation = _hasExif.call(self) ? getExifOrientation(self.elements.img) : self.data.orientation; 1041 | 1042 | if (self.options.enableZoom) { 1043 | _updateZoomLimits.call(self, true); 1044 | } 1045 | else { 1046 | self._currentZoom = initialZoom; 1047 | } 1048 | 1049 | transformReset.scale = self._currentZoom; 1050 | cssReset[CSS_TRANSFORM] = transformReset.toString(); 1051 | css(img, cssReset); 1052 | 1053 | if (self.data.points.length) { 1054 | _bindPoints.call(self, self.data.points); 1055 | } 1056 | else { 1057 | _centerImage.call(self); 1058 | } 1059 | 1060 | _updateCenterPoint.call(self); 1061 | _updateOverlay.call(self); 1062 | } 1063 | 1064 | function _updateZoomLimits (initial) { 1065 | var self = this, 1066 | minZoom = Math.max(self.options.minZoom, 0) || 0, 1067 | maxZoom = self.options.maxZoom || 1.5, 1068 | initialZoom, 1069 | defaultInitialZoom, 1070 | zoomer = self.elements.zoomer, 1071 | scale = parseFloat(zoomer.value), 1072 | boundaryData = self.elements.boundary.getBoundingClientRect(), 1073 | imgData = naturalImageDimensions(self.elements.img, self.data.orientation), 1074 | vpData = self.elements.viewport.getBoundingClientRect(), 1075 | minW, 1076 | minH; 1077 | if (self.options.enforceBoundary) { 1078 | minW = vpData.width / imgData.width; 1079 | minH = vpData.height / imgData.height; 1080 | minZoom = Math.max(minW, minH); 1081 | } 1082 | 1083 | if (minZoom >= maxZoom) { 1084 | maxZoom = minZoom + 1; 1085 | } 1086 | 1087 | zoomer.min = fix(minZoom, 4); 1088 | zoomer.max = fix(maxZoom, 4); 1089 | 1090 | if (!initial && (scale < zoomer.min || scale > zoomer.max)) { 1091 | _setZoomerVal.call(self, scale < zoomer.min ? zoomer.min : zoomer.max); 1092 | } 1093 | else if (initial) { 1094 | defaultInitialZoom = Math.max((boundaryData.width / imgData.width), (boundaryData.height / imgData.height)); 1095 | initialZoom = self.data.boundZoom !== null ? self.data.boundZoom : defaultInitialZoom; 1096 | _setZoomerVal.call(self, initialZoom); 1097 | } 1098 | 1099 | dispatchChange(zoomer); 1100 | } 1101 | 1102 | function _bindPoints(points) { 1103 | if (points.length !== 4) { 1104 | throw "Croppie - Invalid number of points supplied: " + points; 1105 | } 1106 | var self = this, 1107 | pointsWidth = points[2] - points[0], 1108 | // pointsHeight = points[3] - points[1], 1109 | vpData = self.elements.viewport.getBoundingClientRect(), 1110 | boundRect = self.elements.boundary.getBoundingClientRect(), 1111 | vpOffset = { 1112 | left: vpData.left - boundRect.left, 1113 | top: vpData.top - boundRect.top 1114 | }, 1115 | scale = vpData.width / pointsWidth, 1116 | originTop = points[1], 1117 | originLeft = points[0], 1118 | transformTop = (-1 * points[1]) + vpOffset.top, 1119 | transformLeft = (-1 * points[0]) + vpOffset.left, 1120 | newCss = {}; 1121 | 1122 | newCss[CSS_TRANS_ORG] = originLeft + 'px ' + originTop + 'px'; 1123 | newCss[CSS_TRANSFORM] = new Transform(transformLeft, transformTop, scale).toString(); 1124 | css(self.elements.preview, newCss); 1125 | 1126 | _setZoomerVal.call(self, scale); 1127 | self._currentZoom = scale; 1128 | } 1129 | 1130 | function _centerImage() { 1131 | var self = this, 1132 | imgDim = self.elements.preview.getBoundingClientRect(), 1133 | vpDim = self.elements.viewport.getBoundingClientRect(), 1134 | boundDim = self.elements.boundary.getBoundingClientRect(), 1135 | vpLeft = vpDim.left - boundDim.left, 1136 | vpTop = vpDim.top - boundDim.top, 1137 | w = vpLeft - ((imgDim.width - vpDim.width) / 2), 1138 | h = vpTop - ((imgDim.height - vpDim.height) / 2), 1139 | transform = new Transform(w, h, self._currentZoom); 1140 | 1141 | css(self.elements.preview, CSS_TRANSFORM, transform.toString()); 1142 | } 1143 | 1144 | function _transferImageToCanvas(customOrientation) { 1145 | var self = this, 1146 | canvas = self.elements.canvas, 1147 | img = self.elements.img, 1148 | ctx = canvas.getContext('2d'); 1149 | 1150 | ctx.clearRect(0, 0, canvas.width, canvas.height); 1151 | canvas.width = img.width; 1152 | canvas.height = img.height; 1153 | 1154 | var orientation = self.options.enableOrientation && customOrientation || getExifOrientation(img); 1155 | drawCanvas(canvas, img, orientation); 1156 | } 1157 | 1158 | function _getCanvas(data) { 1159 | var self = this, 1160 | points = data.points, 1161 | left = num(points[0]), 1162 | top = num(points[1]), 1163 | right = num(points[2]), 1164 | bottom = num(points[3]), 1165 | width = right-left, 1166 | height = bottom-top, 1167 | circle = data.circle, 1168 | canvas = document.createElement('canvas'), 1169 | ctx = canvas.getContext('2d'), 1170 | startX = 0, 1171 | startY = 0, 1172 | canvasWidth = data.outputWidth || width, 1173 | canvasHeight = data.outputHeight || height; 1174 | 1175 | canvas.width = canvasWidth; 1176 | canvas.height = canvasHeight; 1177 | 1178 | if (data.backgroundColor) { 1179 | ctx.fillStyle = data.backgroundColor; 1180 | ctx.fillRect(0, 0, canvasWidth, canvasHeight); 1181 | } 1182 | 1183 | // By default assume we're going to draw the entire 1184 | // source image onto the destination canvas. 1185 | var sx = left, 1186 | sy = top, 1187 | sWidth = width, 1188 | sHeight = height, 1189 | dx = 0, 1190 | dy = 0, 1191 | dWidth = canvasWidth, 1192 | dHeight = canvasHeight; 1193 | 1194 | // 1195 | // Do not go outside of the original image's bounds along the x-axis. 1196 | // Handle translations when projecting onto the destination canvas. 1197 | // 1198 | 1199 | // The smallest possible source x-position is 0. 1200 | if (left < 0) { 1201 | sx = 0; 1202 | dx = (Math.abs(left) / width) * canvasWidth; 1203 | } 1204 | 1205 | // The largest possible source width is the original image's width. 1206 | if (sWidth + sx > self._originalImageWidth) { 1207 | sWidth = self._originalImageWidth - sx; 1208 | dWidth = (sWidth / width) * canvasWidth; 1209 | } 1210 | 1211 | // 1212 | // Do not go outside of the original image's bounds along the y-axis. 1213 | // 1214 | 1215 | // The smallest possible source y-position is 0. 1216 | if (top < 0) { 1217 | sy = 0; 1218 | dy = (Math.abs(top) / height) * canvasHeight; 1219 | } 1220 | 1221 | // The largest possible source height is the original image's height. 1222 | if (sHeight + sy > self._originalImageHeight) { 1223 | sHeight = self._originalImageHeight - sy; 1224 | dHeight = (sHeight / height) * canvasHeight; 1225 | } 1226 | 1227 | // console.table({ left, right, top, bottom, canvasWidth, canvasHeight, width, height, startX, startY, circle, sx, sy, dx, dy, sWidth, sHeight, dWidth, dHeight }); 1228 | 1229 | ctx.drawImage(this.elements.preview, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); 1230 | if (circle) { 1231 | ctx.fillStyle = '#fff'; 1232 | ctx.globalCompositeOperation = 'destination-in'; 1233 | ctx.beginPath(); 1234 | ctx.arc(canvas.width / 2, canvas.height / 2, canvas.width / 2, 0, Math.PI * 2, true); 1235 | ctx.closePath(); 1236 | ctx.fill(); 1237 | } 1238 | return canvas; 1239 | } 1240 | 1241 | function _getHtmlResult(data) { 1242 | var points = data.points, 1243 | div = document.createElement('div'), 1244 | img = document.createElement('img'), 1245 | width = points[2] - points[0], 1246 | height = points[3] - points[1]; 1247 | 1248 | addClass(div, 'croppie-result'); 1249 | div.appendChild(img); 1250 | css(img, { 1251 | left: (-1 * points[0]) + 'px', 1252 | top: (-1 * points[1]) + 'px' 1253 | }); 1254 | img.src = data.url; 1255 | css(div, { 1256 | width: width + 'px', 1257 | height: height + 'px' 1258 | }); 1259 | 1260 | return div; 1261 | } 1262 | 1263 | function _getBase64Result(data) { 1264 | return _getCanvas.call(this, data).toDataURL(data.format, data.quality); 1265 | } 1266 | 1267 | function _getBlobResult(data) { 1268 | var self = this; 1269 | return new Promise(function (resolve) { 1270 | _getCanvas.call(self, data).toBlob(function (blob) { 1271 | resolve(blob); 1272 | }, data.format, data.quality); 1273 | }); 1274 | } 1275 | 1276 | function _replaceImage(img) { 1277 | if (this.elements.img.parentNode) { 1278 | Array.prototype.forEach.call(this.elements.img.classList, function(c) { img.classList.add(c); }); 1279 | this.elements.img.parentNode.replaceChild(img, this.elements.img); 1280 | this.elements.preview = img; // if the img is attached to the DOM, they're not using the canvas 1281 | } 1282 | this.elements.img = img; 1283 | } 1284 | 1285 | function _bind(options, cb) { 1286 | var self = this, 1287 | url, 1288 | points = [], 1289 | zoom = null, 1290 | hasExif = _hasExif.call(self); 1291 | 1292 | if (typeof (options) === 'string') { 1293 | url = options; 1294 | options = {}; 1295 | } 1296 | else if (Array.isArray(options)) { 1297 | points = options.slice(); 1298 | } 1299 | else if (typeof (options) === 'undefined' && self.data.url) { //refreshing 1300 | _updatePropertiesFromImage.call(self); 1301 | _triggerUpdate.call(self); 1302 | return null; 1303 | } 1304 | else { 1305 | url = options.url; 1306 | points = options.points || []; 1307 | zoom = typeof(options.zoom) === 'undefined' ? null : options.zoom; 1308 | } 1309 | 1310 | self.data.bound = false; 1311 | self.data.url = url || self.data.url; 1312 | self.data.boundZoom = zoom; 1313 | 1314 | return loadImage(url, hasExif).then(function (img) { 1315 | _replaceImage.call(self, img); 1316 | if (!points.length) { 1317 | var natDim = naturalImageDimensions(img); 1318 | var rect = self.elements.viewport.getBoundingClientRect(); 1319 | var aspectRatio = rect.width / rect.height; 1320 | var imgAspectRatio = natDim.width / natDim.height; 1321 | var width, height; 1322 | 1323 | if (imgAspectRatio > aspectRatio) { 1324 | height = natDim.height; 1325 | width = height * aspectRatio; 1326 | } 1327 | else { 1328 | width = natDim.width; 1329 | height = natDim.height / aspectRatio; 1330 | } 1331 | 1332 | var x0 = (natDim.width - width) / 2; 1333 | var y0 = (natDim.height - height) / 2; 1334 | var x1 = x0 + width; 1335 | var y1 = y0 + height; 1336 | self.data.points = [x0, y0, x1, y1]; 1337 | } 1338 | else if (self.options.relative) { 1339 | points = [ 1340 | points[0] * img.naturalWidth / 100, 1341 | points[1] * img.naturalHeight / 100, 1342 | points[2] * img.naturalWidth / 100, 1343 | points[3] * img.naturalHeight / 100 1344 | ]; 1345 | } 1346 | 1347 | self.data.orientation = options.orientation || 1; 1348 | self.data.points = points.map(function (p) { 1349 | return parseFloat(p); 1350 | }); 1351 | if (self.options.useCanvas) { 1352 | _transferImageToCanvas.call(self, self.data.orientation); 1353 | } 1354 | _updatePropertiesFromImage.call(self); 1355 | _triggerUpdate.call(self); 1356 | cb && cb(); 1357 | }); 1358 | } 1359 | 1360 | function fix(v, decimalPoints) { 1361 | return parseFloat(v).toFixed(decimalPoints || 0); 1362 | } 1363 | 1364 | function _get() { 1365 | var self = this, 1366 | imgData = self.elements.preview.getBoundingClientRect(), 1367 | vpData = self.elements.viewport.getBoundingClientRect(), 1368 | x1 = vpData.left - imgData.left, 1369 | y1 = vpData.top - imgData.top, 1370 | widthDiff = (vpData.width - self.elements.viewport.offsetWidth) / 2, //border 1371 | heightDiff = (vpData.height - self.elements.viewport.offsetHeight) / 2, 1372 | x2 = x1 + self.elements.viewport.offsetWidth + widthDiff, 1373 | y2 = y1 + self.elements.viewport.offsetHeight + heightDiff, 1374 | scale = self._currentZoom; 1375 | 1376 | if (scale === Infinity || isNaN(scale)) { 1377 | scale = 1; 1378 | } 1379 | 1380 | var max = self.options.enforceBoundary ? 0 : Number.NEGATIVE_INFINITY; 1381 | x1 = Math.max(max, x1 / scale); 1382 | y1 = Math.max(max, y1 / scale); 1383 | x2 = Math.max(max, x2 / scale); 1384 | y2 = Math.max(max, y2 / scale); 1385 | 1386 | return { 1387 | points: [fix(x1), fix(y1), fix(x2), fix(y2)], 1388 | zoom: scale, 1389 | orientation: self.data.orientation 1390 | }; 1391 | } 1392 | 1393 | var RESULT_DEFAULTS = { 1394 | type: 'canvas', 1395 | format: 'png', 1396 | quality: 1 1397 | }, 1398 | RESULT_FORMATS = ['jpeg', 'webp', 'png']; 1399 | 1400 | function _result(options) { 1401 | var self = this, 1402 | data = _get.call(self), 1403 | opts = deepExtend(clone(RESULT_DEFAULTS), clone(options)), 1404 | resultType = (typeof (options) === 'string' ? options : (opts.type || 'base64')), 1405 | size = opts.size || 'viewport', 1406 | format = opts.format, 1407 | quality = opts.quality, 1408 | backgroundColor = opts.backgroundColor, 1409 | circle = typeof opts.circle === 'boolean' ? opts.circle : (self.options.viewport.type === 'circle'), 1410 | vpRect = self.elements.viewport.getBoundingClientRect(), 1411 | ratio = vpRect.width / vpRect.height, 1412 | prom; 1413 | 1414 | if (size === 'viewport') { 1415 | data.outputWidth = vpRect.width; 1416 | data.outputHeight = vpRect.height; 1417 | } else if (typeof size === 'object') { 1418 | if (size.width && size.height) { 1419 | data.outputWidth = size.width; 1420 | data.outputHeight = size.height; 1421 | } else if (size.width) { 1422 | data.outputWidth = size.width; 1423 | data.outputHeight = size.width / ratio; 1424 | } else if (size.height) { 1425 | data.outputWidth = size.height * ratio; 1426 | data.outputHeight = size.height; 1427 | } 1428 | } 1429 | 1430 | if (RESULT_FORMATS.indexOf(format) > -1) { 1431 | data.format = 'image/' + format; 1432 | data.quality = quality; 1433 | } 1434 | 1435 | data.circle = circle; 1436 | data.url = self.data.url; 1437 | data.backgroundColor = backgroundColor; 1438 | 1439 | prom = new Promise(function (resolve) { 1440 | switch(resultType.toLowerCase()) 1441 | { 1442 | case 'rawcanvas': 1443 | resolve(_getCanvas.call(self, data)); 1444 | break; 1445 | case 'canvas': 1446 | case 'base64': 1447 | resolve(_getBase64Result.call(self, data)); 1448 | break; 1449 | case 'blob': 1450 | _getBlobResult.call(self, data).then(resolve); 1451 | break; 1452 | default: 1453 | resolve(_getHtmlResult.call(self, data)); 1454 | break; 1455 | } 1456 | }); 1457 | return prom; 1458 | } 1459 | 1460 | function _refresh() { 1461 | _updatePropertiesFromImage.call(this); 1462 | } 1463 | 1464 | function _rotate(deg) { 1465 | if (!this.options.useCanvas || !this.options.enableOrientation) { 1466 | throw 'Croppie: Cannot rotate without enableOrientation && EXIF.js included'; 1467 | } 1468 | 1469 | var self = this, 1470 | canvas = self.elements.canvas; 1471 | 1472 | self.data.orientation = getExifOffset(self.data.orientation, deg); 1473 | drawCanvas(canvas, self.elements.img, self.data.orientation); 1474 | _updateCenterPoint.call(self, true); 1475 | _updateZoomLimits.call(self); 1476 | 1477 | // Reverses image dimensions if the degrees of rotation is not divisible by 180. 1478 | if ((Math.abs(deg) / 90) % 2 === 1) { 1479 | var oldHeight = self._originalImageHeight; 1480 | var oldWidth = self._originalImageWidth; 1481 | self._originalImageWidth = oldHeight; 1482 | self._originalImageHeight = oldWidth; 1483 | } 1484 | } 1485 | 1486 | function _destroy() { 1487 | var self = this; 1488 | self.element.removeChild(self.elements.boundary); 1489 | removeClass(self.element, 'croppie-container'); 1490 | if (self.options.enableZoom) { 1491 | self.element.removeChild(self.elements.zoomerWrap); 1492 | } 1493 | delete self.elements; 1494 | } 1495 | 1496 | if (typeof window !== 'undefined' && window.jQuery) { 1497 | var $ = window.jQuery; 1498 | $.fn.croppie = function (opts) { 1499 | var ot = typeof opts; 1500 | 1501 | if (ot === 'string') { 1502 | var args = Array.prototype.slice.call(arguments, 1); 1503 | var singleInst = $(this).data('croppie'); 1504 | 1505 | if (opts === 'get') { 1506 | return singleInst.get(); 1507 | } 1508 | else if (opts === 'result') { 1509 | return singleInst.result.apply(singleInst, args); 1510 | } 1511 | else if (opts === 'bind') { 1512 | return singleInst.bind.apply(singleInst, args); 1513 | } 1514 | 1515 | return this.each(function () { 1516 | var i = $(this).data('croppie'); 1517 | if (!i) return; 1518 | 1519 | var method = i[opts]; 1520 | if ($.isFunction(method)) { 1521 | method.apply(i, args); 1522 | if (opts === 'destroy') { 1523 | $(this).removeData('croppie'); 1524 | } 1525 | } 1526 | else { 1527 | throw 'Croppie ' + opts + ' method not found'; 1528 | } 1529 | }); 1530 | } 1531 | else { 1532 | return this.each(function () { 1533 | var i = new Croppie(this, opts); 1534 | i.$ = $; 1535 | $(this).data('croppie', i); 1536 | }); 1537 | } 1538 | }; 1539 | } 1540 | 1541 | function Croppie(element, opts) { 1542 | if (element.className.indexOf('croppie-container') > -1) { 1543 | throw new Error("Croppie: Can't initialize croppie more than once"); 1544 | } 1545 | this.element = element; 1546 | this.options = deepExtend(clone(Croppie.defaults), opts); 1547 | 1548 | if (this.element.tagName.toLowerCase() === 'img') { 1549 | var origImage = this.element; 1550 | addClass(origImage, 'cr-original-image'); 1551 | setAttributes(origImage, {'aria-hidden' : 'true', 'alt' : '' }); 1552 | var replacementDiv = document.createElement('div'); 1553 | this.element.parentNode.appendChild(replacementDiv); 1554 | replacementDiv.appendChild(origImage); 1555 | this.element = replacementDiv; 1556 | this.options.url = this.options.url || origImage.src; 1557 | } 1558 | 1559 | _create.call(this); 1560 | if (this.options.url) { 1561 | var bindOpts = { 1562 | url: this.options.url, 1563 | points: this.options.points 1564 | }; 1565 | delete this.options['url']; 1566 | delete this.options['points']; 1567 | _bind.call(this, bindOpts); 1568 | } 1569 | } 1570 | 1571 | Croppie.defaults = { 1572 | viewport: { 1573 | width: 100, 1574 | height: 100, 1575 | type: 'square' 1576 | }, 1577 | boundary: { }, 1578 | orientationControls: { 1579 | enabled: true, 1580 | leftClass: '', 1581 | rightClass: '' 1582 | }, 1583 | resizeControls: { 1584 | width: true, 1585 | height: true 1586 | }, 1587 | customClass: '', 1588 | showZoomer: true, 1589 | enableZoom: true, 1590 | enableResize: false, 1591 | mouseWheelZoom: true, 1592 | enableExif: false, 1593 | enforceBoundary: true, 1594 | enableOrientation: false, 1595 | enableKeyMovement: true, 1596 | update: function () { } 1597 | }; 1598 | 1599 | Croppie.globals = { 1600 | translate: 'translate3d' 1601 | }; 1602 | 1603 | deepExtend(Croppie.prototype, { 1604 | bind: function (options, cb) { 1605 | return _bind.call(this, options, cb); 1606 | }, 1607 | get: function () { 1608 | var data = _get.call(this); 1609 | var points = data.points; 1610 | if (this.options.relative) { 1611 | points[0] /= this.elements.img.naturalWidth / 100; 1612 | points[1] /= this.elements.img.naturalHeight / 100; 1613 | points[2] /= this.elements.img.naturalWidth / 100; 1614 | points[3] /= this.elements.img.naturalHeight / 100; 1615 | } 1616 | return data; 1617 | }, 1618 | result: function (type) { 1619 | return _result.call(this, type); 1620 | }, 1621 | refresh: function () { 1622 | return _refresh.call(this); 1623 | }, 1624 | setZoom: function (v) { 1625 | _setZoomerVal.call(this, v); 1626 | dispatchChange(this.elements.zoomer); 1627 | }, 1628 | rotate: function (deg) { 1629 | _rotate.call(this, deg); 1630 | }, 1631 | destroy: function () { 1632 | return _destroy.call(this); 1633 | } 1634 | }); 1635 | return Croppie; 1636 | })); 1637 | -------------------------------------------------------------------------------- /croppie.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports&&"string"!=typeof exports.nodeName?module.exports=t():e.Croppie=t()}("undefined"!=typeof self?self:this,function(){"function"!=typeof Promise&&function(e){function t(e,t){return function(){e.apply(t,arguments)}}function i(e){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],s(e,t(o,this),t(r,this))}function n(e){var t=this;return null===this._state?void this._deferreds.push(e):void h(function(){var i=t._state?e.onFulfilled:e.onRejected;if(null!==i){var n;try{n=i(t._value)}catch(t){return void e.reject(t)}e.resolve(n)}else(t._state?e.resolve:e.reject)(t._value)})}function o(e){try{if(e===this)throw new TypeError("A promise cannot be resolved with itself.");if(e&&("object"==typeof e||"function"==typeof e)){var i=e.then;if("function"==typeof i)return void s(t(i,e),t(o,this),t(r,this))}this._state=!0,this._value=e,a.call(this)}catch(e){r.call(this,e)}}function r(e){this._state=!1,this._value=e,a.call(this)}function a(){for(var e=0,t=this._deferreds.length;t>e;e++)n.call(this,this._deferreds[e]);this._deferreds=null}function s(e,t,i){var n=!1;try{e(function(e){n||(n=!0,t(e))},function(e){n||(n=!0,i(e))})}catch(e){if(n)return;n=!0,i(e)}}var l=setTimeout,h="function"==typeof setImmediate&&setImmediate||function(e){l(e,1)},u=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)};i.prototype.catch=function(e){return this.then(null,e)},i.prototype.then=function(e,t){var o=this;return new i(function(i,r){n.call(o,new function(e,t,i,n){this.onFulfilled="function"==typeof e?e:null,this.onRejected="function"==typeof t?t:null,this.resolve=i,this.reject=n}(e,t,i,r))})},i.all=function(){var e=Array.prototype.slice.call(1===arguments.length&&u(arguments[0])?arguments[0]:arguments);return new i(function(t,i){function n(r,a){try{if(a&&("object"==typeof a||"function"==typeof a)){var s=a.then;if("function"==typeof s)return void s.call(a,function(e){n(r,e)},i)}e[r]=a,0==--o&&t(e)}catch(e){i(e)}}if(0===e.length)return t([]);for(var o=e.length,r=0;rn;n++)e[n].then(t,i)})},i._setImmediateFn=function(e){h=e},"undefined"!=typeof module&&module.exports?module.exports=i:e.Promise||(e.Promise=i)}(this),"undefined"!=typeof window&&"function"!=typeof window.CustomEvent&&function(){function e(e,t){t=t||{bubbles:!1,cancelable:!1,detail:void 0};var i=document.createEvent("CustomEvent");return i.initCustomEvent(e,t.bubbles,t.cancelable,t.detail),i}e.prototype=window.Event.prototype,window.CustomEvent=e}(),"undefined"==typeof HTMLCanvasElement||HTMLCanvasElement.prototype.toBlob||Object.defineProperty(HTMLCanvasElement.prototype,"toBlob",{value:function(e,t,i){for(var n=atob(this.toDataURL(t,i).split(",")[1]),o=n.length,r=new Uint8Array(o),a=0;a=5){var r=i;i=n,n=r}return{width:i,height:n}}t=s("transform"),e=s("transformOrigin"),i=s("userSelect");var v={translate3d:{suffix:", 0px"},translate:{suffix:""}},g=function(e,t,i){this.x=parseFloat(e),this.y=parseFloat(t),this.scale=parseFloat(i)};g.parse=function(e){return e.style?g.parse(e.style[t]):e.indexOf("matrix")>-1||e.indexOf("none")>-1?g.fromMatrix(e):g.fromString(e)},g.fromMatrix=function(e){var t=e.substring(7).split(",");return t.length&&"none"!==e||(t=[1,0,0,1,0,0]),new g(m(t[4]),m(t[5]),parseFloat(t[0]))},g.fromString=function(e){var t=e.split(") "),i=t[0].substring(T.globals.translate.length+1).split(","),n=t.length>1?t[1].substring(6):1,o=i.length>1?i[0]:0,r=i.length>1?i[1]:0;return new g(o,r,n)},g.prototype.toString=function(){var e=v[T.globals.translate].suffix||"";return T.globals.translate+"("+this.x+"px, "+this.y+"px"+e+") scale("+this.scale+")"};var w=function(t){if(!t||!t.style[e])return this.x=0,void(this.y=0);var i=t.style[e].split(" ");this.x=parseFloat(i[0]),this.y=parseFloat(i[1])};function y(e){return e.exifdata&&e.exifdata.Orientation?m(e.exifdata.Orientation):1}function b(e,t,i){var n=t.width,o=t.height,r=e.getContext("2d");switch(e.width=t.width,e.height=t.height,r.save(),i){case 2:r.translate(n,0),r.scale(-1,1);break;case 3:r.translate(n,o),r.rotate(180*Math.PI/180);break;case 4:r.translate(0,o),r.scale(1,-1);break;case 5:e.width=o,e.height=n,r.rotate(90*Math.PI/180),r.scale(1,-1);break;case 6:e.width=o,e.height=n,r.rotate(90*Math.PI/180),r.translate(0,-o);break;case 7:e.width=o,e.height=n,r.rotate(-90*Math.PI/180),r.translate(-n,o),r.scale(1,-1);break;case 8:e.width=o,e.height=n,r.translate(0,n),r.rotate(-90*Math.PI/180)}r.drawImage(t,0,0,n,o),r.restore()}function x(){var n,o,r,a,s,l,h=this.options.viewport.type?"cr-vp-"+this.options.viewport.type:null;this.options.useCanvas=this.options.enableOrientation||C.call(this),this.data={},this.elements={},n=this.elements.boundary=document.createElement("div"),r=this.elements.viewport=document.createElement("div"),o=this.elements.img=document.createElement("img"),a=this.elements.overlay=document.createElement("div"),this.options.useCanvas?(this.elements.canvas=document.createElement("canvas"),this.elements.preview=this.elements.canvas):this.elements.preview=o,p(n,"cr-boundary"),n.setAttribute("aria-dropeffect","none"),s=this.options.boundary.width,l=this.options.boundary.height,c(n,{width:s+(isNaN(s)?"":"px"),height:l+(isNaN(l)?"":"px")}),p(r,"cr-viewport"),h&&p(r,h),c(r,{width:this.options.viewport.width+"px",height:this.options.viewport.height+"px"}),r.setAttribute("tabindex",0),p(this.elements.preview,"cr-image"),d(this.elements.preview,{alt:"preview","aria-grabbed":"false"}),p(a,"cr-overlay"),this.element.appendChild(n),n.appendChild(this.elements.preview),n.appendChild(r),n.appendChild(a),p(this.element,"croppie-container"),this.options.customClass&&p(this.element,this.options.customClass),function(){var e,n,o,r,a,s=this,l=!1;function h(e,t){var i=s.elements.preview.getBoundingClientRect(),n=a.y+t,o=a.x+e;s.options.enforceBoundary?(r.top>i.top+t&&r.bottomi.left+e&&r.right1){var v=i.touches[0],g=i.touches[1],w=Math.sqrt((v.pageX-g.pageX)*(v.pageX-g.pageX)+(v.pageY-g.pageY)*(v.pageY-g.pageY));o||(o=w/s._currentZoom);var y=w/o;return E.call(s,y),void u(s.elements.zoomer)}h(d,m),f[t]=a.toString(),c(s.elements.preview,f),L.call(s),n=l,e=r}function f(){p(l=!1),window.removeEventListener("mousemove",m),window.removeEventListener("touchmove",m),window.removeEventListener("mouseup",f),window.removeEventListener("touchend",f),document.body.style[i]="",_.call(s),z.call(s),o=0}s.elements.overlay.addEventListener("mousedown",d),s.elements.viewport.addEventListener("keydown",function(e){var n=37,l=38,u=39,p=40;if(!e.shiftKey||e.keyCode!==l&&e.keyCode!==p){if(s.options.enableKeyMovement&&e.keyCode>=37&&e.keyCode<=40){e.preventDefault();var d=function(e){switch(e){case n:return[1,0];case l:return[0,1];case u:return[-1,0];case p:return[0,-1]}}(e.keyCode);a=g.parse(s.elements.preview),document.body.style[i]="none",r=s.elements.viewport.getBoundingClientRect(),function(e){var n=e[0],r=e[1],l={};h(n,r),l[t]=a.toString(),c(s.elements.preview,l),L.call(s),document.body.style[i]="",_.call(s),z.call(s),o=0}(d)}}else{var m;m=e.keyCode===l?parseFloat(s.elements.zoomer.value)+parseFloat(s.elements.zoomer.step):parseFloat(s.elements.zoomer.value)-parseFloat(s.elements.zoomer.step),s.setZoom(m)}}),s.elements.overlay.addEventListener("touchstart",d)}.call(this),this.options.enableZoom&&function(){var i=this,n=i.elements.zoomerWrap=document.createElement("div"),o=i.elements.zoomer=document.createElement("input");function r(){(function(i){var n=this,o=i?i.transform:g.parse(n.elements.preview),r=i?i.viewportRect:n.elements.viewport.getBoundingClientRect(),a=i?i.origin:new w(n.elements.preview);function s(){var i={};i[t]=o.toString(),i[e]=a.toString(),c(n.elements.preview,i)}if(n._currentZoom=i?i.value:n._currentZoom,o.scale=n._currentZoom,n.elements.zoomer.setAttribute("aria-valuenow",n._currentZoom),s(),n.options.enforceBoundary){var l=function(e){var t=this._currentZoom,i=e.width,n=e.height,o=this.elements.boundary.clientWidth/2,r=this.elements.boundary.clientHeight/2,a=this.elements.preview.getBoundingClientRect(),s=a.width,l=a.height,h=i/2,u=n/2,c=-1*(h/t-o),p=-1*(u/t-r),d=1/t*h,m=1/t*u;return{translate:{maxX:c,minX:c-(s*(1/t)-i*(1/t)),maxY:p,minY:p-(l*(1/t)-n*(1/t))},origin:{maxX:s*(1/t)-d,minX:d,maxY:l*(1/t)-m,minY:m}}}.call(n,r),h=l.translate,u=l.origin;o.x>=h.maxX&&(a.x=u.minX,o.x=h.maxX),o.x<=h.minX&&(a.x=u.maxX,o.x=h.minX),o.y>=h.maxY&&(a.y=u.minY,o.y=h.maxY),o.y<=h.minY&&(a.y=u.maxY,o.y=h.minY)}s(),M.call(n),z.call(n)}).call(i,{value:parseFloat(o.value),origin:new w(i.elements.preview),viewportRect:i.elements.viewport.getBoundingClientRect(),transform:g.parse(i.elements.preview)})}function a(e){var t,n;if("ctrl"===i.options.mouseWheelZoom&&!0!==e.ctrlKey)return 0;t=e.wheelDelta?e.wheelDelta/1200:e.deltaY?e.deltaY/1060:e.detail?e.detail/-60:0,n=i._currentZoom+t*i._currentZoom,e.preventDefault(),E.call(i,n),r.call(i)}p(n,"cr-slider-wrap"),p(o,"cr-slider"),o.type="range",o.step="0.0001",o.value="1",o.style.display=i.options.showZoomer?"":"none",o.setAttribute("aria-label","zoom"),i.element.appendChild(n),n.appendChild(o),i._currentZoom=1,i.elements.zoomer.addEventListener("input",r),i.elements.zoomer.addEventListener("change",r),i.options.mouseWheelZoom&&(i.elements.boundary.addEventListener("mousewheel",a),i.elements.boundary.addEventListener("DOMMouseScroll",a))}.call(this),this.options.enableResize&&function(){var e,t,n,o,r,a,s,l=this,h=document.createElement("div"),u=!1,d=50;p(h,"cr-resizer"),c(h,{width:this.options.viewport.width+"px",height:this.options.viewport.height+"px"}),this.options.resizeControls.height&&(p(a=document.createElement("div"),"cr-resizer-vertical"),h.appendChild(a));this.options.resizeControls.width&&(p(s=document.createElement("div"),"cr-resizer-horisontal"),h.appendChild(s));function m(a){if((void 0===a.button||0===a.button)&&(a.preventDefault(),!u)){var s=l.elements.overlay.getBoundingClientRect();if(u=!0,t=a.pageX,n=a.pageY,e=-1!==a.currentTarget.className.indexOf("vertical")?"v":"h",o=s.width,r=s.height,a.touches){var h=a.touches[0];t=h.pageX,n=h.pageY}window.addEventListener("mousemove",f),window.addEventListener("touchmove",f),window.addEventListener("mouseup",v),window.addEventListener("touchend",v),document.body.style[i]="none"}}function f(i){var a=i.pageX,s=i.pageY;if(i.preventDefault(),i.touches){var u=i.touches[0];a=u.pageX,s=u.pageY}var p=a-t,m=s-n,f=l.options.viewport.height+m,v=l.options.viewport.width+p;"v"===e&&f>=d&&f<=r?(c(h,{height:f+"px"}),l.options.boundary.height+=m,c(l.elements.boundary,{height:l.options.boundary.height+"px"}),l.options.viewport.height+=m,c(l.elements.viewport,{height:l.options.viewport.height+"px"})):"h"===e&&v>=d&&v<=o&&(c(h,{width:v+"px"}),l.options.boundary.width+=p,c(l.elements.boundary,{width:l.options.boundary.width+"px"}),l.options.viewport.width+=p,c(l.elements.viewport,{width:l.options.viewport.width+"px"})),L.call(l),X.call(l),_.call(l),z.call(l),n=s,t=a}function v(){u=!1,window.removeEventListener("mousemove",f),window.removeEventListener("touchmove",f),window.removeEventListener("mouseup",v),window.removeEventListener("touchend",v),document.body.style[i]=""}a&&(a.addEventListener("mousedown",m),a.addEventListener("touchstart",m));s&&(s.addEventListener("mousedown",m),s.addEventListener("touchstart",m));this.elements.boundary.appendChild(h)}.call(this)}function C(){return this.options.enableExif&&window.EXIF}function E(e){if(this.options.enableZoom){var t=this.elements.zoomer,i=O(e,4);t.value=Math.max(parseFloat(t.min),Math.min(parseFloat(t.max),i)).toString()}}function _(i){var n=this._currentZoom,o=this.elements.preview.getBoundingClientRect(),r=this.elements.viewport.getBoundingClientRect(),a=g.parse(this.elements.preview.style[t]),s=new w(this.elements.preview),l=r.top-o.top+r.height/2,h=r.left-o.left+r.width/2,u={},p={};if(i){var d=s.x,m=s.y,f=a.x,v=a.y;u.y=d,u.x=m,a.y=f,a.x=v}else u.y=l/n,u.x=h/n,p.y=(u.y-s.y)*(1-n),p.x=(u.x-s.x)*(1-n),a.x-=p.x,a.y-=p.y;var y={};y[e]=u.x+"px "+u.y+"px",y[t]=a.toString(),c(this.elements.preview,y)}function L(){if(this.elements){var e=this.elements.boundary.getBoundingClientRect(),t=this.elements.preview.getBoundingClientRect();c(this.elements.overlay,{width:t.width+"px",height:t.height+"px",top:t.top-e.top+"px",left:t.left-e.left+"px"})}}w.prototype.toString=function(){return this.x+"px "+this.y+"px"};var R,B,Z,I,M=(R=L,B=500,function(){var e=this,t=arguments,i=Z&&!I;clearTimeout(I),I=setTimeout(function(){I=null,Z||R.apply(e,t)},B),i&&R.apply(e,t)});function z(){var e,t=this.get();F.call(this)&&(this.options.update.call(this,t),this.$&&"undefined"==typeof Prototype?this.$(this.element).trigger("update.croppie",t):(window.CustomEvent?e=new CustomEvent("update",{detail:t}):(e=document.createEvent("CustomEvent")).initCustomEvent("update",!0,!0,t),this.element.dispatchEvent(e)))}function F(){return this.elements.preview.offsetHeight>0&&this.elements.preview.offsetWidth>0}function W(){var i,n={},o=this.elements.preview,r=new g(0,0,1),a=new w;F.call(this)&&!this.data.bound&&(this.data.bound=!0,n[t]=r.toString(),n[e]=a.toString(),n.opacity=1,c(o,n),i=this.elements.preview.getBoundingClientRect(),this._originalImageWidth=i.width,this._originalImageHeight=i.height,this.data.orientation=C.call(this)?y(this.elements.img):this.data.orientation,this.options.enableZoom?X.call(this,!0):this._currentZoom=1,r.scale=this._currentZoom,n[t]=r.toString(),c(o,n),this.data.points.length?function(i){if(4!==i.length)throw"Croppie - Invalid number of points supplied: "+i;var n=i[2]-i[0],o=this.elements.viewport.getBoundingClientRect(),r=this.elements.boundary.getBoundingClientRect(),a={left:o.left-r.left,top:o.top-r.top},s=o.width/n,l=i[1],h=i[0],u=-1*i[1]+a.top,p=-1*i[0]+a.left,d={};d[e]=h+"px "+l+"px",d[t]=new g(p,u,s).toString(),c(this.elements.preview,d),E.call(this,s),this._currentZoom=s}.call(this,this.data.points):function(){var e=this.elements.preview.getBoundingClientRect(),i=this.elements.viewport.getBoundingClientRect(),n=this.elements.boundary.getBoundingClientRect(),o=i.left-n.left,r=i.top-n.top,a=o-(e.width-i.width)/2,s=r-(e.height-i.height)/2,l=new g(a,s,this._currentZoom);c(this.elements.preview,t,l.toString())}.call(this),_.call(this),L.call(this))}function X(e){var t,i,n,o,r=Math.max(this.options.minZoom,0)||0,a=this.options.maxZoom||1.5,s=this.elements.zoomer,l=parseFloat(s.value),h=this.elements.boundary.getBoundingClientRect(),c=f(this.elements.img,this.data.orientation),p=this.elements.viewport.getBoundingClientRect();this.options.enforceBoundary&&(n=p.width/c.width,o=p.height/c.height,r=Math.max(n,o)),r>=a&&(a=r+1),s.min=O(r,4),s.max=O(a,4),!e&&(ls.max)?E.call(this,lthis._originalImageWidth&&(w=(d=this._originalImageWidth-c)/o*h),n<0&&(p=0,g=Math.abs(n)/r*u),f+p>this._originalImageHeight&&(y=(f=this._originalImageHeight-p)/r*u),l.drawImage(this.elements.preview,c,p,d,f,v,g,w,y),a&&(l.fillStyle="#fff",l.globalCompositeOperation="destination-in",l.beginPath(),l.arc(s.width/2,s.height/2,s.width/2,0,2*Math.PI,!0),l.closePath(),l.fill()),s}function H(e,t){var i,n=this,o=[],r=null,a=C.call(n);if("string"==typeof e)i=e,e={};else if(Array.isArray(e))o=e.slice();else{if(void 0===e&&n.data.url)return W.call(n),z.call(n),null;i=e.url,o=e.points||[],r=void 0===e.zoom?null:e.zoom}return n.data.bound=!1,n.data.url=i||n.data.url,n.data.boundZoom=r,function(e,t){if(!e)throw"Source image missing";var i=new Image;return i.style.opacity="0",new Promise(function(n,o){function r(){i.style.opacity="1",setTimeout(function(){n(i)},1)}i.removeAttribute("crossOrigin"),e.match(/^https?:\/\/|^\/\//)&&i.setAttribute("crossOrigin","anonymous"),i.onload=function(){t?EXIF.getData(i,function(){r()}):r()},i.onerror=function(e){i.style.opacity=1,setTimeout(function(){o(e)},1)},i.src=e})}(i,a).then(function(i){if(function(e){this.elements.img.parentNode&&(Array.prototype.forEach.call(this.elements.img.classList,function(t){e.classList.add(t)}),this.elements.img.parentNode.replaceChild(e,this.elements.img),this.elements.preview=e),this.elements.img=e}.call(n,i),o.length)n.options.relative&&(o=[o[0]*i.naturalWidth/100,o[1]*i.naturalHeight/100,o[2]*i.naturalWidth/100,o[3]*i.naturalHeight/100]);else{var r,a,s=f(i),l=n.elements.viewport.getBoundingClientRect(),h=l.width/l.height;s.width/s.height>h?r=(a=s.height)*h:(r=s.width,a=s.height/h);var u=(s.width-r)/2,c=(s.height-a)/2,p=u+r,d=c+a;n.data.points=[u,c,p,d]}n.data.orientation=e.orientation||1,n.data.points=o.map(function(e){return parseFloat(e)}),n.options.useCanvas&&function(e){var t=this.elements.canvas,i=this.elements.img;t.getContext("2d").clearRect(0,0,t.width,t.height),t.width=i.width,t.height=i.height,b(t,i,this.options.enableOrientation&&e||y(i))}.call(n,n.data.orientation),W.call(n),z.call(n),t&&t()})}function O(e,t){return parseFloat(e).toFixed(t||0)}function k(){var e=this.elements.preview.getBoundingClientRect(),t=this.elements.viewport.getBoundingClientRect(),i=t.left-e.left,n=t.top-e.top,o=(t.width-this.elements.viewport.offsetWidth)/2,r=(t.height-this.elements.viewport.offsetHeight)/2,a=i+this.elements.viewport.offsetWidth+o,s=n+this.elements.viewport.offsetHeight+r,l=this._currentZoom;(l===1/0||isNaN(l))&&(l=1);var h=this.options.enforceBoundary?0:Number.NEGATIVE_INFINITY;return i=Math.max(h,i/l),n=Math.max(h,n/l),a=Math.max(h,a/l),s=Math.max(h,s/l),{points:[O(i),O(n),O(a),O(s)],zoom:l,orientation:this.data.orientation}}var A={type:"canvas",format:"png",quality:1},S=["jpeg","webp","png"];function j(e){var t=this,i=k.call(t),n=l(h(A),h(e)),o="string"==typeof e?e:n.type||"base64",r=n.size||"viewport",a=n.format,s=n.quality,u=n.backgroundColor,d="boolean"==typeof n.circle?n.circle:"circle"===t.options.viewport.type,m=t.elements.viewport.getBoundingClientRect(),f=m.width/m.height;return"viewport"===r?(i.outputWidth=m.width,i.outputHeight=m.height):"object"==typeof r&&(r.width&&r.height?(i.outputWidth=r.width,i.outputHeight=r.height):r.width?(i.outputWidth=r.width,i.outputHeight=r.width/f):r.height&&(i.outputWidth=r.height*f,i.outputHeight=r.height)),S.indexOf(a)>-1&&(i.format="image/"+a,i.quality=s),i.circle=d,i.url=t.data.url,i.backgroundColor=u,new Promise(function(e){switch(o.toLowerCase()){case"rawcanvas":e(Y.call(t,i));break;case"canvas":case"base64":e(function(e){return Y.call(this,e).toDataURL(e.format,e.quality)}.call(t,i));break;case"blob":(function(e){var t=this;return new Promise(function(i){Y.call(t,e).toBlob(function(e){i(e)},e.format,e.quality)})}).call(t,i).then(e);break;default:e(function(e){var t=e.points,i=document.createElement("div"),n=document.createElement("img"),o=t[2]-t[0],r=t[3]-t[1];return p(i,"croppie-result"),i.appendChild(n),c(n,{left:-1*t[0]+"px",top:-1*t[1]+"px"}),n.src=e.url,c(i,{width:o+"px",height:r+"px"}),i}.call(t,i))}})}function N(e){if(!this.options.useCanvas||!this.options.enableOrientation)throw"Croppie: Cannot rotate without enableOrientation && EXIF.js included";var t,i,n,o,s,l=this.elements.canvas;if(this.data.orientation=(t=this.data.orientation,i=e,n=r.indexOf(t)>-1?r:a,o=n.indexOf(t),s=i/90%n.length,n[(n.length+o+s%n.length)%n.length]),b(l,this.elements.img,this.data.orientation),_.call(this,!0),X.call(this),Math.abs(e)/90%2==1){var h=this._originalImageHeight,u=this._originalImageWidth;this._originalImageWidth=h,this._originalImageHeight=u}}if("undefined"!=typeof window&&window.jQuery){var P=window.jQuery;P.fn.croppie=function(e){if("string"===typeof e){var t=Array.prototype.slice.call(arguments,1),i=P(this).data("croppie");return"get"===e?i.get():"result"===e?i.result.apply(i,t):"bind"===e?i.bind.apply(i,t):this.each(function(){var i=P(this).data("croppie");if(i){var n=i[e];if(!P.isFunction(n))throw"Croppie "+e+" method not found";n.apply(i,t),"destroy"===e&&P(this).removeData("croppie")}})}return this.each(function(){var t=new T(this,e);t.$=P,P(this).data("croppie",t)})}}function T(e,t){if(e.className.indexOf("croppie-container")>-1)throw new Error("Croppie: Can't initialize croppie more than once");if(this.element=e,this.options=l(h(T.defaults),t),"img"===this.element.tagName.toLowerCase()){var i=this.element;p(i,"cr-original-image"),d(i,{"aria-hidden":"true",alt:""});var n=document.createElement("div");this.element.parentNode.appendChild(n),n.appendChild(i),this.element=n,this.options.url=this.options.url||i.src}if(x.call(this),this.options.url){var o={url:this.options.url,points:this.options.points};delete this.options.url,delete this.options.points,H.call(this,o)}}return T.defaults={viewport:{width:100,height:100,type:"square"},boundary:{},orientationControls:{enabled:!0,leftClass:"",rightClass:""},resizeControls:{width:!0,height:!0},customClass:"",showZoomer:!0,enableZoom:!0,enableResize:!1,mouseWheelZoom:!0,enableExif:!1,enforceBoundary:!0,enableOrientation:!1,enableKeyMovement:!0,update:function(){}},T.globals={translate:"translate3d"},l(T.prototype,{bind:function(e,t){return H.call(this,e,t)},get:function(){var e=k.call(this),t=e.points;return this.options.relative&&(t[0]/=this.elements.img.naturalWidth/100,t[1]/=this.elements.img.naturalHeight/100,t[2]/=this.elements.img.naturalWidth/100,t[3]/=this.elements.img.naturalHeight/100),e},result:function(e){return j.call(this,e)},refresh:function(){return function(){W.call(this)}.call(this)},setZoom:function(e){E.call(this,e),u(this.elements.zoomer)},rotate:function(e){N.call(this,e)},destroy:function(){return function(){var e,t;this.element.removeChild(this.elements.boundary),e=this.element,t="croppie-container",e.classList?e.classList.remove(t):e.className=e.className.replace(t,""),this.options.enableZoom&&this.element.removeChild(this.elements.zoomerWrap),delete this.elements}.call(this)}}),T}); -------------------------------------------------------------------------------- /demo/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foliotek/Croppie/8998b1e35a79f5c8f920e7fa76f72523bffc8df5/demo/cat.jpg -------------------------------------------------------------------------------- /demo/demo-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foliotek/Croppie/8998b1e35a79f5c8f920e7fa76f72523bffc8df5/demo/demo-1.jpg -------------------------------------------------------------------------------- /demo/demo-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foliotek/Croppie/8998b1e35a79f5c8f920e7fa76f72523bffc8df5/demo/demo-2.jpg -------------------------------------------------------------------------------- /demo/demo-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foliotek/Croppie/8998b1e35a79f5c8f920e7fa76f72523bffc8df5/demo/demo-3.jpg -------------------------------------------------------------------------------- /demo/demo.css: -------------------------------------------------------------------------------- 1 | /* 2 | Colors 3 | #20C1C7 4 | #204648 5 | #189094 6 | #61CED2 7 | #0C4648 8 | */ 9 | html, 10 | body { 11 | height: 100%; 12 | padding: 0; 13 | margin: 0; 14 | } 15 | 16 | body { 17 | font-size: 14px; 18 | color: #222; 19 | min-height: 100%; 20 | height: auto; 21 | } 22 | 23 | body, 24 | button, 25 | input { 26 | font-family: 'Open Sans', sans-serif; 27 | } 28 | 29 | h1 { 30 | font-size: 42px; 31 | font-weight: 300; 32 | padding-top: 30px; 33 | margin: 15px 0; 34 | } 35 | h3 { 36 | font-size: 18px; 37 | font-weight: 400; 38 | border-bottom: 1px solid #0C4648; 39 | margin: 15px 0 10px; 40 | } 41 | nav { 42 | position: absolute; 43 | top: 0; 44 | right: 0; 45 | padding:15px; 46 | } 47 | 48 | nav a { 49 | color: white; 50 | text-decoration: none; 51 | padding: 0 10px; 52 | } 53 | 54 | footer { 55 | text-align: center; 56 | color: #555; 57 | font-size: 12px; 58 | padding: 5px; 59 | } 60 | 61 | section { 62 | margin-bottom: 25px; 63 | } 64 | 65 | section.hero { 66 | background: #20C1C7; 67 | background: linear-gradient(#189094, #20C1C7); 68 | color: white; 69 | text-shadow: 0 1px 1px rgba(20,20,20,0.6); 70 | margin-bottom: 0; 71 | min-height: 450px; 72 | } 73 | .hero h2 { 74 | font-size: 16px; 75 | font-weight: 400; 76 | } 77 | 78 | section.who { 79 | margin-bottom: 30px; 80 | } 81 | .section-header { 82 | background: #204648; 83 | padding: 5px 0; 84 | } 85 | 86 | input[type="number"], 87 | input[type="text"] { 88 | border: 1px solid #aaa; 89 | padding: 5px; 90 | font-size: 18px; 91 | width: 100px; 92 | } 93 | 94 | button, 95 | a.btn { 96 | background-color: #189094; 97 | color: white; 98 | padding: 10px 15px; 99 | border-radius: 3px; 100 | border: 1px solid rgba(255, 255, 255, 0.5); 101 | font-size: 16px; 102 | cursor: pointer; 103 | text-decoration: none; 104 | text-shadow: none; 105 | display: inline-block; 106 | cursor: pointer; 107 | } 108 | input[type="file"] { 109 | cursor: pointer; 110 | } 111 | button:focus { 112 | outline: 0; 113 | } 114 | 115 | .file-btn { 116 | position: relative; 117 | } 118 | .file-btn input[type="file"] { 119 | position: absolute; 120 | top: 0; 121 | left: 0; 122 | width: 100%; 123 | height: 100%; 124 | opacity: 0; 125 | } 126 | 127 | .actions { 128 | padding: 5px 0; 129 | } 130 | .actions button { 131 | margin-right: 5px; 132 | } 133 | 134 | pre[class*="language"] { 135 | margin: 10px 0; 136 | padding-top: 0; 137 | border-left-color: #189094; 138 | } 139 | 140 | .container, 141 | .section-header h2 { 142 | position: relative; 143 | max-width: 1000px; 144 | margin: 0 auto; 145 | min-width: 500px; 146 | padding: 0 10px; 147 | } 148 | 149 | .hero p { 150 | font-size: 16px; 151 | } 152 | 153 | .hero .grid { 154 | padding-top: 50px; 155 | } 156 | 157 | h2 { 158 | color: white; 159 | font-size: 23px; 160 | font-weight: 300; 161 | } 162 | 163 | .demo-wrap { 164 | border-bottom: 1px solid #ddd; 165 | padding-top: 20px; 166 | } 167 | 168 | .demo-wrap .container { 169 | padding-bottom: 10px; 170 | } 171 | 172 | .demo-wrap strong { 173 | font-size: 16px; 174 | display: block; 175 | font-weight: 400; 176 | color: #aaa; 177 | margin: 0 0 5px 0; 178 | } 179 | 180 | .documentation ul { 181 | list-style: none; 182 | padding: 0; 183 | margin: 0; 184 | } 185 | 186 | .documentation section > ul > li { 187 | margin-bottom: 1.5em; 188 | } 189 | 190 | .documentation p { 191 | margin: 5px 0 10px; 192 | } 193 | 194 | .documentation .parameter-list li { 195 | padding-left: 5px; 196 | line-height: 28px; 197 | } 198 | .documentation .parameter-list li.values { 199 | padding-left: 20px; 200 | } 201 | .documentation em { 202 | color: #aaa; 203 | font-style: normal; 204 | padding: 0 10px; 205 | } 206 | .documentation i { 207 | color: #666; 208 | } 209 | 210 | .documentation strong.focus { 211 | font-size: 18px; 212 | color: #189094; 213 | font-weight: 700; 214 | } 215 | .documentation span.default { 216 | padding-right: 10px; 217 | font-weight: 600; 218 | color: #777; 219 | } 220 | 221 | .important-notes article { 222 | margin-bottom: 2em; 223 | } 224 | 225 | .upload-demo .upload-demo-wrap, 226 | .upload-demo .upload-result, 227 | .upload-demo.ready .upload-msg { 228 | display: none; 229 | } 230 | .upload-demo.ready .upload-demo-wrap { 231 | display: block; 232 | } 233 | .upload-demo.ready .upload-result { 234 | display: inline-block; 235 | } 236 | .upload-demo-wrap { 237 | width: 300px; 238 | height: 300px; 239 | margin: 0 auto; 240 | } 241 | 242 | .upload-msg { 243 | text-align: center; 244 | padding: 50px; 245 | font-size: 22px; 246 | color: #aaa; 247 | width: 260px; 248 | margin: 50px auto; 249 | border: 1px solid #aaa; 250 | } 251 | 252 | /* Sweet alert modifications */ 253 | .sweet-alert { 254 | width: auto; 255 | max-width: 85%; 256 | } 257 | 258 | /* Grid - subset */ 259 | *, *:after, *:before { 260 | -webkit-box-sizing: border-box; 261 | -moz-box-sizing: border-box; 262 | box-sizing: border-box; 263 | } 264 | [class*='col-'] { 265 | float: left; 266 | padding-right: 20px; /* column-space */ 267 | } 268 | 269 | .grid { 270 | width: 100%; 271 | max-width: 1140px; 272 | min-width: 755px; 273 | margin: 0 auto; 274 | overflow: hidden; 275 | } 276 | .grid:after { 277 | content: ""; 278 | display: table; 279 | clear: both; 280 | } 281 | 282 | .col-1-2 { 283 | width: 50%; 284 | } 285 | 286 | .col-1-3 { 287 | width: 33.33%; 288 | } 289 | .col-2-3 { 290 | width: 66.66%; 291 | } 292 | .col-1-4 { 293 | width: 25%; 294 | } 295 | .col-3-4 { 296 | width: 75%; 297 | } 298 | 299 | @media handheld, only screen and (max-width: 767px) { 300 | .grid { 301 | width: 100%; 302 | min-width: 0; 303 | margin-left: 0; 304 | margin-right: 0; 305 | padding-left: 20px; /* grid-space to left */ 306 | padding-right: 10px; /* grid-space to right: (grid-space-left - column-space) e.g. 20px-10px=10px */ 307 | } 308 | 309 | [class*='col-'] { 310 | width: auto; 311 | float: none; 312 | margin: 10px 0; 313 | padding-left: 0; 314 | padding-right: 10px; /* column-space */ 315 | } 316 | .container, 317 | .section-header h2 { 318 | min-width: 0; 319 | } 320 | 321 | .croppie-container { 322 | padding: 30px 0; 323 | } 324 | } -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | var Demo = (function() { 2 | 3 | function output(node) { 4 | var existing = $('#result .croppie-result'); 5 | if (existing.length > 0) { 6 | existing[0].parentNode.replaceChild(node, existing[0]); 7 | } 8 | else { 9 | $('#result')[0].appendChild(node); 10 | } 11 | } 12 | 13 | function popupResult(result) { 14 | var html; 15 | if (result.html) { 16 | html = result.html; 17 | } 18 | if (result.src) { 19 | html = ''; 20 | } 21 | swal({ 22 | title: '', 23 | html: true, 24 | text: html, 25 | allowOutsideClick: true 26 | }); 27 | setTimeout(function(){ 28 | $('.sweet-alert').css('margin', function() { 29 | var top = -1 * ($(this).height() / 2), 30 | left = -1 * ($(this).width() / 2); 31 | 32 | return top + 'px 0 0 ' + left + 'px'; 33 | }); 34 | }, 1); 35 | } 36 | 37 | function demoMain () { 38 | var mc = $('#cropper-1'); 39 | mc.croppie({ 40 | viewport: { 41 | width: 150, 42 | height: 150, 43 | type: 'circle' 44 | }, 45 | boundary: { 46 | width: 300, 47 | height: 300 48 | }, 49 | // url: 'demo/demo-1.jpg', 50 | // enforceBoundary: false 51 | // mouseWheelZoom: false 52 | }); 53 | mc.on('update.croppie', function (ev, data) { 54 | // console.log('jquery update', ev, data); 55 | }); 56 | $('.js-main-image').on('click', function (ev) { 57 | mc.croppie('result', { 58 | type: 'rawcanvas', 59 | circle: true, 60 | // size: { width: 300, height: 300 }, 61 | format: 'png' 62 | }).then(function (canvas) { 63 | popupResult({ 64 | src: canvas.toDataURL() 65 | }); 66 | }); 67 | }); 68 | } 69 | 70 | function demoBasic() { 71 | var $w = $('.basic-width'), 72 | $h = $('.basic-height'), 73 | basic = $('#demo-basic').croppie({ 74 | viewport: { 75 | width: 150, 76 | height: 200 77 | }, 78 | boundary: { 79 | width: 300, 80 | height: 300 81 | } 82 | }); 83 | basic.croppie('bind', { 84 | url: 'demo/cat.jpg', 85 | points: [77,469,280,739] 86 | }); 87 | 88 | $('.basic-result').on('click', function() { 89 | var w = parseInt($w.val(), 10), 90 | h = parseInt($h.val(), 10),s 91 | size = 'viewport'; 92 | if (w || h) { 93 | size = { width: w, height: h }; 94 | } 95 | basic.croppie('result', { 96 | type: 'canvas', 97 | size: size, 98 | resultSize: { 99 | width: 50, 100 | height: 50 101 | } 102 | }).then(function (resp) { 103 | popupResult({ 104 | src: resp 105 | }); 106 | }); 107 | }); 108 | } 109 | 110 | function demoVanilla() { 111 | var vEl = document.getElementById('vanilla-demo'), 112 | vanilla = new Croppie(vEl, { 113 | viewport: { width: 200, height: 100 }, 114 | boundary: { width: 300, height: 300 }, 115 | showZoomer: false, 116 | enableOrientation: true 117 | }); 118 | vanilla.bind({ 119 | url: 'demo/demo-2.jpg', 120 | orientation: 4, 121 | zoom: 0 122 | }); 123 | vEl.addEventListener('update', function (ev) { 124 | // console.log('vanilla update', ev); 125 | }); 126 | document.querySelector('.vanilla-result').addEventListener('click', function (ev) { 127 | vanilla.result({ 128 | type: 'blob' 129 | }).then(function (blob) { 130 | popupResult({ 131 | src: window.URL.createObjectURL(blob) 132 | }); 133 | }); 134 | }); 135 | 136 | $('.vanilla-rotate').on('click', function(ev) { 137 | vanilla.rotate(parseInt($(this).data('deg'))); 138 | }); 139 | } 140 | 141 | function demoResizer() { 142 | var vEl = document.getElementById('resizer-demo'), 143 | resize = new Croppie(vEl, { 144 | viewport: { width: 100, height: 100 }, 145 | boundary: { width: 300, height: 300 }, 146 | showZoomer: false, 147 | enableResize: true, 148 | enableOrientation: true, 149 | mouseWheelZoom: 'ctrl' 150 | }); 151 | resize.bind({ 152 | url: 'demo/demo-2.jpg', 153 | zoom: 0 154 | }); 155 | vEl.addEventListener('update', function (ev) { 156 | console.log('resize update', ev); 157 | }); 158 | document.querySelector('.resizer-result').addEventListener('click', function (ev) { 159 | resize.result({ 160 | type: 'blob' 161 | }).then(function (blob) { 162 | popupResult({ 163 | src: window.URL.createObjectURL(blob) 164 | }); 165 | }); 166 | }); 167 | } 168 | 169 | function demoUpload() { 170 | var $uploadCrop; 171 | 172 | function readFile(input) { 173 | if (input.files && input.files[0]) { 174 | var reader = new FileReader(); 175 | 176 | reader.onload = function (e) { 177 | $('.upload-demo').addClass('ready'); 178 | $uploadCrop.croppie('bind', { 179 | url: e.target.result 180 | }).then(function(){ 181 | console.log('jQuery bind complete'); 182 | }); 183 | 184 | } 185 | 186 | reader.readAsDataURL(input.files[0]); 187 | } 188 | else { 189 | swal("Sorry - you're browser doesn't support the FileReader API"); 190 | } 191 | } 192 | 193 | $uploadCrop = $('#upload-demo').croppie({ 194 | viewport: { 195 | width: 100, 196 | height: 100, 197 | type: 'circle' 198 | }, 199 | enableExif: true 200 | }); 201 | 202 | $('#upload').on('change', function () { readFile(this); }); 203 | $('.upload-result').on('click', function (ev) { 204 | $uploadCrop.croppie('result', { 205 | type: 'canvas', 206 | size: 'viewport' 207 | }).then(function (resp) { 208 | popupResult({ 209 | src: resp 210 | }); 211 | }); 212 | }); 213 | } 214 | 215 | function demoHidden() { 216 | var $hid = $('#hidden-demo'); 217 | 218 | $hid.croppie({ 219 | viewport: { 220 | width: 175, 221 | height: 175, 222 | type: 'circle' 223 | }, 224 | boundary: { 225 | width: 200, 226 | height: 200 227 | } 228 | }); 229 | $hid.croppie('bind', 'demo/demo-3.jpg'); 230 | $('.show-hidden').on('click', function () { 231 | $hid.toggle(); 232 | $hid.croppie('bind'); 233 | }); 234 | } 235 | 236 | function bindNavigation () { 237 | var $html = $('html'); 238 | $('nav a').on('click', function (ev) { 239 | var lnk = $(ev.currentTarget), 240 | href = lnk.attr('href'), 241 | targetTop = $('a[name=' + href.substring(1) + ']').offset().top; 242 | 243 | $html.animate({ scrollTop: targetTop }); 244 | ev.preventDefault(); 245 | }); 246 | } 247 | 248 | function init() { 249 | bindNavigation(); 250 | demoMain(); 251 | demoBasic(); 252 | demoVanilla(); 253 | demoResizer(); 254 | demoUpload(); 255 | demoHidden(); 256 | } 257 | 258 | return { 259 | init: init 260 | }; 261 | })(); 262 | 263 | 264 | // Full version of `log` that: 265 | // * Prevents errors on console methods when no console present. 266 | // * Exposes a global 'log' function that preserves line numbering and formatting. 267 | (function () { 268 | var method; 269 | var noop = function () { }; 270 | var methods = [ 271 | 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 272 | 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 273 | 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 274 | 'timeStamp', 'trace', 'warn' 275 | ]; 276 | var length = methods.length; 277 | var console = (window.console = window.console || {}); 278 | 279 | while (length--) { 280 | method = methods[length]; 281 | 282 | // Only stub undefined methods. 283 | if (!console[method]) { 284 | console[method] = noop; 285 | } 286 | } 287 | 288 | 289 | if (Function.prototype.bind) { 290 | window.log = Function.prototype.bind.call(console.log, console); 291 | } 292 | else { 293 | window.log = function() { 294 | Function.prototype.apply.call(console.log, console, arguments); 295 | }; 296 | } 297 | })(); 298 | -------------------------------------------------------------------------------- /demo/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Foliotek/Croppie/8998b1e35a79f5c8f920e7fa76f72523bffc8df5/demo/hero.png -------------------------------------------------------------------------------- /demo/prism.css: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism-coy&languages=markup+css+clike+javascript&plugins=line-highlight+line-numbers */ 2 | /** 3 | * prism.js Coy theme for JavaScript, CoffeeScript, CSS and HTML 4 | * Based on https://github.com/tshedor/workshop-wp-theme (Example: http://workshop.kansan.com/category/sessions/basics or http://workshop.timshedor.com/category/sessions/basics); 5 | * @author Tim Shedor 6 | */ 7 | 8 | code[class*="language-"], 9 | pre[class*="language-"] { 10 | color: black; 11 | background: none; 12 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 13 | direction: ltr; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | word-wrap: normal; 19 | line-height: 1.5; 20 | 21 | -moz-tab-size: 4; 22 | -o-tab-size: 4; 23 | tab-size: 4; 24 | 25 | -webkit-hyphens: none; 26 | -moz-hyphens: none; 27 | -ms-hyphens: none; 28 | hyphens: none; 29 | } 30 | 31 | /* Code blocks */ 32 | pre[class*="language-"] { 33 | position: relative; 34 | margin: .5em 0; 35 | -webkit-box-shadow: -1px 0px 0px 0px #358ccb, 0px 0px 0px 1px #dfdfdf; 36 | -moz-box-shadow: -1px 0px 0px 0px #358ccb, 0px 0px 0px 1px #dfdfdf; 37 | box-shadow: -1px 0px 0px 0px #358ccb, 0px 0px 0px 1px #dfdfdf; 38 | border-left: 10px solid #358ccb; 39 | background-color: #fdfdfd; 40 | background-image: -webkit-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%); 41 | background-image: -moz-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%); 42 | background-image: -ms-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%); 43 | background-image: -o-linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%); 44 | background-image: linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%); 45 | background-size: 3em 3em; 46 | background-origin: content-box; 47 | overflow: visible; 48 | padding: 0; 49 | } 50 | 51 | code[class*="language"] { 52 | max-height: inherit; 53 | height: 100%; 54 | padding: 0 1em; 55 | display: block; 56 | overflow: auto; 57 | } 58 | 59 | /* Margin bottom to accomodate shadow */ 60 | :not(pre) > code[class*="language-"], 61 | pre[class*="language-"] { 62 | background-color: #fdfdfd; 63 | -webkit-box-sizing: border-box; 64 | -moz-box-sizing: border-box; 65 | box-sizing: border-box; 66 | margin-bottom: 1em; 67 | } 68 | 69 | /* Inline code */ 70 | :not(pre) > code[class*="language-"] { 71 | position: relative; 72 | padding: .2em; 73 | -webkit-border-radius: 0.3em; 74 | -moz-border-radius: 0.3em; 75 | -ms-border-radius: 0.3em; 76 | -o-border-radius: 0.3em; 77 | border-radius: 0.3em; 78 | color: #c92c2c; 79 | border: 1px solid rgba(0, 0, 0, 0.1); 80 | display: inline; 81 | white-space: normal; 82 | } 83 | 84 | pre[class*="language-"]:before, 85 | pre[class*="language-"]:after { 86 | content: ''; 87 | z-index: -2; 88 | display: block; 89 | position: absolute; 90 | bottom: 0.75em; 91 | left: 0.18em; 92 | width: 40%; 93 | height: 20%; 94 | max-height: 13em; 95 | -webkit-box-shadow: 0px 13px 8px #979797; 96 | -moz-box-shadow: 0px 13px 8px #979797; 97 | box-shadow: 0px 13px 8px #979797; 98 | -webkit-transform: rotate(-2deg); 99 | -moz-transform: rotate(-2deg); 100 | -ms-transform: rotate(-2deg); 101 | -o-transform: rotate(-2deg); 102 | transform: rotate(-2deg); 103 | } 104 | 105 | :not(pre) > code[class*="language-"]:after, 106 | pre[class*="language-"]:after { 107 | right: 0.75em; 108 | left: auto; 109 | -webkit-transform: rotate(2deg); 110 | -moz-transform: rotate(2deg); 111 | -ms-transform: rotate(2deg); 112 | -o-transform: rotate(2deg); 113 | transform: rotate(2deg); 114 | } 115 | 116 | .token.comment, 117 | .token.block-comment, 118 | .token.prolog, 119 | .token.doctype, 120 | .token.cdata { 121 | color: #7D8B99; 122 | } 123 | 124 | .token.punctuation { 125 | color: #5F6364; 126 | } 127 | 128 | .token.property, 129 | .token.tag, 130 | .token.boolean, 131 | .token.number, 132 | .token.function-name, 133 | .token.constant, 134 | .token.symbol, 135 | .token.deleted { 136 | color: #c92c2c; 137 | } 138 | 139 | .token.selector, 140 | .token.attr-name, 141 | .token.string, 142 | .token.char, 143 | .token.function, 144 | .token.builtin, 145 | .token.inserted { 146 | color: #2f9c0a; 147 | } 148 | 149 | .token.operator, 150 | .token.entity, 151 | .token.url, 152 | .token.variable { 153 | color: #a67f59; 154 | background: rgba(255, 255, 255, 0.5); 155 | } 156 | 157 | .token.atrule, 158 | .token.attr-value, 159 | .token.keyword, 160 | .token.class-name { 161 | color: #1990b8; 162 | } 163 | 164 | .token.regex, 165 | .token.important { 166 | color: #e90; 167 | } 168 | 169 | .language-css .token.string, 170 | .style .token.string { 171 | color: #a67f59; 172 | background: rgba(255, 255, 255, 0.5); 173 | } 174 | 175 | .token.important { 176 | font-weight: normal; 177 | } 178 | 179 | .token.bold { 180 | font-weight: bold; 181 | } 182 | .token.italic { 183 | font-style: italic; 184 | } 185 | 186 | .token.entity { 187 | cursor: help; 188 | } 189 | 190 | .namespace { 191 | opacity: .7; 192 | } 193 | 194 | @media screen and (max-width: 767px) { 195 | pre[class*="language-"]:before, 196 | pre[class*="language-"]:after { 197 | bottom: 14px; 198 | -webkit-box-shadow: none; 199 | -moz-box-shadow: none; 200 | box-shadow: none; 201 | } 202 | 203 | } 204 | 205 | /* Plugin styles */ 206 | .token.tab:not(:empty):before, 207 | .token.cr:before, 208 | .token.lf:before { 209 | color: #e0d7d1; 210 | } 211 | 212 | /* Plugin styles: Line Numbers */ 213 | pre[class*="language-"].line-numbers { 214 | padding-left: 0; 215 | } 216 | 217 | pre[class*="language-"].line-numbers code { 218 | padding-left: 3.8em; 219 | } 220 | 221 | pre[class*="language-"].line-numbers .line-numbers-rows { 222 | left: 0; 223 | } 224 | 225 | /* Plugin styles: Line Highlight */ 226 | pre[class*="language-"][data-line] { 227 | padding-top: 0; 228 | padding-bottom: 0; 229 | padding-left: 0; 230 | } 231 | pre[data-line] code { 232 | position: relative; 233 | padding-left: 4em; 234 | } 235 | pre .line-highlight { 236 | margin-top: 0; 237 | } 238 | 239 | pre[data-line] { 240 | position: relative; 241 | padding: 1em 0 1em 3em; 242 | } 243 | 244 | .line-highlight { 245 | position: absolute; 246 | left: 0; 247 | right: 0; 248 | padding: inherit 0; 249 | margin-top: 1em; /* Same as .prism’s padding-top */ 250 | 251 | background: hsla(24, 20%, 50%,.08); 252 | background: -moz-linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0)); 253 | background: -webkit-linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0)); 254 | background: -o-linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0)); 255 | background: linear-gradient(left, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0)); 256 | 257 | pointer-events: none; 258 | 259 | line-height: inherit; 260 | white-space: pre; 261 | } 262 | 263 | .line-highlight:before, 264 | .line-highlight[data-end]:after { 265 | content: attr(data-start); 266 | position: absolute; 267 | top: .4em; 268 | left: .6em; 269 | min-width: 1em; 270 | padding: 0 .5em; 271 | background-color: hsla(24, 20%, 50%,.4); 272 | color: hsl(24, 20%, 95%); 273 | font: bold 65%/1.5 sans-serif; 274 | text-align: center; 275 | vertical-align: .3em; 276 | border-radius: 999px; 277 | text-shadow: none; 278 | box-shadow: 0 1px white; 279 | } 280 | 281 | .line-highlight[data-end]:after { 282 | content: attr(data-end); 283 | top: auto; 284 | bottom: .4em; 285 | } 286 | pre.line-numbers { 287 | position: relative; 288 | padding-left: 3.8em; 289 | counter-reset: linenumber; 290 | } 291 | 292 | pre.line-numbers > code { 293 | position: relative; 294 | } 295 | 296 | .line-numbers .line-numbers-rows { 297 | position: absolute; 298 | pointer-events: none; 299 | top: 0; 300 | font-size: 100%; 301 | left: -3.8em; 302 | width: 3em; /* works for line-numbers below 1000 lines */ 303 | letter-spacing: -1px; 304 | border-right: 1px solid #999; 305 | 306 | -webkit-user-select: none; 307 | -moz-user-select: none; 308 | -ms-user-select: none; 309 | user-select: none; 310 | 311 | } 312 | 313 | .line-numbers-rows > span { 314 | pointer-events: none; 315 | display: block; 316 | counter-increment: linenumber; 317 | } 318 | 319 | .line-numbers-rows > span:before { 320 | content: counter(linenumber); 321 | color: #999; 322 | display: block; 323 | padding-right: 0.8em; 324 | text-align: right; 325 | } -------------------------------------------------------------------------------- /demo/prism.js: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism-coy&languages=markup+css+clike+javascript&plugins=line-highlight+line-numbers */ 2 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){g&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,b=y+v,k=d.slice(0,y+1),w=d.slice(b+1),_=[p,1];k&&_.push(k);var P=new a(l,c?n.tokenize(m,c):m,h);_.push(P),w&&_.push(w),Array.prototype.splice.apply(r,_)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(t)}}},a=n.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var i={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}n.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=(o?" ":"")+s+'="'+(i.attributes[s]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+o+">"+i.content+""},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,i=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),i&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",n.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); 3 | Prism.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=.$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup; 4 | Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/()[\w\W]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css"}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|').*?\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag)); 5 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}; 6 | Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,number:/\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,"function":/[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^\/])\/(?!\/)(\[.+?]|\\.|[^\/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,lookbehind:!0}}),Prism.languages.insertBefore("javascript","class-name",{"template-string":{pattern:/`(?:\\`|\\?[^`])*`/,inside:{interpolation:{pattern:/\$\{[^}]+\}/,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/()[\w\W]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript"}}),Prism.languages.js=Prism.languages.javascript; 7 | !function(){function e(e,t){return Array.prototype.slice.call((t||document).querySelectorAll(e))}function t(e,t){return t=" "+t+" ",(" "+e.className+" ").replace(/[\n\t]/g," ").indexOf(t)>-1}function n(e,n,i){for(var o,a=n.replace(/\s+/g,"").split(","),l=+e.getAttribute("data-line-offset")||0,d=r()?parseInt:parseFloat,c=d(getComputedStyle(e).lineHeight),s=0;o=a[s++];){o=o.split("-");var u=+o[0],m=+o[1]||u,h=document.createElement("div");h.textContent=Array(m-u+2).join(" \n"),h.className=(i||"")+" line-highlight",t(e,"line-numbers")||(h.setAttribute("data-start",u),m>u&&h.setAttribute("data-end",m)),h.style.top=(u-l-1)*c+"px",t(e,"line-numbers")?e.appendChild(h):(e.querySelector("code")||e).appendChild(h)}}function i(){var t=location.hash.slice(1);e(".temporary.line-highlight").forEach(function(e){e.parentNode.removeChild(e)});var i=(t.match(/\.([\d,-]+)$/)||[,""])[1];if(i&&!document.getElementById(t)){var r=t.slice(0,t.lastIndexOf(".")),o=document.getElementById(r);o&&(o.hasAttribute("data-line")||o.setAttribute("data-line",""),n(o,i,"temporary "),document.querySelector(".temporary.line-highlight").scrollIntoView())}}if("undefined"!=typeof self&&self.Prism&&self.document&&document.querySelector){var r=function(){var e;return function(){if("undefined"==typeof e){var t=document.createElement("div");t.style.fontSize="13px",t.style.lineHeight="1.5",t.style.padding=0,t.style.border=0,t.innerHTML=" 
 ",document.body.appendChild(t),e=38===t.offsetHeight,document.body.removeChild(t)}return e}}(),o=0;Prism.hooks.add("complete",function(t){var r=t.element.parentNode,a=r&&r.getAttribute("data-line");r&&a&&/pre/i.test(r.nodeName)&&(clearTimeout(o),e(".line-highlight",r).forEach(function(e){e.parentNode.removeChild(e)}),n(r,a),o=setTimeout(i,1))}),window.addEventListener&&window.addEventListener("hashchange",i)}}(); 8 | !function(){"undefined"!=typeof self&&self.Prism&&self.document&&Prism.hooks.add("complete",function(e){if(e.code){var t=e.element.parentNode,s=/\s*\bline-numbers\b\s*/;if(t&&/pre/i.test(t.nodeName)&&(s.test(t.className)||s.test(e.element.className))&&!e.element.querySelector(".line-numbers-rows")){s.test(e.element.className)&&(e.element.className=e.element.className.replace(s,"")),s.test(t.className)||(t.className+=" line-numbers");var n,a=e.code.match(/\n(?!$)/g),l=a?a.length+1:1,m=new Array(l+1);m=m.join(""),n=document.createElement("span"),n.className="line-numbers-rows",n.innerHTML=m,t.hasAttribute("data-start")&&(t.style.counterReset="linenumber "+(parseInt(t.getAttribute("data-start"),10)-1)),e.element.appendChild(n)}}})}(); 9 | -------------------------------------------------------------------------------- /deploy.js: -------------------------------------------------------------------------------- 1 | var ghpages = require('gh-pages'), 2 | path = require('path'); 3 | 4 | ghpages.publish(__dirname, function (err) { 5 | if (err) console.error(err); 6 | else console.log('Successfully deployed to gh-pages'); 7 | }); 8 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Croppie - a simple javascript image cropper - Foliotek 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 33 |
34 |
35 |

Croppie

36 |

37 | Croppie is a fast, easy to use image cropping plugin with tons of configuration options! 38 |

39 |
40 | View on Github 41 | 42 |
43 |
44 |
45 | 46 |
47 |
48 |
49 |
50 |
51 |

The Basics

52 |
53 |
<div class="demo"></div>
 54 | <script>
 55 | $('.demo').croppie({
 56 |     url: 'demo/demo-1.jpg',
 57 | });
 58 | </script>
 59 | <!-- or even simpler -->
 60 | <img class="my-image" src="demo/demo-1.jpg" />
 61 | <script>
 62 | $('.my-image').croppie();
 63 | </script>
 64 | 
65 |
66 |
67 |
68 | 69 |

Installation

70 |
71 |

NPM:

72 |
npm install croppie
73 |

Bower:

74 |
bower install croppie
75 |

Download:

76 |

Source

77 | 78 |
79 |

Then add the following elements to your page:

80 |
<link rel="stylesheet" href="croppie.css" />
 81 | <script src="croppie.js"></script>
82 |
83 |
84 |
85 | 86 |

Usage

87 |
88 |

You can initialize Croppie with the following code:

89 | 90 |
var c = new Croppie(document.getElementById('item'), opts);
 91 | // call a method
 92 | c.method(args);
93 |

Or you can use jquery

94 |
$('#item').croppie(opts);
 95 | // call a method via jquery
 96 | $('#item').croppie(method, args);
97 |
98 |
99 | 100 |
101 | 102 |

Documentation

103 |
104 |
105 |

Options

106 |
    107 |
  • 108 | boundaryobject 109 |

    The outer container of the cropper

    110 | Default 111 | will default to the size of the container 112 |
  • 113 |
  • 114 | customClassstring 115 |

    A class of your choosing to add to the container to add custom styles to your croppie

    116 | Default'' 117 |
  • 118 |
  • 119 | enableExifboolean 120 |

    Enable exif orientation reading. Tells Croppie to read exif orientation from the image data and orient the image correctly before rendering to the page.

    121 |

    Requires exif.js

    122 | Defaultfalse 123 |
  • 124 |
  • 125 | enableOrientationboolean 126 |

    Enable or disable support for specifying a custom orientation when binding images (See bind method)

    127 | Default 128 | false 129 |
  • 130 |
  • 131 | enableResizeboolean 132 |

    Enable or disable support for resizing the viewport area.

    133 | Default 134 | false 135 |
  • 136 |
  • 137 | enableZoomboolean 138 |

    Enable zooming functionality. If set to false - scrolling and pinching would not zoom.

    139 | Defaulttrue 140 |
  • 141 |
  • 142 | enforceBoundaryboolean* Experimental 143 |

    Restricts zoom so image cannot be smaller than viewport

    144 | Defaulttrue 145 |
  • 146 |
  • 147 | mouseWheelZoombool/string 148 |

    Enable or disable the ability to use the mouse wheel to zoom in and out on a croppie instance. If 'ctrl' is passed mouse wheel will only work while control keyboard is pressed

    149 | Defaulttrue 150 |
  • 151 |
  • 152 | showZoomerboolean 153 |

    Hide or Show the zoom slider

    154 | Defaulttrue 155 |
  • 156 |
  • 157 | viewportobject 158 |

    The inner container of the coppie. The visible part of the image

    159 | Default 160 | { 161 | width: 100, 162 | height: 100, 163 | type: 'square' 164 | } 165 |

    166 | Valid type values:'square' 'circle' 167 |
  • 168 |
169 |
170 |
171 |

Methods

172 |
    173 |
  • 174 | get()object 175 |

    Get the crop points, and the zoom of the image.

    176 |
  • 177 |
  • 178 | bind({ url, points, orientation, zoom })Promise 179 |

    Bind an image to the croppie. Returns a promise to be resolved when the image has been loaded and the croppie has been initialized.

    180 | Parameters 181 |
    182 |
      183 |
    • url URL to image
    • 184 |
    • points Array of points that translate into [topLeftX, topLeftY, bottomRightX, bottomRightY]
    • 185 |
    • zoom Apply zoom after image has been bound
    • 186 |
    • orientation Custom orientation, applied after exif orientation (if enabled). Only works with 187 | enableOrientation option enabled (see 'Options'). 188 |
      189 | Valid options are: 190 |
        191 |
      • 1 unchanged
      • 192 |
      • 2 flipped horizontally
      • 193 |
      • 3 rotated 180 degrees
      • 194 |
      • 4 flipped vertically
      • 195 |
      • 5 flipped horizontally, then rotated left by 90 degrees
      • 196 |
      • 6 rotated clockwise by 90 degrees
      • 197 |
      • 7 flipped horizontally, then rotated right by 90 degrees
      • 198 |
      • 8 rotated counter-clockwise by 90 degrees
      • 199 |
      200 |
    • 201 |
    202 |
  • 203 |
  • 204 | destroy() 205 |

    Destroy a croppie instance and remove it from the DOM

    206 |
  • 207 |
  • 208 | result({ type, size, format, quality, circle })Promise 209 |

    Get the resulting crop of the image.

    210 | Parameters 211 |
    212 |
      213 |
    • 214 | type The type of result to return defaults to 'canvas' 215 |
    • 216 |
    • 217 | 'base64' returns a the cropped image encoded in base64 218 |
    • 219 |
    • 220 | 'html' returns html of the image positioned within an div of hidden overflow 221 |
    • 222 |
    • 223 | 'blob' returns a blob of the cropped image 224 |
    • 225 |
    • 226 | 'rawcanvas' returns the canvas element allowing you to manipulate prior to getting the resulted image 227 |
    • 228 |
    • 229 | size The size of the cropped image defaults to 'viewport' 230 |
    • 231 |
    • 232 | 'viewport' the size of the resulting image will be the same width and height as the viewport 233 |
    • 234 |
    • 235 | 'original' the size of the resulting image will be at the original scale of the image 236 |
    • 237 |
    • 238 | {width, height} an object defining the width and height. If only one dimension is specified, the other will be calculated using the viewport aspect ratio. 239 |
    • 240 |
    • 241 | format Indicating the image format. 242 |
    • 243 |
    • 244 | Default:'png' 245 |
    • 246 |
    • 247 | Valid values:'jpeg'|'png'|'webp' 248 |
    • 249 |
    • 250 | quality Number between 0 and 1 indicating image quality. 251 |
    • 252 |
    • 253 | Default:1 254 |
    • 255 |
    • 256 | circle force the result to be cropped into a circle 257 |
    • 258 |
    • 259 | Valid Values:truefalse 260 |
    • 261 |
    262 |
  • 263 |
  • 264 | rotate(degrees) 265 |

    Rotate the image by a specified degree amount. Only works with enableOrientation option enabled (see 'Options').

    266 |
      267 |
    • 268 | degrees Valid Values:90, 180, 270, -90, -180, -270 269 |
    • 270 |
    271 |
  • 272 |
  • 273 | setZoom(value) 274 |

    Set the zoom of a Croppie instance. The value passed in is still restricted to the min/max set by Croppie.

    275 |
      276 |
    • 277 | value a floating point to scale the image within the croppie. Must be between a min and max value set by croppie. 278 |
    • 279 |
    280 |
  • 281 |
282 |
283 |
284 |

Events

285 |
    286 |
  • 287 | update(croppie)Croppie 288 |

    Triggered when a drag or zoom occurs

    289 |
    
    290 | $('.my-croppie').on('update.croppie', function(ev, cropData) {});
    291 | // or
    292 | document.getElementById('another-croppie').addEventListener('update', function(ev) { var cropData = ev.detail; });
    293 |                             
    294 |
  • 295 |
296 |
297 |
298 |
299 | 300 |
301 | 302 |
303 |

Demos

304 |
305 |
306 |
307 |
308 |
309 | Basic Example 310 |

311 | var basic = $('#demo-basic').croppie({
312 |     viewport: {
313 |         width: 150,
314 |         height: 200
315 |     }
316 | });
317 | basic.croppie('bind', {
318 |     url: 'demo/cat.jpg',
319 |     points: [77,469,280,739]
320 | });
321 | //on button click
322 | basic.croppie('result', 'html').then(function(html) {
323 |     // html is div (overflow hidden)
324 |     // with img positioned inside.
325 | });
326 |
327 | 328 | x 329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 | Vanilla Example 345 |

346 | var el = document.getElementById('vanilla-demo');
347 | var vanilla = new Croppie(el, {
348 |     viewport: { width: 100, height: 100 },
349 |     boundary: { width: 300, height: 300 },
350 |     showZoomer: false,
351 |     enableOrientation: true
352 | });
353 | vanilla.bind({
354 |     url: 'demo/demo-2.jpg',
355 |     orientation: 4
356 | });
357 | //on button click
358 | vanilla.result('blob').then(function(blob) {
359 |     // do something with cropped blob
360 | });
361 |
362 | 363 | 364 | 365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 | Resizer Example 375 |

376 | var el = document.getElementById('resizer-demo');
377 | var resize = new Croppie(el, {
378 |     viewport: { width: 100, height: 100 },
379 |     boundary: { width: 300, height: 300 },
380 |     showZoomer: false,
381 |     enableResize: true,
382 |     enableOrientation: true,
383 |     mouseWheelZoom: 'ctrl'
384 | });
385 | resize.bind({
386 |     url: 'demo/demo-2.jpg',
387 | });
388 | //on button click
389 | resize.result('blob').then(function(blob) {
390 |     // do something with cropped blob
391 | });
392 |
393 | 394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 | Upload Example (with exif orientation compatability) 407 |

408 | $uploadCrop = $('#upload-demo').croppie({
409 |     enableExif: true,
410 |     viewport: {
411 |         width: 200,
412 |         height: 200,
413 |         type: 'circle'
414 |     },
415 |     boundary: {
416 |         width: 300,
417 |         height: 300
418 |     }
419 | });
420 |
421 | 422 | Upload 423 | 424 | 425 | 426 |
427 |
428 |
429 |
430 | Upload a file to start cropping 431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 | Hidden Example 444 |

When binding a croppie element that isn't visible, i.e., in a modal - you'll need to call bind again on your croppie element, to indicate to croppie that the position has changed and it needs to recalculate its points.

445 | 446 |

447 | $('#hidden-demo').croppie('bind')
448 |
449 | 450 |
451 |
452 |
453 | 454 |
455 |
456 |
457 |
458 |
459 | 460 |
461 | 462 |

Important Notes

463 |
464 |
465 | 466 |

Image Hosting & Cross Origin Errors

467 |

Croppie uses canvas.drawImage(...) to manipulate images. Thus, images must obey the CORS policy. More info can be found here.

468 |
469 |
470 | 471 |

Visibility and Binding

472 |

Croppie is dependent on it's container being visible when the bind method is called. This can be an issue when your croppie component is inside a modal that isn't shown. Let's take the bootstrap modal for example..

473 |

474 | var myCroppie = $('#my-croppie').croppie(opts);
475 | $('#my-modal').on('shown.bs.modal', function(){ 
476 |     myCroppie.croppie('bind', bindOpts);
477 | });
478 |                     
479 |

If you are having issues getting the correct crop result, and your croppie instance is shown inside of a modal, try taking your croppie out of the modal and see if your issues persist. If they don't, then make sure that your bind method is called after the modal is done animating.

480 |
481 |
482 |
483 | 484 |
485 | 486 |
487 |

Browser Support

488 |
489 |
490 |

Croppie is supported in the following browsers:

491 |
    492 |
  • Firefox 10+
  • 493 |
  • Chrome 12+
  • 494 |
  • IE 10+
  • 495 |
  • Edge
  • 496 |
  • Safari 4+
  • 497 |
  • Opera 15+
  • 498 |
  • iOS
  • 499 |
  • Android
  • 500 |
501 |
502 |
503 |
504 | 505 |
506 |

Who

507 |
508 |
509 |

510 | Croppie was built by Foliotek for use in Foliotek Presentation. 511 |

512 |

513 | Please submit any issues or questions on Github. 514 |

515 |

516 | Check out some of our other open source tools: AjaxQ | LTI Tester 517 |

518 |
519 |
520 |
521 | Copyright © 2017 | Foliotek 522 |
523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 535 | 543 | 544 | 545 | 546 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "croppie", 3 | "version": "2.6.5", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "array-union": { 8 | "version": "1.0.2", 9 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 10 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", 11 | "dev": true, 12 | "requires": { 13 | "array-uniq": "^1.0.1" 14 | } 15 | }, 16 | "array-uniq": { 17 | "version": "1.0.3", 18 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 19 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", 20 | "dev": true 21 | }, 22 | "arrify": { 23 | "version": "1.0.1", 24 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 25 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", 26 | "dev": true 27 | }, 28 | "async": { 29 | "version": "1.5.2", 30 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 31 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", 32 | "dev": true 33 | }, 34 | "balanced-match": { 35 | "version": "1.0.0", 36 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 37 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 38 | "dev": true 39 | }, 40 | "brace-expansion": { 41 | "version": "1.1.11", 42 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 43 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 44 | "dev": true, 45 | "requires": { 46 | "balanced-match": "^1.0.0", 47 | "concat-map": "0.0.1" 48 | } 49 | }, 50 | "collections": { 51 | "version": "0.2.2", 52 | "resolved": "https://registry.npmjs.org/collections/-/collections-0.2.2.tgz", 53 | "integrity": "sha1-HyMCay7zb5J+7MkB6ZxfDUj6M04=", 54 | "dev": true, 55 | "requires": { 56 | "weak-map": "1.0.0" 57 | } 58 | }, 59 | "commander": { 60 | "version": "2.9.0", 61 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", 62 | "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", 63 | "dev": true, 64 | "requires": { 65 | "graceful-readlink": ">= 1.0.0" 66 | } 67 | }, 68 | "concat-map": { 69 | "version": "0.0.1", 70 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 71 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 72 | "dev": true 73 | }, 74 | "debug": { 75 | "version": "2.2.0", 76 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", 77 | "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", 78 | "dev": true, 79 | "requires": { 80 | "ms": "0.7.1" 81 | } 82 | }, 83 | "diff": { 84 | "version": "1.4.0", 85 | "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", 86 | "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", 87 | "dev": true 88 | }, 89 | "escape-string-regexp": { 90 | "version": "1.0.2", 91 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", 92 | "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=", 93 | "dev": true 94 | }, 95 | "gh-pages": { 96 | "version": "0.11.0", 97 | "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-0.11.0.tgz", 98 | "integrity": "sha1-kzE8bcv8dNQmvIminr/2QgrMPBs=", 99 | "dev": true, 100 | "requires": { 101 | "async": "1.5.2", 102 | "commander": "2.9.0", 103 | "globby": "^4.0.0", 104 | "graceful-fs": "4.1.2", 105 | "q": "1.4.1", 106 | "q-io": "1.13.2", 107 | "wrench": "1.5.8" 108 | } 109 | }, 110 | "glob": { 111 | "version": "6.0.4", 112 | "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", 113 | "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", 114 | "dev": true, 115 | "requires": { 116 | "inflight": "^1.0.4", 117 | "inherits": "2", 118 | "minimatch": "2 || 3", 119 | "once": "^1.3.0", 120 | "path-is-absolute": "^1.0.0" 121 | } 122 | }, 123 | "globby": { 124 | "version": "4.1.0", 125 | "resolved": "https://registry.npmjs.org/globby/-/globby-4.1.0.tgz", 126 | "integrity": "sha1-CA9UVJ7BuCpsYOYx/ILhIR2+lfg=", 127 | "dev": true, 128 | "requires": { 129 | "array-union": "^1.0.1", 130 | "arrify": "^1.0.0", 131 | "glob": "^6.0.1", 132 | "object-assign": "^4.0.1", 133 | "pify": "^2.0.0", 134 | "pinkie-promise": "^2.0.0" 135 | } 136 | }, 137 | "graceful-fs": { 138 | "version": "4.1.2", 139 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.2.tgz", 140 | "integrity": "sha1-/iI5t1dJcuZ+QfgIgj+b+kqZHjc=", 141 | "dev": true 142 | }, 143 | "graceful-readlink": { 144 | "version": "1.0.1", 145 | "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", 146 | "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", 147 | "dev": true 148 | }, 149 | "growl": { 150 | "version": "1.8.1", 151 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz", 152 | "integrity": "sha1-Sy3sjZB+k9szZiTc7AGDUC+MlCg=", 153 | "dev": true 154 | }, 155 | "inflight": { 156 | "version": "1.0.6", 157 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 158 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 159 | "dev": true, 160 | "requires": { 161 | "once": "^1.3.0", 162 | "wrappy": "1" 163 | } 164 | }, 165 | "inherits": { 166 | "version": "2.0.3", 167 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 168 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 169 | "dev": true 170 | }, 171 | "jade": { 172 | "version": "0.26.3", 173 | "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", 174 | "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", 175 | "dev": true, 176 | "requires": { 177 | "commander": "0.6.1", 178 | "mkdirp": "0.3.0" 179 | }, 180 | "dependencies": { 181 | "commander": { 182 | "version": "0.6.1", 183 | "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", 184 | "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", 185 | "dev": true 186 | }, 187 | "mkdirp": { 188 | "version": "0.3.0", 189 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", 190 | "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", 191 | "dev": true 192 | } 193 | } 194 | }, 195 | "lru-cache": { 196 | "version": "2.7.3", 197 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", 198 | "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", 199 | "dev": true 200 | }, 201 | "mime": { 202 | "version": "1.6.0", 203 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 204 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 205 | "dev": true 206 | }, 207 | "mimeparse": { 208 | "version": "0.1.4", 209 | "resolved": "https://registry.npmjs.org/mimeparse/-/mimeparse-0.1.4.tgz", 210 | "integrity": "sha1-2vsCdSNw/SJgk64xUsJxrwGsJUo=", 211 | "dev": true 212 | }, 213 | "minimatch": { 214 | "version": "3.0.4", 215 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 216 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 217 | "dev": true, 218 | "requires": { 219 | "brace-expansion": "^1.1.7" 220 | } 221 | }, 222 | "minimist": { 223 | "version": "0.0.8", 224 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 225 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 226 | "dev": true 227 | }, 228 | "mkdirp": { 229 | "version": "0.5.1", 230 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 231 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 232 | "dev": true, 233 | "requires": { 234 | "minimist": "0.0.8" 235 | } 236 | }, 237 | "mocha": { 238 | "version": "2.4.5", 239 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.4.5.tgz", 240 | "integrity": "sha1-FRdo3Sh161G8gpXpgAAm6fK7OY8=", 241 | "dev": true, 242 | "requires": { 243 | "commander": "2.3.0", 244 | "debug": "2.2.0", 245 | "diff": "1.4.0", 246 | "escape-string-regexp": "1.0.2", 247 | "glob": "3.2.3", 248 | "growl": "1.8.1", 249 | "jade": "0.26.3", 250 | "mkdirp": "0.5.1", 251 | "supports-color": "1.2.0" 252 | }, 253 | "dependencies": { 254 | "commander": { 255 | "version": "2.3.0", 256 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", 257 | "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=", 258 | "dev": true 259 | }, 260 | "glob": { 261 | "version": "3.2.3", 262 | "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", 263 | "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", 264 | "dev": true, 265 | "requires": { 266 | "graceful-fs": "~2.0.0", 267 | "inherits": "2", 268 | "minimatch": "~0.2.11" 269 | } 270 | }, 271 | "graceful-fs": { 272 | "version": "2.0.3", 273 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", 274 | "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=", 275 | "dev": true 276 | }, 277 | "minimatch": { 278 | "version": "0.2.14", 279 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", 280 | "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", 281 | "dev": true, 282 | "requires": { 283 | "lru-cache": "2", 284 | "sigmund": "~1.0.0" 285 | } 286 | } 287 | } 288 | }, 289 | "ms": { 290 | "version": "0.7.1", 291 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", 292 | "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", 293 | "dev": true 294 | }, 295 | "object-assign": { 296 | "version": "4.1.1", 297 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 298 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 299 | "dev": true 300 | }, 301 | "once": { 302 | "version": "1.4.0", 303 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 304 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 305 | "dev": true, 306 | "requires": { 307 | "wrappy": "1" 308 | } 309 | }, 310 | "path-is-absolute": { 311 | "version": "1.0.1", 312 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 313 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 314 | "dev": true 315 | }, 316 | "pify": { 317 | "version": "2.3.0", 318 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 319 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", 320 | "dev": true 321 | }, 322 | "pinkie": { 323 | "version": "2.0.4", 324 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 325 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", 326 | "dev": true 327 | }, 328 | "pinkie-promise": { 329 | "version": "2.0.1", 330 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 331 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 332 | "dev": true, 333 | "requires": { 334 | "pinkie": "^2.0.0" 335 | } 336 | }, 337 | "q": { 338 | "version": "1.4.1", 339 | "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", 340 | "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", 341 | "dev": true 342 | }, 343 | "q-io": { 344 | "version": "1.13.2", 345 | "resolved": "https://registry.npmjs.org/q-io/-/q-io-1.13.2.tgz", 346 | "integrity": "sha1-7qEw1IHdteGqG8WmaFX3OR0G8AM=", 347 | "dev": true, 348 | "requires": { 349 | "collections": "^0.2.0", 350 | "mime": "^1.2.11", 351 | "mimeparse": "^0.1.4", 352 | "q": "^1.0.1", 353 | "qs": "^1.2.1", 354 | "url2": "^0.0.0" 355 | } 356 | }, 357 | "qs": { 358 | "version": "1.2.2", 359 | "resolved": "https://registry.npmjs.org/qs/-/qs-1.2.2.tgz", 360 | "integrity": "sha1-GbV/8k3CqZzh+L32r82ln472H4g=", 361 | "dev": true 362 | }, 363 | "sigmund": { 364 | "version": "1.0.1", 365 | "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", 366 | "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", 367 | "dev": true 368 | }, 369 | "source-map": { 370 | "version": "0.6.1", 371 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 372 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 373 | "dev": true 374 | }, 375 | "supports-color": { 376 | "version": "1.2.0", 377 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", 378 | "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=", 379 | "dev": true 380 | }, 381 | "uglify-js": { 382 | "version": "3.3.15", 383 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.15.tgz", 384 | "integrity": "sha512-bqtBCAINYXX/OkdnqMGpbXr+OPWc00hsozRpk+dAtfnbdk2jjKiLmyOkQ7zamg648lVMnzATL8JrSN6LmaVpYA==", 385 | "dev": true, 386 | "requires": { 387 | "commander": "~2.15.0", 388 | "source-map": "~0.6.1" 389 | }, 390 | "dependencies": { 391 | "commander": { 392 | "version": "2.15.1", 393 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", 394 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", 395 | "dev": true 396 | } 397 | } 398 | }, 399 | "url2": { 400 | "version": "0.0.0", 401 | "resolved": "https://registry.npmjs.org/url2/-/url2-0.0.0.tgz", 402 | "integrity": "sha1-Tqq9HVw6yQ1iq0SFyZhCKGWgSxo=", 403 | "dev": true 404 | }, 405 | "weak-map": { 406 | "version": "1.0.0", 407 | "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.0.tgz", 408 | "integrity": "sha1-tm5Wqd8L0lp2u/G1FNsSkIBhSjc=", 409 | "dev": true 410 | }, 411 | "wrappy": { 412 | "version": "1.0.2", 413 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 414 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 415 | "dev": true 416 | }, 417 | "wrench": { 418 | "version": "1.5.8", 419 | "resolved": "https://registry.npmjs.org/wrench/-/wrench-1.5.8.tgz", 420 | "integrity": "sha1-ejHJf3hpJG12xc8vXJd6HEyOWrU=", 421 | "dev": true 422 | } 423 | } 424 | } 425 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "croppie", 3 | "version": "2.6.5", 4 | "description": "A simple javascript image cropper", 5 | "main": "croppie.js", 6 | "devDependencies": { 7 | "gh-pages": "^0.11.0", 8 | "mocha": "2.4.5", 9 | "uglify-js": "3.3.15" 10 | }, 11 | "scripts": { 12 | "test": "mocha test/unit", 13 | "deploy": "node deploy.js", 14 | "uglify": "uglifyjs croppie.js -c -m -r '$,require,exports' -o croppie.min.js" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/Foliotek/Croppie.git" 19 | }, 20 | "keywords": [ 21 | "crop", 22 | "cropper", 23 | "image", 24 | "cropping" 25 | ], 26 | "authors": [ 27 | "Dustin Smith ", 28 | "Ethan Calvert " 29 | ], 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/Foliotek/Croppie/issues" 33 | }, 34 | "homepage": "http://foliotek.github.io/Croppie", 35 | "files": [ 36 | "croppie.css", 37 | "croppie.js", 38 | "croppie.min.js" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /test/unit/Croppie.js: -------------------------------------------------------------------------------- 1 | var assert, Croppie; 2 | 3 | assert = require('assert'); 4 | 5 | require('./stubs/window'); 6 | Croppie = require('../../croppie'); 7 | 8 | describe('Croppie', function () { 9 | var testCroppieObject, stubElement; 10 | 11 | beforeEach(function () { 12 | stubElement = new HTMLElement(); 13 | testCroppieObject = new Croppie(stubElement); 14 | }); 15 | 16 | describe('constructor', function () { 17 | it('should expose a reference to its bound element.', function () { 18 | assert.strictEqual(testCroppieObject.element, stubElement); 19 | }); 20 | 21 | it('should use croppy defaults if no options are provided.', function () { 22 | function matchDefaults(actualOptionGroup, expectedOptionGroup, path) { 23 | path = path || 'options'; 24 | 25 | Object 26 | .keys(expectedOptionGroup) 27 | .forEach(function (optionName) { 28 | var currentPath; 29 | 30 | currentPath = [ 31 | path, 32 | optionName 33 | ].join('.'); 34 | 35 | if (typeof expectedOptionGroup[optionName] === 'object') { 36 | matchDefaults(actualOptionGroup[optionName], expectedOptionGroup[optionName], currentPath); 37 | } else { 38 | assert.equal(actualOptionGroup[optionName], expectedOptionGroup[optionName], 'Matching ' + currentPath); 39 | } 40 | }); 41 | } 42 | 43 | matchDefaults(testCroppieObject.options, Croppie.defaults); 44 | }); 45 | 46 | }); 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /test/unit/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --recursive 3 | --growl 4 | -------------------------------------------------------------------------------- /test/unit/stubs/DOMTokenList.js: -------------------------------------------------------------------------------- 1 | DOMTokenList = function DOMTokenList() { 2 | this.add = function () { 3 | }; 4 | this.remove = function () { 5 | }; 6 | this.item = function () { 7 | }; 8 | this.toggle = function () { 9 | }; 10 | this.contains = function () { 11 | }; 12 | }; 13 | 14 | module.exports = DOMTokenList; -------------------------------------------------------------------------------- /test/unit/stubs/EventTarget.js: -------------------------------------------------------------------------------- 1 | EventTarget = function EventTarget() { 2 | this.addEventListener = function () { 3 | }; 4 | this.removeEventListener = function () { 5 | }; 6 | this.dispatchEvent = function () { 7 | }; 8 | }; 9 | 10 | module.exports = EventTarget; -------------------------------------------------------------------------------- /test/unit/stubs/HTMLElement.js: -------------------------------------------------------------------------------- 1 | DOMTokenList = require('./DOMTokenList'); 2 | EventTarget = require('./EventTarget'); 3 | 4 | HTMLElement = function HTMLElement() { 5 | EventTarget.apply(this, arguments); 6 | this.style = {}; 7 | this.classList = new DOMTokenList(); 8 | this.appendChild = function () { 9 | }; 10 | }; 11 | HTMLElement.prototype = Object.create(EventTarget.prototype); 12 | 13 | module.exports = HTMLElement; -------------------------------------------------------------------------------- /test/unit/stubs/document.js: -------------------------------------------------------------------------------- 1 | var HTMLElement; 2 | 3 | HTMLElement = require('./HTMLElement'); 4 | 5 | document = { 6 | createElement : function () { 7 | return new HTMLElement(); 8 | } 9 | }; 10 | 11 | module.exports = document; -------------------------------------------------------------------------------- /test/unit/stubs/window.js: -------------------------------------------------------------------------------- 1 | var document; 2 | 3 | document = require('./document'); 4 | 5 | window = { 6 | document : document 7 | }; 8 | 9 | module.exports = window; --------------------------------------------------------------------------------