[
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n# dist/*.js\n\n# Dependency directory\n# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git\nnode_modules\n\n# IDE\n.idea\ndemo/**/*.bundle.js\n\n# Prototype File\nprototype"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 饿了么前端\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.MD",
    "content": "# Image Cropper\nA image cropper for cropping user avatar, no dependencies. \n\nDocument & Demo: http://elemefe.github.io/image-cropper/\n\n# Build & Watch\n\nBefore Build & Watch, you should install browserify & watchify use:\n\n```Shell\nnpm install\n```\n\nBuild your own js file after modify source code (including angular & standalone):\n\n```Shell\nnpm run build\n```\n\nBuild standalone dist:\n\n```Shell\nnpm run build-plain\n```\n\nWatch source code then auto build:\n\n```Shell\nnpm run watch\n```"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"image-cropper\",\n  \"main\": \"dist/cropper.js\",\n  \"version\": \"0.3.1\",\n  \"authors\": [\n    \"long.zhang <long.zhang@ele.me>\"\n  ],\n  \"license\": \"MIT\",\n  \"ignore\": [\n    \"demo\",\n    \"test\"\n  ]\n}"
  },
  {
    "path": "demo/angular-demo.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Angular Demo</title>\n  <link href=\"cropper-demo.css\" rel=\"stylesheet\" />\n  <link href=\"../dist/cropper.css\" rel=\"stylesheet\"/>\n  <script type=\"text/javascript\" src=\"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.28/angular.js\"></script>\n  <script type=\"text/javascript\" src=\"../dist/cropper.js\"></script>\n</head>\n<body>\n<form method=\"post\" action=\"#\" enctype=\"multipart/form-data\" ng-app=\"cropperDemo\" ng-controller=\"demoController\">\n  <p>\n    <button class=\"btn-upload btn-lg\">Select</button>\n    <input type=\"file\" name=\"avatar\" cropper-source=\"avatar\" cropper-file-types=\"jpg,jpeg,png\" />\n    Support formats: JPG, PNG\n  </p>\n  <div class=\"preview-container\">\n    <div class=\"image-container target\" cropper=\"avatar\" cropper-context=\"cropContext\">\n      <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n    </div>\n    <div class=\"large-wrapper\">\n      <div class=\"image-container large\" cropper-preview=\"avatar\">\n        <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n      </div>\n      <p>Large</p>\n    </div>\n    <div>\n      <div class=\"image-container medium\" cropper-preview=\"avatar\">\n        <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n      </div>\n      <p>Medium</p>\n      <div class=\"image-container small\" cropper-preview=\"avatar\">\n        <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n      </div>\n      <p>Small</p>\n    </div>\n  </div>\n\n  <input type=\"hidden\" name=\"x\" value=\"{{cropContext.left}}\"/>\n  <input type=\"hidden\" name=\"y\" value=\"{{cropContext.top}}\"/>\n  <input type=\"hidden\" name=\"w\" value=\"{{cropContext.width}}\"/>\n  <input type=\"hidden\" name=\"h\" value=\"{{cropContext.height}}\"/>\n</form>\n<script>\n  angular.module('cropperDemo', ['cropper'])\n    .controller('demoController', function ($scope) {\n      $scope.cropContext = {};\n    });\n</script>\n</body>\n</html>"
  },
  {
    "path": "demo/cropper-demo.css",
    "content": "form {\n  margin-left: 18px;\n}\n\nform > p {\n  padding-top: 22px;\n  line-height: 40px;\n  font-size: 14px;\n  position: relative;\n  color: #666;\n  margin-bottom: 30px;\n  overflow: hidden;\n}\n\nform > p .btn-upload {\n  width: 120px;\n  padding: 12px 20px;\n  font-size: 14px;\n  border: 1px solid #1e89e0;\n  background-color: transparent;\n  color: #1e89e0;\n  margin-right: 20px;\n}\n\nform > p input {\n  position: absolute;\n  font-size: 480px;\n  top: 22px;\n  left: -30px;\n  height: 42px;\n  width: 150px;\n  opacity: 0;\n  -ms-filter: \"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)\";\n  cursor: pointer;\n}\n\nform .btn-submit {\n  font-size: 14px;\n  padding: 12px 20px;\n  width: 120px;\n  margin-right: 20px;\n}\n\n.preview-container {\n  overflow: hidden;\n  color: #999;\n  margin-bottom: 20px;\n}\n\n.preview-container .noavatar {\n  background-color: #efeeef;\n}\n\n.preview-container > div {\n  float: left;\n  height: 320px;\n}\n\n.large-wrapper {\n  border-left: 1px solid #eee;\n  padding-left: 30px;\n  margin-right: 30px;\n}\n\n.image-container {\n  position: relative;\n  overflow: hidden;\n  border: 1px solid #bbb;\n  margin-bottom: 5px;\n}\n\n.image-container .image-wrapper {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  left: 0;\n  top: 0;\n  overflow: hidden;\n}\n\n.image-container img {\n  position: absolute;\n}\n\n.image-container.target {\n  width: 320px;\n  height: 320px;\n  margin-right: 30px;\n}\n\n.image-container.target img {\n  width: 320px;\n  height: 320px;\n}\n\n.image-container.large {\n  width: 210px;\n  height: 210px;\n}\n\n.image-container.large img {\n  width: 210px;\n  height: 210px;\n}\n\n.image-container.large .noavatar {\n  background-position: -320px 0;\n}\n\n.image-container.medium {\n  width: 48px;\n  height: 48px;\n}\n\n.image-container.medium img {\n  width: 48px;\n  height: 48px;\n}\n\n.image-container.medium .noavatar {\n  background-position: -324px -210px;\n}\n\n.image-container.small {\n  width: 20px;\n  height: 20px;\n  margin-top: 30px;\n}\n\n.image-container.small img {\n  width: 20px;\n  height: 20px;\n}\n\n.image-container.small .noavatar {\n  background-position: -373px -210px;\n}"
  },
  {
    "path": "demo/standalone-noar.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Standalone demo</title>\n  <link href=\"cropper-demo.css\" rel=\"stylesheet\" />\n  <link href=\"../dist/cropper.css\" rel=\"stylesheet\"/>\n  <script type=\"text/javascript\" src=\"../dist/cropper.js\"></script>\n</head>\n<body>\n<form method=\"post\" action=\"#\" enctype=\"multipart/form-data\">\n  <p>\n    <button class=\"btn-upload btn-lg\">Select</button>\n    <input type=\"file\" name=\"avatar\" id=\"cropper-input\"/>\n    Support formats: JPG, PNG\n  </p>\n  <div class=\"preview-container\">\n    <div class=\"image-container target\" id=\"cropper-target\">\n      <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n    </div>\n    <div class=\"large-wrapper\">\n      <div class=\"image-container large\" id=\"preview-large\">\n        <div class=\"image-wrapper\">\n          <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n        </div>\n      </div>\n      <p>Large</p>\n    </div>\n    <div>\n      <div class=\"image-container medium\" id=\"preview-medium\">\n        <div class=\"image-wrapper\">\n          <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n        </div>\n      </div>\n      <p>Medium</p>\n      <div class=\"image-container small\" id=\"preview-small\">\n        <div class=\"image-wrapper\">\n          <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n        </div>\n      </div>\n      <p>Small</p>\n    </div>\n  </div>\n\n  <input type=\"hidden\" name=\"x\" value=\"{{cropContext.left}}\"/>\n  <input type=\"hidden\" name=\"y\" value=\"{{cropContext.top}}\"/>\n  <input type=\"hidden\" name=\"w\" value=\"{{cropContext.width}}\"/>\n  <input type=\"hidden\" name=\"h\" value=\"{{cropContext.height}}\"/>\n</form>\n<script>\n  var cropper = new Cropper({\n    aspectRatio: 'auto',\n    element: document.getElementById('cropper-target'),\n    previews: [\n      document.getElementById('preview-large'),\n      document.getElementById('preview-medium'),\n      document.getElementById('preview-small')\n    ],\n    onCroppedRectChange: function(rect) {\n      console.log(rect);\n    }\n  });\n  var input = document.getElementById('cropper-input');\n  input.onchange = function() {\n    if (typeof FileReader !== 'undefined') {\n      var reader = new FileReader();\n      reader.onload = function (event) {\n        cropper.setImage(event.target.result);\n      };\n      if (input.files && input.files[0]) {\n        reader.readAsDataURL(input.files[0]);\n      }\n    } else { // IE10-\n      input.select();\n      input.blur();\n\n      var src = document.selection.createRange().text;\n      cropper.setImage(src);\n    }\n  };\n</script>\n</body>\n</html>"
  },
  {
    "path": "demo/standalone.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Standalone demo</title>\n  <link href=\"cropper-demo.css\" rel=\"stylesheet\" />\n  <link href=\"../dist/cropper.css\" rel=\"stylesheet\"/>\n  <script type=\"text/javascript\" src=\"../dist/cropper.js\"></script>\n</head>\n<body>\n<form method=\"post\" action=\"#\" enctype=\"multipart/form-data\">\n  <p>\n    <button class=\"btn-upload btn-lg\">Select</button>\n    <input type=\"file\" name=\"avatar\" id=\"cropper-input\"/>\n    Support formats: JPG, PNG\n  </p>\n  <div class=\"preview-container\">\n    <div class=\"image-container target\" id=\"cropper-target\">\n      <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n    </div>\n    <div class=\"large-wrapper\">\n      <div class=\"image-container large\" id=\"preview-large\">\n        <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n      </div>\n      <p>Large</p>\n    </div>\n    <div>\n      <div class=\"image-container medium\" id=\"preview-medium\">\n        <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n      </div>\n      <p>Medium</p>\n      <div class=\"image-container small\" id=\"preview-small\">\n        <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n      </div>\n      <p>Small</p>\n    </div>\n  </div>\n\n  <input type=\"hidden\" name=\"x\" value=\"{{cropContext.left}}\"/>\n  <input type=\"hidden\" name=\"y\" value=\"{{cropContext.top}}\"/>\n  <input type=\"hidden\" name=\"w\" value=\"{{cropContext.width}}\"/>\n  <input type=\"hidden\" name=\"h\" value=\"{{cropContext.height}}\"/>\n</form>\n<script>\n  var cropper = new Cropper({\n    element: document.getElementById('cropper-target'),\n    previews: [\n      document.getElementById('preview-large'),\n      document.getElementById('preview-medium'),\n      document.getElementById('preview-small')\n    ],\n    onCroppedRectChange: function(rect) {\n      console.log(rect);\n    }\n  });\n  var input = document.getElementById('cropper-input');\n  input.onchange = function() {\n    if (typeof FileReader !== 'undefined') {\n      var reader = new FileReader();\n      reader.onload = function (event) {\n        cropper.setImage(event.target.result);\n      };\n      if (input.files && input.files[0]) {\n        reader.readAsDataURL(input.files[0]);\n      }\n    } else { // IE10-\n      input.select();\n      input.blur();\n\n      var src = document.selection.createRange().text;\n      cropper.setImage(src);\n    }\n  };\n</script>\n</body>\n</html>"
  },
  {
    "path": "dist/cropper.css",
    "content": ".resizer {\n  position: absolute;\n  box-sizing: border-box;\n  border: 1px dashed gray;\n  background-color: transparent;\n  cursor: move;\n}\n\n.resizer .resize-handle {\n  position: absolute;\n  background-color: #333;\n  opacity: 0.5;\n  filter: alpha(opacity=50);\n  font-size: 1px;\n  height: 7px;\n  width: 7px;\n}\n\n.resizer .resize-handle.ord-n {\n  cursor: n-resize;\n  left: 50%;\n  margin-left: -4px;\n  margin-top: -1px;\n  top: 0;\n}\n\n.resizer .resize-handle.ord-s {\n  cursor: s-resize;\n  bottom: 0;\n  left: 50%;\n  margin-bottom: -1px;\n  margin-left: -4px;\n}\n\n.resizer .resize-handle.ord-e {\n  cursor: e-resize;\n  margin-right: -1px;\n  margin-top: -4px;\n  right: 0;\n  top: 50%;\n}\n\n.resizer .resize-handle.ord-w {\n  cursor: w-resize;\n  left: 0;\n  margin-left: -1px;\n  margin-top: -4px;\n  top: 50%;\n}\n\n.resizer .resize-handle.ord-nw {\n  cursor: nw-resize;\n  left: 0;\n  margin-left: -1px;\n  margin-top: -1px;\n  top: 0;\n}\n\n.resizer .resize-handle.ord-ne {\n  cursor: ne-resize;\n  margin-right: -1px;\n  margin-top: -1px;\n  right: 0;\n  top: 0;\n}\n\n.resizer .resize-handle.ord-se {\n  cursor: se-resize;\n  bottom: 0;\n  margin-bottom: -1px;\n  margin-right: -1px;\n  right: 0;\n}\n\n.resizer .resize-handle.ord-sw {\n  cursor: sw-resize;\n  bottom: 0;\n  left: 0;\n  margin-bottom: -1px;\n  margin-left: -1px;\n}\n\n.resizer .resize-bar.ord-n, .resizer .resize-bar.ord-s {\n  position: absolute;\n  height: 7px;\n  width: 100%;\n}\n\n.resizer .resize-bar.ord-e, .resizer .resize-bar.ord-w {\n  position: absolute;\n  height: 100%;\n  width: 7px;\n}\n\n.resizer .resize-bar.ord-n {\n  cursor: n-resize;\n  margin-top: -1px;\n}\n\n.resizer .resize-bar.ord-s {\n  cursor: s-resize;\n  bottom: 0;\n  margin-bottom: -1px;\n}\n\n.resizer .resize-bar.ord-e {\n  cursor: e-resize;\n  margin-right: -1px;\n  right: 0;\n}\n\n.resizer .resize-bar.ord-w {\n  cursor: w-resize;\n  margin-left: -1px;\n}\n\n.resizer .inner-rect {\n  position: absolute;\n  left: 0;\n  top: 0;\n  right: 0;\n  bottom: 0;\n  background-color: white;\n  opacity: 0;\n  filter: alpha(opacity=0);\n}\n\n.cropper {\n  position: absolute;\n  box-sizing: border-box;\n}\n\n.cropper .mask {\n  width: 100%;\n  height: 100%;\n  opacity: 0.4;\n  filter: alpha(opacity=40);\n  display: block;\n  background-color: black;\n}\n\n.cropper .resizer .wrapper {\n  position: absolute;\n  left: 0;\n  top: 0;\n  right: 0;\n  bottom: 0;\n\n  overflow: hidden;\n}\n\n.cropper .resizer .wrapper img {\n  position: absolute;\n  -webkit-user-select: none;\n  -webkit-user-drag: none;\n}"
  },
  {
    "path": "dist/cropper.js",
    "content": "(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){\nvar buildDom = require('./build-dom');\nvar draggable = require('./draggable');\n\nvar configMap = {\n  'n':  { top: true, height: -1 },\n  'w':  { left: true, width: -1 },\n  'e':  { width: 1 },\n  's':  { height: 1 },\n  'nw': { left: true, top: true, width: -1, height: -1 },\n  'ne': { top: true, width: 1, height: -1 },\n  'sw': { left: true, width: -1, height: 1 },\n  'se': { width: 1, height: 1 }\n};\n\nvar getPosition = function (element) {\n  var selfRect = element.getBoundingClientRect();\n  var parentRect = element.offsetParent.getBoundingClientRect();\n\n  return {\n    left: selfRect.left - parentRect.left,\n    top: selfRect.top - parentRect.top\n  };\n};\n\nvar Resizer = function(options) {\n  for (var prop in options) {\n    if (options.hasOwnProperty(prop)) this[prop] = options[prop];\n  }\n};\n\nResizer.prototype.doOnStateChange = function(state) {\n};\n\nResizer.prototype.makeDraggable = function(dom) {\n  var self = this;\n  var dragState = {};\n  var containment;\n\n  draggable(dom, {\n    start: function (event) {\n      var parentNode = dom.parentNode;\n      containment = {\n        left: 0,\n        top: 0,\n        width: parentNode.clientWidth,\n        height: parentNode.clientHeight,\n        right: parentNode.clientWidth,\n        bottom: parentNode.clientHeight\n      };\n\n      dragState.startLeft = event.clientX;\n      dragState.startTop = event.clientY;\n\n      var position = getPosition(dom);\n\n      dragState.resizerStartLeft = position.left;\n      dragState.resizerStartTop = position.top;\n      dragState.resizerStartWidth = dom.offsetWidth;\n      dragState.resizerStartHeight = dom.offsetHeight;\n    },\n    drag: function (event) {\n      var offsetLeft = event.clientX - dragState.startLeft;\n      var offsetTop = event.clientY - dragState.startTop;\n\n      var left = dragState.resizerStartLeft + offsetLeft;\n      var top = dragState.resizerStartTop + offsetTop;\n\n      if (left < containment.left) {\n        left = containment.left;\n      }\n\n      if (top < containment.top) {\n        top = containment.top;\n      }\n\n      if (left + dragState.resizerStartWidth > containment.right) {\n        left = containment.right - dragState.resizerStartWidth;\n      }\n\n      if (top + dragState.resizerStartHeight > containment.bottom) {\n        top = containment.bottom - dragState.resizerStartHeight;\n      }\n\n      dom.style.left = left + 'px';\n      dom.style.top = top + 'px';\n\n      self.doOnStateChange();\n    },\n    end: function () {\n      dragState = {};\n      if (self.doOnDragEnd) {\n        self.doOnDragEnd();\n      }\n    }\n  });\n};\n\nResizer.prototype.bindResizeEvent = function(dom) {\n  var self = this;\n  var resizeState = {};\n  var aspectRatio = self.aspectRatio;\n\n  if (typeof aspectRatio !== 'number') {\n    aspectRatio = undefined;\n  }\n\n  var makeResizable = function (bar) {\n    var type = bar.className.split(' ')[0];\n    var transformMap = configMap[type.substr(4)];\n\n    var containment;\n\n    draggable(bar, {\n      start: function (event) {\n        var parentNode = dom.parentNode;\n        containment = {\n          left: 0,\n          top: 0,\n          width: parentNode.clientWidth,\n          height: parentNode.clientHeight,\n          right: parentNode.clientWidth,\n          bottom: parentNode.clientHeight\n        };\n\n        resizeState.startWidth = dom.clientWidth;\n        resizeState.startHeight = dom.clientHeight;\n        resizeState.startLeft = event.clientX;\n        resizeState.startTop = event.clientY;\n\n        var position = getPosition(dom);\n        resizeState.resizerStartLeft = position.left;\n        resizeState.resizerStartTop = position.top;\n      },\n      drag: function (event) {\n        var widthRatio = transformMap.width;\n        var heightRatio = transformMap.height;\n\n        var offsetLeft = event.clientX - resizeState.startLeft;\n        var offsetTop = event.clientY - resizeState.startTop;\n\n        var width, height, minWidth = 50, maxWidth = 10000, minHeight = 50, maxHeight = 10000;\n\n        if (widthRatio !== undefined) {\n          width = resizeState.startWidth + widthRatio * offsetLeft;\n          if (width < minWidth) {\n            width = minWidth;\n          }\n\n          if (maxWidth && width > maxWidth) {\n            width = maxWidth;\n          }\n        }\n\n        if (heightRatio !== undefined) {\n          height = resizeState.startHeight + heightRatio * offsetTop;\n          if (height < minHeight) {\n            height = minHeight;\n          }\n\n          if (maxHeight && height > maxHeight) {\n            height = maxHeight;\n          }\n        }\n\n        if (aspectRatio !== undefined) {\n          if (type === 'ord-n' || type === 'ord-s') {\n            width = height * aspectRatio;\n          } else if (type === 'ord-w' || type === 'ord-e') {\n            height = width / aspectRatio;\n          } else {\n            if (width / height < aspectRatio) {\n              height = width / aspectRatio;\n            } else {\n              width = height * aspectRatio;\n            }\n          }\n        }\n\n        var position = {\n          left: resizeState.resizerStartLeft,\n          top: resizeState.resizerStartTop\n        };\n\n        if (transformMap.left !== undefined) {\n          position.left = resizeState.resizerStartLeft + (width - resizeState.startWidth) * widthRatio;\n        }\n\n        if (transformMap.top !== undefined) {\n          position.top = resizeState.resizerStartTop + (height - resizeState.startHeight) * heightRatio;\n        }\n\n        //=== containment start\n\n        if (width + position.left > containment.right) {\n          width = containment.right - position.left;\n        }\n\n        if (position.left < containment.left) {\n          width -= containment.left - position.left;\n          position.left = containment.left;\n        }\n\n        if (height + position.top > containment.bottom) {\n          height = containment.bottom - position.top;\n        }\n\n        if (position.top < containment.top) {\n          height -= containment.top - position.top;\n          position.top = containment.top;\n        }\n\n        //=== containment end\n\n        if (aspectRatio !== undefined) {\n          if (width / height < aspectRatio) {\n            height = width / aspectRatio;\n          } else {\n            width = height * aspectRatio;\n          }\n        }\n\n        if (transformMap.left !== undefined) {\n          position.left = resizeState.resizerStartLeft + (width - resizeState.startWidth) * widthRatio;\n        }\n\n        if (transformMap.top !== undefined) {\n          position.top = resizeState.resizerStartTop + (height - resizeState.startHeight) * heightRatio;\n        }\n\n        dom.style.width = width + 'px';\n        dom.style.height = height + 'px';\n\n        if (position.left !== undefined) {\n          dom.style.left = position.left + 'px';\n        }\n\n        if (position.top !== undefined) {\n          dom.style.top = position.top + 'px';\n        }\n\n        self.doOnStateChange();\n      },\n      end: function () {\n        if (self.doOnDragEnd) {\n          self.doOnDragEnd();\n        }\n      }\n    });\n  };\n\n  var bars = dom.querySelectorAll('.resize-bar');\n  var handles = dom.querySelectorAll('.resize-handle');\n\n  var i, j;\n\n  for (i = 0, j = bars.length; i < j; i++) {\n    makeResizable(bars[i]);\n  }\n\n  for (i = 0, j = handles.length; i < j; i++) {\n    makeResizable(handles[i]);\n  }\n};\n\nResizer.prototype.render = function(container) {\n  var self = this;\n\n  var dom = buildDom({\n    tag: 'div',\n    className: 'resizer',\n    content: [\n      { tag: 'div', className: 'ord-n resize-bar' },\n      { tag: 'div', className: 'ord-s resize-bar' },\n      { tag: 'div', className: 'ord-w resize-bar' },\n      { tag: 'div', className: 'ord-e resize-bar' },\n      { tag: 'div', className: 'ord-nw resize-handle' },\n      { tag: 'div', className: 'ord-n resize-handle' },\n      { tag: 'div', className: 'ord-ne resize-handle' },\n      { tag: 'div', className: 'ord-w resize-handle' },\n      { tag: 'div', className: 'ord-e resize-handle' },\n      { tag: 'div', className: 'ord-sw resize-handle' },\n      { tag: 'div', className: 'ord-s resize-handle' },\n      { tag: 'div', className: 'ord-se resize-handle' }\n    ]\n  });\n\n  self.dom = dom;\n\n  self.bindResizeEvent(dom);\n  self.makeDraggable(dom);\n\n  if (container) {\n    container.appendChild(dom);\n  }\n\n  return dom;\n};\n\nmodule.exports = Resizer;\n},{\"./build-dom\":3,\"./draggable\":5}],2:[function(require,module,exports){\nvar Cropper = require('./cropper');\n\nvar cropperInstances = {};\n\nCropper.getInstance = function(id) {\n  return cropperInstances[id];\n};\n\nangular.module('cropper', [])\n.factory('Cropper', function() {\n  return Cropper;\n})\n.directive('cropper', function() {\n  return {\n    restrict: 'A',\n    scope: {\n      cropperContext: '=',\n      cropperAspectRatio: '@'\n    },\n    link: function(scope, element, attrs) {\n      var id = attrs.cropper;\n      if (!id) throw new Error('cropper id is required');\n      var cropperAspectRatio = scope.cropperAspectRatio;\n\n      if (cropperAspectRatio) {\n        if (/^\\d*(\\.)?\\d+$/g.test(cropperAspectRatio)) {\n          cropperAspectRatio = parseFloat(cropperAspectRatio);\n        }\n      } else {\n        cropperAspectRatio = 1;\n      }\n\n      var cropper = Cropper({ element: element[0], aspectRatio: cropperAspectRatio });\n\n      cropperInstances[id] = cropper;\n\n      var cropperContext = scope.cropperContext;\n\n      cropper.onCroppedRectChange = function(rect) {\n        if (cropperContext) {\n          cropperContext.left = rect.left;\n          cropperContext.top = rect.top;\n          cropperContext.width = rect.width;\n          cropperContext.height = rect.height;\n        }\n        try { scope.$apply(); } catch(e) {}\n      };\n\n      scope.$on('$destroy', function() {\n        cropperInstances[id] = null;\n        delete cropperInstances[id];\n      });\n    }\n  };\n}).directive('cropperPreview', function(){\n  return {\n    restrict: 'A',\n    link: function(scope, element, attrs) {\n      var id = attrs.cropperPreview;\n      if (!id) throw new Error('cropper id is required');\n\n      var cropper = cropperInstances[id];\n\n      if (cropper) {\n        cropper.addPreview(element[0]);\n      }\n    }\n  }\n}).directive('cropperSource', function() {\n  return {\n    restrict: 'A',\n    link: function ($scope, $el, attrs) {\n      var id = attrs.cropperSource;\n      if (!id) throw new Error('cropper id is required');\n\n      var fileValidateRegex = /\\.(jpg|png|gif|jpeg)$/i;\n      var fileTypes = attrs.cropperFileTypes;\n\n      if (fileTypes) {\n        var types = fileTypes.split(',');\n        if (types.length > 0) {\n          fileValidateRegex = new RegExp('\\.(' + types.join('|') + ')$', 'i');\n        }\n      }\n\n      $el.on('change', function () {\n        var input = this;\n        var cropper = cropperInstances[id];\n\n        var fileName = input.value;\n        if (!fileValidateRegex.test(fileName)) {\n          cropper.setImage();\n          return;\n        }\n\n        if (typeof FileReader !== 'undefined') {\n          var reader = new FileReader();\n          reader.onload = function (event) {\n            cropper.setImage(event.target.result);\n          };\n          if (input.files && input.files[0]) {\n            reader.readAsDataURL(input.files[0]);\n          }\n        } else {\n          input.select();\n          input.blur();\n\n          var src = document.selection.createRange().text;\n          cropper.setImage(src);\n        }\n      });\n    }\n  };\n});\n},{\"./cropper\":4}],3:[function(require,module,exports){\nvar buildDOM = function(config, refs) {\n  if (!config) return null;\n  var dom, childElement;\n  if (config.tag) {\n    dom = document.createElement(config.tag);\n    for (var prop in config) {\n      if (config.hasOwnProperty(prop)) {\n        if (prop === 'content' || prop === 'tag') continue;\n        if (prop === 'key' && refs) {\n          var key = config[prop];\n          if (key) {\n            refs[key] = dom;\n          }\n        }\n        dom[prop] = config[prop];\n      }\n    }\n    var content = config.content;\n    if (content instanceof Array) {\n      for (var i = 0, j = content.length; i < j; i++) {\n        var child = content[i];\n        childElement = buildDOM(child, refs);\n        dom.appendChild(childElement);\n      }\n    } else if (typeof content === 'string') {\n      childElement = document.createTextNode(content);\n      dom.appendChild(childElement);\n    }\n  }\n  return dom;\n};\n\nmodule.exports = buildDOM;\n},{}],4:[function(require,module,exports){\nvar Resizer = require('./Resizer');\nvar buildDom = require('./build-dom');\n\nvar blankImage = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';\n\nvar preLoadElement;\n\nvar ieVersion = Number(document.documentMode);\n\nvar getImageSize = function(src, callback) {\n  if (ieVersion < 10) {\n    if (!preLoadElement) {\n      preLoadElement = document.createElement('div');\n      preLoadElement.style.position = 'absolute';\n      preLoadElement.style.width = '1px';\n      preLoadElement.style.height = '1px';\n      preLoadElement.style.left = '-9999px';\n      preLoadElement.style.top = '-9999px';\n      preLoadElement.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=image)';\n      document.body.insertBefore(preLoadElement, document.body.firstChild);\n    }\n\n    preLoadElement.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src;\n\n    var size = {\n      width: preLoadElement.offsetWidth,\n      height: preLoadElement.offsetHeight\n    };\n\n    if (typeof callback === 'function') {\n      callback(size);\n    }\n  } else {\n    var image = new Image();\n    image.onload = function() {\n      var size = {\n        width: image.width,\n        height: image.height\n      };\n      if (typeof callback === 'function') {\n        callback(size);\n      }\n    };\n    image.src = src;\n  }\n};\n\nvar Cropper = function(options) {\n  var cropper = this;\n  if (!(this instanceof Cropper)) {\n    cropper = new Cropper();\n  }\n  cropper.aspectRatio = 1;\n  for (var prop in options) {\n    if (options.hasOwnProperty(prop)) cropper[prop] = options[prop];\n  }\n\n  if (cropper.element) {\n    cropper.render(cropper.element);\n  }\n\n  return cropper;\n};\n\nCropper.prototype.resetResizer = function() {\n  var resizer = this.resizer;\n  var cropperRect = this.cropperRect;\n  var aspectRatio = this.aspectRatio;\n\n  if (typeof aspectRatio !== 'number') {\n    aspectRatio = 1;\n  }\n\n  var width = 100;\n  var height = 100 / aspectRatio;\n\n  var resizerDom = resizer.dom;\n  resizerDom.style.width = width + 'px';\n  resizerDom.style.height = height + 'px';\n\n  if (cropperRect) {\n    resizerDom.style.left = (cropperRect.width - width) / 2 + 'px';\n    resizerDom.style.top = (cropperRect.height - height) / 2 + 'px';\n  } else {\n    resizerDom.style.left = resizerDom.style.top = '';\n  }\n\n  resizer.doOnStateChange();\n  resizer.doOnDragEnd();\n};\n\nCropper.prototype.setImage = function(src) {\n  var element = this.element;\n  var sourceImage = element.querySelector('img');\n  var resizeImage = this.refs.image;\n\n  var self = this;\n\n  if (src === undefined || src === null) {\n    resizeImage.src = sourceImage.src = blankImage;\n    resizeImage.style.width = resizeImage.style.height = resizeImage.style.left = resizeImage.style.top = '';\n    sourceImage.style.width = sourceImage.style.height = sourceImage.style.left = sourceImage.style.top = '';\n\n    self.updatePreview(blankImage);\n\n    self.dom.style.display = 'none';\n    self.resetResizer();\n\n    self.dom.style.left = self.dom.style.top = '';\n    self.dom.style.width = element.offsetWidth + 'px';\n    self.dom.style.height = element.offsetHeight + 'px';\n\n    self.croppedRect = {\n      width: 0,\n      height: 0,\n      left: 0,\n      top: 0\n    };\n\n    self.onCroppedRectChange && self.onCroppedRectChange(self.croppedRect);\n\n    return;\n  }\n\n  getImageSize(src, function(size) {\n    if (ieVersion < 10) {\n      resizeImage.src = sourceImage.src = blankImage;\n      resizeImage.style.filter = sourceImage.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)';\n\n      sourceImage.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src;\n      resizeImage.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src;\n    }\n\n    self.imageSize = size;\n\n    var elementWidth = element.offsetWidth;\n    var elementHeight = element.offsetHeight;\n\n    var dom = self.dom;\n\n    var cropperRect = {};\n\n    if (size.width / size.height > elementWidth / elementHeight) {\n      cropperRect.width = elementWidth;\n      cropperRect.height = elementWidth * size.height / size.width;\n      cropperRect.top = (elementHeight - cropperRect.height) / 2;\n      cropperRect.left = 0;\n    } else {\n      cropperRect.height = elementHeight;\n      cropperRect.width = elementHeight * size.width / size.height;\n      cropperRect.top = 0;\n      cropperRect.left = (elementWidth - cropperRect.width) / 2;\n    }\n\n    self.cropperRect = cropperRect;\n\n    for (var style in cropperRect) {\n      if (cropperRect.hasOwnProperty(style)) {\n        dom.style[style] = sourceImage.style[style] = resizeImage.style[style] = cropperRect[style] + 'px';\n      }\n    }\n\n    if (!ieVersion || ieVersion > 9) {\n      resizeImage.src = sourceImage.src = src;\n    }\n\n    self.dom.style.display = '';\n    self.resetResizer();\n\n    self.updatePreview(src);\n  });\n};\n\nCropper.prototype.addPreview = function(preview) {\n  var previews = this.previews;\n  if (!previews) {\n    previews = this.previews = [];\n  }\n  previews.push(preview);\n};\n\nCropper.prototype.render = function(container) {\n  var resizer = new Resizer({ aspectRatio: this.aspectRatio });\n  var refs = {};\n\n  var dom = buildDom({\n    tag: 'div',\n    className: 'cropper',\n    content: [{\n      tag: 'div',\n      className: 'mask'\n    }]\n  }, refs);\n\n  var resizerDom = resizer.render(dom);\n\n  var img = buildDom({\n    tag: 'div',\n    className: 'wrapper',\n    content: [{\n      tag: 'img',\n      key: 'image',\n      src: blankImage\n    }]\n  }, refs);\n\n  var self = this;\n  self.refs = refs;\n\n  resizer.doOnStateChange = function() {\n    var left = parseInt(resizerDom.style.left, 10) || 0;\n    var top = parseInt(resizerDom.style.top, 10) || 0;\n\n    var image = refs.image;\n\n    image.style.left = -left + 'px';\n    image.style.top = -top + 'px';\n\n    self.updatePreview();\n  };\n\n  resizer.doOnDragEnd = function() {\n    var left = parseInt(resizerDom.style.left, 10) || 0;\n    var top = parseInt(resizerDom.style.top, 10) || 0;\n    var resizerWidth = resizerDom.offsetWidth;\n    var resizerHeight = resizerDom.offsetHeight;\n\n    var imageSize = self.imageSize;\n    var cropperRect = self.cropperRect;\n    if (cropperRect) {\n      var scale = cropperRect.width / imageSize.width;\n\n      self.croppedRect = {\n        width: Math.floor(resizerWidth / scale),\n        height: Math.floor(resizerHeight / scale),\n        left: Math.floor(left / scale),\n        top: Math.floor(top / scale)\n      };\n\n      self.onCroppedRectChange && self.onCroppedRectChange(self.croppedRect);\n    }\n  };\n  self.resizer = resizer;\n  self.dom = dom;\n\n  resizerDom.insertBefore(img, resizerDom.firstChild);\n\n  container.appendChild(dom);\n\n  self.dom.style.display = 'none';\n};\n\nCropper.prototype.updatePreview = function(src) {\n  var imageSize = this.imageSize;\n  var cropperRect = this.cropperRect;\n  if (!imageSize || !cropperRect) return;\n\n  var previews = this.previews || [];\n\n  var resizerDom = this.resizer.dom;\n  var resizerLeft = parseInt(resizerDom.style.left, 10) || 0;\n  var resizerTop = parseInt(resizerDom.style.top, 10) || 0;\n\n  var resizerWidth = resizerDom.offsetWidth;\n  var resizerHeight = resizerDom.offsetHeight;\n\n  for (var i = 0, j = previews.length; i < j; i++) {\n    var previewElement = previews[i];\n    var previewImage = previewElement.querySelector('img');\n    var previewWrapper = previewElement.querySelector('div');\n\n    if (!previewImage) continue;\n\n    if (src === blankImage) {\n      previewImage.style.width = previewImage.style.height = previewImage.style.left = previewImage.style.top = '';\n      previewImage.src = blankImage;\n    } else {\n      if (ieVersion < 10) {\n        if (src) {\n          previewImage.src = blankImage;\n\n          previewImage.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)';\n          previewImage.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src;\n          previewImage.style.width = cropperRect.width + 'px';\n          previewImage.style.height = cropperRect.height + 'px';\n        }\n      } else if (src) {\n        previewImage.src = src;\n      }\n\n      var elementWidth = previewElement.offsetWidth;\n      var elementHeight = previewElement.offsetHeight;\n\n      var scale = elementWidth / resizerWidth;\n\n      if (previewWrapper) {\n        var elementRatio = elementWidth / elementHeight;\n        var resizerRatio = resizerWidth / resizerHeight;\n\n        if (elementRatio < resizerRatio) {\n          previewWrapper.style.width = elementWidth + 'px';\n          previewWrapper.style.height = resizerHeight * elementWidth / resizerWidth + 'px';\n          previewWrapper.style.top = (elementHeight - previewWrapper.clientHeight) / 2 + 'px';\n          previewWrapper.style.left = '';\n        } else {\n          var visibleWidth = resizerWidth * elementHeight / resizerHeight;\n          scale = visibleWidth / resizerWidth;\n          previewWrapper.style.height = elementHeight + 'px';\n          previewWrapper.style.width = visibleWidth + 'px';\n          previewWrapper.style.left = (elementWidth - previewWrapper.clientWidth) / 2 + 'px';\n          previewWrapper.style.top = '';\n        }\n      }\n\n      previewImage.style.width = scale * cropperRect.width + 'px';\n      previewImage.style.height = scale * cropperRect.height + 'px';\n      previewImage.style.left = -resizerLeft * scale + 'px';\n      previewImage.style.top = -resizerTop * scale + 'px';\n    }\n  }\n};\n\nmodule.exports = Cropper;\n},{\"./Resizer\":1,\"./build-dom\":3}],5:[function(require,module,exports){\nvar bind = function(element, event, fn) {\n  if (element.attachEvent) {\n    element.attachEvent('on' + event, fn);\n  } else {\n    element.addEventListener(event, fn, false);\n  }\n};\n\nvar unbind = function(element, event, fn) {\n  if (element.detachEvent) {\n    element.detachEvent('on' + event, fn);\n  } else {\n    element.removeEventListener(event, fn);\n  }\n};\n\nvar isDragging = false;\n\nvar isIE8 = Number(document.documentMode) < 9;\n\nvar fixEvent = function(event) {\n  var scrollTop = Math.max(window.scrollY || 0, document.documentElement.scrollTop || 0);\n  var scrollLeft = Math.max(window.scrollX || 0, document.documentElement.scrollLeft || 0);\n\n  event.target = event.srcElement;\n  event.pageX = scrollLeft + event.clientX;\n  event.pageY = scrollTop + event.clientY;\n};\n\nmodule.exports = function(element, options) {\n  var moveFn = function(event) {\n    if (isIE8) {\n      fixEvent(event);\n    }\n    if (options.drag) {\n      options.drag(event);\n    }\n  };\n  var upFn = function(event) {\n    if (isIE8) {\n      fixEvent(event);\n    }\n    unbind(document, 'mousemove', moveFn);\n    unbind(document, 'mouseup', upFn);\n    document.onselectstart = null;\n    document.ondragstart = null;\n\n    isDragging = false;\n\n    if (options.end) {\n      options.end(event);\n    }\n  };\n  bind(element, 'mousedown', function(event) {\n    if (isIE8) {\n      fixEvent(event);\n    }\n    if (isDragging) return;\n    document.onselectstart = function() { return false; };\n    document.ondragstart = function() { return false; };\n\n    bind(document, 'mousemove', moveFn);\n    bind(document, 'mouseup', upFn);\n    isDragging = true;\n\n    if (options.start) {\n      options.start(event);\n    }\n  });\n};\n},{}],6:[function(require,module,exports){\nwindow.Cropper = require('./cropper');\n},{\"./cropper\":4}],7:[function(require,module,exports){\nif (typeof angular !== 'undefined') {\n  require('./angular');\n} else {\n  require('./export');\n}\n\n\n},{\"./angular\":2,\"./export\":6}]},{},[7]);\n"
  },
  {
    "path": "doc/README.MD",
    "content": "## 介绍\nImage Cropper可以为图片显示一个裁剪框，裁剪框允许用户调整大小和位置，常用来做用户自定义头像的裁剪功能。\n\n目前Image Cropper的实现是无依赖的，浏览器支持到IE8+。Image Cropper可以和Angular一块使用，也可以独立使用。\n\n## 安装\n\n如果你使用bower，可以在bower.json中添加这么一段配置：\n\n    \"image-cropper\": \"git@github.com:ElemeFE/image-cropper.git#~0.2.0\"\n\n如果你使用npm，可以直接这么安装：\n\n    npm install image-cropper.js --save\n\n## 引入文件\n\nImage Cropper的使用依赖两个文件：\n\n- dist/cropper.css\n- dist/cropper.js\n\n最简单的办法是直接在HTML中引用这两个文件，不过每个项目的情况不同，可以根据情况来决定如何引入这两个文件。 \n\n## 使用说明\n\nImage Cropper是因为一个裁剪头像并上传的需求诞生的。\n\n一个裁剪头像的流程大概是这样：\n\n- 用户选择一张本地图片。\n- 用户使用鼠标拖拽裁剪头像，并实时预览不同大小的裁剪效果。\n- 把用户的裁剪范围和图片提交到服务器端。\n\n从提交到服务器端的角度来看，我们关注的点只有两个：\n\n- 用户选择的本地图片\n- 用户的裁剪结果(Rect: 相对于图片的left、top、width、height)\n\n### 在Angular中使用\n\nImage Cropper在同一个页面上可以指定多个Instance，该属性定义一个字符串，作为Image Cropper的一个实例的ID。\n\nImage Cropper的ID是必需的，在下面的例子里面，我们使用avatar作为例子中的Image Cropper的实例的ID。\n\nImage Cropper为Angular提供了三个属性Directive，通过这三个Directive的配合，即可实现图像裁剪和预览的效果。\n\n- cropper：添加到要显示裁剪框的元素上，该directive的属性值是cropper的ID。\n- cropper-preview：添加到要显示预览的元素上。\n- cropper-source：添加到input type=file上。 \n\n注意：下面例子中的data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7是一个空的gif文件，用来在用户未选择图片的情况下显示该图片。\n\n#### cropper\ncropper需要添加到要显示裁剪框的元素上，directive的属性值为Image Cropper的实例的ID，例如：\n\n    <div class=\"image-container target\" cropper=\"avatar\">\n      <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n    </div>\n\n#### cropper-aspect-ratio\ncropper默认的款高比为1，如果需要使用其他宽高比或者不限制宽高比，则可以设置此属性。比如不限制宽高比，则可以这么设置：\n\n    <div class=\"image-container target\" cropper=\"avatar\" cropper-aspect-ratio=\"auto\">\n      <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n    </div>\n\ncropper-aspect-ratio如果不是数字，则为不限制宽高比。\n\n##### cropper-context\n用户的裁剪结果需要导出到Controller中的一个变量里面，我们可以使用cropper-context来指定这个变量，比如Controller中的变量名是cropContext，则这么定义：\n\n    <div class=\"image-container source\" cropper=\"avatar\" cropper-context=\"cropContext\">\n      <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n    </div>\n\n#### cropper-source\ncropper-source需要添加到一个input[type=file]上，directive的属性值为Image Cropper的实例的ID，例如：\n\n    <input type=\"file\" name=\"avatar\" cropper-source=\"avatar\" />\n\n在input[type=file]的值发生变化的时候，cropper和cropper-preview会去使用用户选择的图片。\n\n##### cropper-file-types\nImage Cropper默认支持的类型为gif、png、jpg，如果需要定义只支持者某几种格式，比如只支持jpg、png，可以这么定义：\n\n    <input type=\"file\" name=\"avatar\" cropper-source=\"avatar\" cropper-file-types=\"jpg,jpeg,png\" />\n\n#### cropper-preview\n在用户裁剪图像的过程中，需要动态的预览不同大小的头像的效果，在显示预览图像的元素上添加这个directive。这个directive的属性值为Image Cropper的实例的ID，例如：\n\n    <div class=\"image-container small\" cropper-preview=\"avatar\">\n        <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n    </div>\n\n注意，如果设置了aspectRatio为非数值，即不限制裁剪图片的宽高比，cropper-preview的HTML应该是这种结构的：\n\n    <div class=\"image-container small\" id=\"preview-small\">\n      <div class=\"image-wrapper\">\n        <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\" class=\"noavatar\" />\n      </div>\n    </div>\n\n### 独立使用\n\nImage Cropper是可以不和Angular集成，独立使用的。如果你的页面中没有Angular，则在window上可以直接使用Cropper。\n\n#### 创建Cropper实例\n\n独立使用Cropper，需要指定这么几个属性：\n\n- element：显示裁剪框的Element。\n- aspectRatio：裁剪框的宽高比，默认值为1。如果设置非数字类型，则为不限制款高比。\n- previews：数组类型，可以指定多个显示图片预览的元素。 \n- onCroppedRectChange：在裁剪结果变更之后会触发这个回调。\n\n一个例子如下：\n\n    var cropper = new Cropper({\n        element: document.getElementById('cropper-target'),\n        previews: [\n          document.getElementById('preview-large'),\n          document.getElementById('preview-medium'),\n          document.getElementById('preview-small')\n        ],\n        onCroppedRectChange: function(rect) {\n          console.log(rect);\n        }\n    });\n\n#### 更改Cropper使用的图片\n\nImage Cropper为Angular提供了一个cropper-source来在input[type=file]变更之后更改Cropper的图片，在独立使用的时候，需要手动去做这件事。\n\n变更Cropper的图片使用setImage方法来完成，假设input[type=file]的id为cropper-input，示例代码如下：\n\n    var input = document.getElementById('cropper-input');\n    input.onchange = function() {\n      if (typeof FileReader !== 'undefined') {\n        var reader = new FileReader();\n        reader.onload = function (event) {\n          cropper.setImage(event.target.result);\n        };\n        if (input.files && input.files[0]) {\n          reader.readAsDataURL(input.files[0]);\n        }\n      } else { // IE10-\n        input.select();\n        input.blur();\n    \n        var src = document.selection.createRange().text;\n        cropper.setImage(src);\n      }\n    };\n\n## License\nMIT\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"image-cropper.js\",\n  \"version\": \"0.3.1\",\n  \"description\": \"A image cropper for cropping user avatar, no dependencies\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/ElemeFE/image-cropper.git\"\n  },\n  \"keywords\": [\n    \"crop\",\n    \"cropper\",\n    \"image\"\n  ],\n  \"main\": \"src/index.js\",\n  \"author\": \"long.zhang\",\n  \"license\": \"MIT\",\n  \"devDependencies\": {\n    \"browserify\": \"^9.0.8\",\n    \"watchify\": \"^3.3.0\"\n  },\n  \"scripts\": {\n    \"build\": \"browserify src/index.js -o dist/cropper.js\",\n    \"watch\": \"watchify src/index.js -o dist/cropper.js -dv\",\n    \"build-plain\": \"browserify src/export.js -o dist/cropper-plain.js\"\n  }\n}\n"
  },
  {
    "path": "src/angular.js",
    "content": "var Cropper = require('./cropper');\n\nvar cropperInstances = {};\n\nCropper.getInstance = function(id) {\n  return cropperInstances[id];\n};\n\nangular.module('cropper', [])\n.factory('Cropper', function() {\n  return Cropper;\n})\n.directive('cropper', function() {\n  return {\n    restrict: 'A',\n    scope: {\n      cropperContext: '=',\n      cropperAspectRatio: '@'\n    },\n    link: function(scope, element, attrs) {\n      var id = attrs.cropper;\n      if (!id) throw new Error('cropper id is required');\n      var cropperAspectRatio = scope.cropperAspectRatio;\n\n      if (cropperAspectRatio) {\n        if (/^\\d*(\\.)?\\d+$/g.test(cropperAspectRatio)) {\n          cropperAspectRatio = parseFloat(cropperAspectRatio);\n        }\n      } else {\n        cropperAspectRatio = 1;\n      }\n\n      var cropper = Cropper({ element: element[0], aspectRatio: cropperAspectRatio });\n\n      cropperInstances[id] = cropper;\n\n      var cropperContext = scope.cropperContext;\n\n      cropper.onCroppedRectChange = function(rect) {\n        if (cropperContext) {\n          cropperContext.left = rect.left;\n          cropperContext.top = rect.top;\n          cropperContext.width = rect.width;\n          cropperContext.height = rect.height;\n        }\n        try { scope.$apply(); } catch(e) {}\n      };\n\n      scope.$on('$destroy', function() {\n        cropperInstances[id] = null;\n        delete cropperInstances[id];\n      });\n    }\n  };\n}).directive('cropperPreview', function(){\n  return {\n    restrict: 'A',\n    link: function(scope, element, attrs) {\n      var id = attrs.cropperPreview;\n      if (!id) throw new Error('cropper id is required');\n\n      var cropper = cropperInstances[id];\n\n      if (cropper) {\n        cropper.addPreview(element[0]);\n      }\n    }\n  }\n}).directive('cropperSource', function() {\n  return {\n    restrict: 'A',\n    link: function ($scope, $el, attrs) {\n      var id = attrs.cropperSource;\n      if (!id) throw new Error('cropper id is required');\n\n      var fileValidateRegex = /\\.(jpg|png|gif|jpeg)$/i;\n      var fileTypes = attrs.cropperFileTypes;\n\n      if (fileTypes) {\n        var types = fileTypes.split(',');\n        if (types.length > 0) {\n          fileValidateRegex = new RegExp('\\.(' + types.join('|') + ')$', 'i');\n        }\n      }\n\n      $el.on('change', function () {\n        var input = this;\n        var cropper = cropperInstances[id];\n\n        var fileName = input.value;\n        if (!fileValidateRegex.test(fileName)) {\n          cropper.setImage();\n          return;\n        }\n\n        if (typeof FileReader !== 'undefined') {\n          var reader = new FileReader();\n          reader.onload = function (event) {\n            cropper.setImage(event.target.result);\n          };\n          if (input.files && input.files[0]) {\n            reader.readAsDataURL(input.files[0]);\n          }\n        } else {\n          input.select();\n          input.blur();\n\n          var src = document.selection.createRange().text;\n          cropper.setImage(src);\n        }\n      });\n    }\n  };\n});"
  },
  {
    "path": "src/build-dom.js",
    "content": "var buildDOM = function(config, refs) {\n  if (!config) return null;\n  var dom, childElement;\n  if (config.tag) {\n    dom = document.createElement(config.tag);\n    for (var prop in config) {\n      if (config.hasOwnProperty(prop)) {\n        if (prop === 'content' || prop === 'tag') continue;\n        if (prop === 'key' && refs) {\n          var key = config[prop];\n          if (key) {\n            refs[key] = dom;\n          }\n        }\n        dom[prop] = config[prop];\n      }\n    }\n    var content = config.content;\n    if (content instanceof Array) {\n      for (var i = 0, j = content.length; i < j; i++) {\n        var child = content[i];\n        childElement = buildDOM(child, refs);\n        dom.appendChild(childElement);\n      }\n    } else if (typeof content === 'string') {\n      childElement = document.createTextNode(content);\n      dom.appendChild(childElement);\n    }\n  }\n  return dom;\n};\n\nmodule.exports = buildDOM;"
  },
  {
    "path": "src/cropper.js",
    "content": "var Resizer = require('./Resizer');\nvar buildDom = require('./build-dom');\n\nvar blankImage = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';\n\nvar preLoadElement;\n\nvar ieVersion = Number(document.documentMode);\n\nvar getImageSize = function(src, callback) {\n  if (ieVersion < 10) {\n    if (!preLoadElement) {\n      preLoadElement = document.createElement('div');\n      preLoadElement.style.position = 'absolute';\n      preLoadElement.style.width = '1px';\n      preLoadElement.style.height = '1px';\n      preLoadElement.style.left = '-9999px';\n      preLoadElement.style.top = '-9999px';\n      preLoadElement.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=image)';\n      document.body.insertBefore(preLoadElement, document.body.firstChild);\n    }\n\n    preLoadElement.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src;\n\n    var size = {\n      width: preLoadElement.offsetWidth,\n      height: preLoadElement.offsetHeight\n    };\n\n    if (typeof callback === 'function') {\n      callback(size);\n    }\n  } else {\n    var image = new Image();\n    image.onload = function() {\n      var size = {\n        width: image.width,\n        height: image.height\n      };\n      if (typeof callback === 'function') {\n        callback(size);\n      }\n    };\n    image.src = src;\n  }\n};\n\nvar Cropper = function(options) {\n  var cropper = this;\n  if (!(this instanceof Cropper)) {\n    cropper = new Cropper();\n  }\n  cropper.aspectRatio = 1;\n  for (var prop in options) {\n    if (options.hasOwnProperty(prop)) cropper[prop] = options[prop];\n  }\n\n  if (cropper.element) {\n    cropper.render(cropper.element);\n  }\n\n  return cropper;\n};\n\nCropper.prototype.resetResizer = function() {\n  var resizer = this.resizer;\n  var cropperRect = this.cropperRect;\n  var aspectRatio = this.aspectRatio;\n\n  if (typeof aspectRatio !== 'number') {\n    aspectRatio = 1;\n  }\n\n  var width = 100;\n  var height = 100 / aspectRatio;\n\n  var resizerDom = resizer.dom;\n  resizerDom.style.width = width + 'px';\n  resizerDom.style.height = height + 'px';\n\n  if (cropperRect) {\n    resizerDom.style.left = (cropperRect.width - width) / 2 + 'px';\n    resizerDom.style.top = (cropperRect.height - height) / 2 + 'px';\n  } else {\n    resizerDom.style.left = resizerDom.style.top = '';\n  }\n\n  resizer.doOnStateChange();\n  resizer.doOnDragEnd();\n};\n\nCropper.prototype.setImage = function(src) {\n  var element = this.element;\n  var sourceImage = element.querySelector('img');\n  var resizeImage = this.refs.image;\n\n  var self = this;\n\n  if (src === undefined || src === null) {\n    resizeImage.src = sourceImage.src = blankImage;\n    resizeImage.style.width = resizeImage.style.height = resizeImage.style.left = resizeImage.style.top = '';\n    sourceImage.style.width = sourceImage.style.height = sourceImage.style.left = sourceImage.style.top = '';\n\n    self.updatePreview(blankImage);\n\n    self.dom.style.display = 'none';\n    self.resetResizer();\n\n    self.dom.style.left = self.dom.style.top = '';\n    self.dom.style.width = element.offsetWidth + 'px';\n    self.dom.style.height = element.offsetHeight + 'px';\n\n    self.croppedRect = {\n      width: 0,\n      height: 0,\n      left: 0,\n      top: 0\n    };\n\n    self.onCroppedRectChange && self.onCroppedRectChange(self.croppedRect);\n\n    return;\n  }\n\n  getImageSize(src, function(size) {\n    if (ieVersion < 10) {\n      resizeImage.src = sourceImage.src = blankImage;\n      resizeImage.style.filter = sourceImage.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)';\n\n      sourceImage.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src;\n      resizeImage.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src;\n    }\n\n    self.imageSize = size;\n\n    var elementWidth = element.offsetWidth;\n    var elementHeight = element.offsetHeight;\n\n    var dom = self.dom;\n\n    var cropperRect = {};\n\n    if (size.width / size.height > elementWidth / elementHeight) {\n      cropperRect.width = elementWidth;\n      cropperRect.height = elementWidth * size.height / size.width;\n      cropperRect.top = (elementHeight - cropperRect.height) / 2;\n      cropperRect.left = 0;\n    } else {\n      cropperRect.height = elementHeight;\n      cropperRect.width = elementHeight * size.width / size.height;\n      cropperRect.top = 0;\n      cropperRect.left = (elementWidth - cropperRect.width) / 2;\n    }\n\n    self.cropperRect = cropperRect;\n\n    for (var style in cropperRect) {\n      if (cropperRect.hasOwnProperty(style)) {\n        dom.style[style] = sourceImage.style[style] = resizeImage.style[style] = cropperRect[style] + 'px';\n      }\n    }\n\n    if (!ieVersion || ieVersion > 9) {\n      resizeImage.src = sourceImage.src = src;\n    }\n\n    self.dom.style.display = '';\n    self.resetResizer();\n\n    self.updatePreview(src);\n  });\n};\n\nCropper.prototype.addPreview = function(preview) {\n  var previews = this.previews;\n  if (!previews) {\n    previews = this.previews = [];\n  }\n  previews.push(preview);\n};\n\nCropper.prototype.render = function(container) {\n  var resizer = new Resizer({ aspectRatio: this.aspectRatio });\n  var refs = {};\n\n  var dom = buildDom({\n    tag: 'div',\n    className: 'cropper',\n    content: [{\n      tag: 'div',\n      className: 'mask'\n    }]\n  }, refs);\n\n  var resizerDom = resizer.render(dom);\n\n  var img = buildDom({\n    tag: 'div',\n    className: 'wrapper',\n    content: [{\n      tag: 'img',\n      key: 'image',\n      src: blankImage\n    }]\n  }, refs);\n\n  var self = this;\n  self.refs = refs;\n\n  resizer.doOnStateChange = function() {\n    var left = parseInt(resizerDom.style.left, 10) || 0;\n    var top = parseInt(resizerDom.style.top, 10) || 0;\n\n    var image = refs.image;\n\n    image.style.left = -left + 'px';\n    image.style.top = -top + 'px';\n\n    self.updatePreview();\n  };\n\n  resizer.doOnDragEnd = function() {\n    var left = parseInt(resizerDom.style.left, 10) || 0;\n    var top = parseInt(resizerDom.style.top, 10) || 0;\n    var resizerWidth = resizerDom.offsetWidth;\n    var resizerHeight = resizerDom.offsetHeight;\n\n    var imageSize = self.imageSize;\n    var cropperRect = self.cropperRect;\n    if (cropperRect) {\n      var scale = cropperRect.width / imageSize.width;\n\n      self.croppedRect = {\n        width: Math.floor(resizerWidth / scale),\n        height: Math.floor(resizerHeight / scale),\n        left: Math.floor(left / scale),\n        top: Math.floor(top / scale)\n      };\n\n      self.onCroppedRectChange && self.onCroppedRectChange(self.croppedRect);\n    }\n  };\n  self.resizer = resizer;\n  self.dom = dom;\n\n  resizerDom.insertBefore(img, resizerDom.firstChild);\n\n  container.appendChild(dom);\n\n  self.dom.style.display = 'none';\n};\n\nCropper.prototype.updatePreview = function(src) {\n  var imageSize = this.imageSize;\n  var cropperRect = this.cropperRect;\n  if (!imageSize || !cropperRect) return;\n\n  var previews = this.previews || [];\n\n  var resizerDom = this.resizer.dom;\n  var resizerLeft = parseInt(resizerDom.style.left, 10) || 0;\n  var resizerTop = parseInt(resizerDom.style.top, 10) || 0;\n\n  var resizerWidth = resizerDom.offsetWidth;\n  var resizerHeight = resizerDom.offsetHeight;\n\n  for (var i = 0, j = previews.length; i < j; i++) {\n    var previewElement = previews[i];\n    var previewImage = previewElement.querySelector('img');\n    var previewWrapper = previewElement.querySelector('div');\n\n    if (!previewImage) continue;\n\n    if (src === blankImage) {\n      previewImage.style.width = previewImage.style.height = previewImage.style.left = previewImage.style.top = '';\n      previewImage.src = blankImage;\n    } else {\n      if (ieVersion < 10) {\n        if (src) {\n          previewImage.src = blankImage;\n\n          previewImage.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)';\n          previewImage.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = src;\n          previewImage.style.width = cropperRect.width + 'px';\n          previewImage.style.height = cropperRect.height + 'px';\n        }\n      } else if (src) {\n        previewImage.src = src;\n      }\n\n      var elementWidth = previewElement.offsetWidth;\n      var elementHeight = previewElement.offsetHeight;\n\n      var scale = elementWidth / resizerWidth;\n\n      if (previewWrapper) {\n        var elementRatio = elementWidth / elementHeight;\n        var resizerRatio = resizerWidth / resizerHeight;\n\n        if (elementRatio < resizerRatio) {\n          previewWrapper.style.width = elementWidth + 'px';\n          previewWrapper.style.height = resizerHeight * elementWidth / resizerWidth + 'px';\n          previewWrapper.style.top = (elementHeight - previewWrapper.clientHeight) / 2 + 'px';\n          previewWrapper.style.left = '';\n        } else {\n          var visibleWidth = resizerWidth * elementHeight / resizerHeight;\n          scale = visibleWidth / resizerWidth;\n          previewWrapper.style.height = elementHeight + 'px';\n          previewWrapper.style.width = visibleWidth + 'px';\n          previewWrapper.style.left = (elementWidth - previewWrapper.clientWidth) / 2 + 'px';\n          previewWrapper.style.top = '';\n        }\n      }\n\n      previewImage.style.width = scale * cropperRect.width + 'px';\n      previewImage.style.height = scale * cropperRect.height + 'px';\n      previewImage.style.left = -resizerLeft * scale + 'px';\n      previewImage.style.top = -resizerTop * scale + 'px';\n    }\n  }\n};\n\nmodule.exports = Cropper;"
  },
  {
    "path": "src/draggable.js",
    "content": "var bind = function(element, event, fn) {\n  if (element.attachEvent) {\n    element.attachEvent('on' + event, fn);\n  } else {\n    element.addEventListener(event, fn, false);\n  }\n};\n\nvar unbind = function(element, event, fn) {\n  if (element.detachEvent) {\n    element.detachEvent('on' + event, fn);\n  } else {\n    element.removeEventListener(event, fn);\n  }\n};\n\nvar isDragging = false;\n\nvar isIE8 = Number(document.documentMode) < 9;\n\nvar fixEvent = function(event) {\n  var scrollTop = Math.max(window.scrollY || 0, document.documentElement.scrollTop || 0);\n  var scrollLeft = Math.max(window.scrollX || 0, document.documentElement.scrollLeft || 0);\n\n  event.target = event.srcElement;\n  event.pageX = scrollLeft + event.clientX;\n  event.pageY = scrollTop + event.clientY;\n};\n\nmodule.exports = function(element, options) {\n  var moveFn = function(event) {\n    if (isIE8) {\n      fixEvent(event);\n    }\n    if (options.drag) {\n      options.drag(event);\n    }\n  };\n  var upFn = function(event) {\n    if (isIE8) {\n      fixEvent(event);\n    }\n    unbind(document, 'mousemove', moveFn);\n    unbind(document, 'mouseup', upFn);\n    document.onselectstart = null;\n    document.ondragstart = null;\n\n    isDragging = false;\n\n    if (options.end) {\n      options.end(event);\n    }\n  };\n  bind(element, 'mousedown', function(event) {\n    if (isIE8) {\n      fixEvent(event);\n    }\n    if (isDragging) return;\n    document.onselectstart = function() { return false; };\n    document.ondragstart = function() { return false; };\n\n    bind(document, 'mousemove', moveFn);\n    bind(document, 'mouseup', upFn);\n    isDragging = true;\n\n    if (options.start) {\n      options.start(event);\n    }\n  });\n};"
  },
  {
    "path": "src/export.js",
    "content": "window.Cropper = require('./cropper');"
  },
  {
    "path": "src/index.js",
    "content": "if (typeof angular !== 'undefined') {\n  require('./angular');\n} else {\n  require('./export');\n}\n\n"
  },
  {
    "path": "src/resizer.js",
    "content": "var buildDom = require('./build-dom');\nvar draggable = require('./draggable');\n\nvar configMap = {\n  'n':  { top: true, height: -1 },\n  'w':  { left: true, width: -1 },\n  'e':  { width: 1 },\n  's':  { height: 1 },\n  'nw': { left: true, top: true, width: -1, height: -1 },\n  'ne': { top: true, width: 1, height: -1 },\n  'sw': { left: true, width: -1, height: 1 },\n  'se': { width: 1, height: 1 }\n};\n\nvar getPosition = function (element) {\n  var selfRect = element.getBoundingClientRect();\n  var parentRect = element.offsetParent.getBoundingClientRect();\n\n  return {\n    left: selfRect.left - parentRect.left,\n    top: selfRect.top - parentRect.top\n  };\n};\n\nvar Resizer = function(options) {\n  for (var prop in options) {\n    if (options.hasOwnProperty(prop)) this[prop] = options[prop];\n  }\n};\n\nResizer.prototype.doOnStateChange = function(state) {\n};\n\nResizer.prototype.makeDraggable = function(dom) {\n  var self = this;\n  var dragState = {};\n  var containment;\n\n  draggable(dom, {\n    start: function (event) {\n      var parentNode = dom.parentNode;\n      containment = {\n        left: 0,\n        top: 0,\n        width: parentNode.clientWidth,\n        height: parentNode.clientHeight,\n        right: parentNode.clientWidth,\n        bottom: parentNode.clientHeight\n      };\n\n      dragState.startLeft = event.clientX;\n      dragState.startTop = event.clientY;\n\n      var position = getPosition(dom);\n\n      dragState.resizerStartLeft = position.left;\n      dragState.resizerStartTop = position.top;\n      dragState.resizerStartWidth = dom.offsetWidth;\n      dragState.resizerStartHeight = dom.offsetHeight;\n    },\n    drag: function (event) {\n      var offsetLeft = event.clientX - dragState.startLeft;\n      var offsetTop = event.clientY - dragState.startTop;\n\n      var left = dragState.resizerStartLeft + offsetLeft;\n      var top = dragState.resizerStartTop + offsetTop;\n\n      if (left < containment.left) {\n        left = containment.left;\n      }\n\n      if (top < containment.top) {\n        top = containment.top;\n      }\n\n      if (left + dragState.resizerStartWidth > containment.right) {\n        left = containment.right - dragState.resizerStartWidth;\n      }\n\n      if (top + dragState.resizerStartHeight > containment.bottom) {\n        top = containment.bottom - dragState.resizerStartHeight;\n      }\n\n      dom.style.left = left + 'px';\n      dom.style.top = top + 'px';\n\n      self.doOnStateChange();\n    },\n    end: function () {\n      dragState = {};\n      if (self.doOnDragEnd) {\n        self.doOnDragEnd();\n      }\n    }\n  });\n};\n\nResizer.prototype.bindResizeEvent = function(dom) {\n  var self = this;\n  var resizeState = {};\n  var aspectRatio = self.aspectRatio;\n\n  if (typeof aspectRatio !== 'number') {\n    aspectRatio = undefined;\n  }\n\n  var makeResizable = function (bar) {\n    var type = bar.className.split(' ')[0];\n    var transformMap = configMap[type.substr(4)];\n\n    var containment;\n\n    draggable(bar, {\n      start: function (event) {\n        var parentNode = dom.parentNode;\n        containment = {\n          left: 0,\n          top: 0,\n          width: parentNode.clientWidth,\n          height: parentNode.clientHeight,\n          right: parentNode.clientWidth,\n          bottom: parentNode.clientHeight\n        };\n\n        resizeState.startWidth = dom.clientWidth;\n        resizeState.startHeight = dom.clientHeight;\n        resizeState.startLeft = event.clientX;\n        resizeState.startTop = event.clientY;\n\n        var position = getPosition(dom);\n        resizeState.resizerStartLeft = position.left;\n        resizeState.resizerStartTop = position.top;\n      },\n      drag: function (event) {\n        var widthRatio = transformMap.width;\n        var heightRatio = transformMap.height;\n\n        var offsetLeft = event.clientX - resizeState.startLeft;\n        var offsetTop = event.clientY - resizeState.startTop;\n\n        var width, height, minWidth = 50, maxWidth = 10000, minHeight = 50, maxHeight = 10000;\n\n        if (widthRatio !== undefined) {\n          width = resizeState.startWidth + widthRatio * offsetLeft;\n          if (width < minWidth) {\n            width = minWidth;\n          }\n\n          if (maxWidth && width > maxWidth) {\n            width = maxWidth;\n          }\n        }\n\n        if (heightRatio !== undefined) {\n          height = resizeState.startHeight + heightRatio * offsetTop;\n          if (height < minHeight) {\n            height = minHeight;\n          }\n\n          if (maxHeight && height > maxHeight) {\n            height = maxHeight;\n          }\n        }\n\n        if (aspectRatio !== undefined) {\n          if (type === 'ord-n' || type === 'ord-s') {\n            width = height * aspectRatio;\n          } else if (type === 'ord-w' || type === 'ord-e') {\n            height = width / aspectRatio;\n          } else {\n            if (width / height < aspectRatio) {\n              height = width / aspectRatio;\n            } else {\n              width = height * aspectRatio;\n            }\n          }\n        }\n\n        var position = {\n          left: resizeState.resizerStartLeft,\n          top: resizeState.resizerStartTop\n        };\n\n        if (transformMap.left !== undefined) {\n          position.left = resizeState.resizerStartLeft + (width - resizeState.startWidth) * widthRatio;\n        }\n\n        if (transformMap.top !== undefined) {\n          position.top = resizeState.resizerStartTop + (height - resizeState.startHeight) * heightRatio;\n        }\n\n        //=== containment start\n\n        if (width + position.left > containment.right) {\n          width = containment.right - position.left;\n        }\n\n        if (position.left < containment.left) {\n          width -= containment.left - position.left;\n          position.left = containment.left;\n        }\n\n        if (height + position.top > containment.bottom) {\n          height = containment.bottom - position.top;\n        }\n\n        if (position.top < containment.top) {\n          height -= containment.top - position.top;\n          position.top = containment.top;\n        }\n\n        //=== containment end\n\n        if (aspectRatio !== undefined) {\n          if (width / height < aspectRatio) {\n            height = width / aspectRatio;\n          } else {\n            width = height * aspectRatio;\n          }\n        }\n\n        if (transformMap.left !== undefined) {\n          position.left = resizeState.resizerStartLeft + (width - resizeState.startWidth) * widthRatio;\n        }\n\n        if (transformMap.top !== undefined) {\n          position.top = resizeState.resizerStartTop + (height - resizeState.startHeight) * heightRatio;\n        }\n\n        dom.style.width = width + 'px';\n        dom.style.height = height + 'px';\n\n        if (position.left !== undefined) {\n          dom.style.left = position.left + 'px';\n        }\n\n        if (position.top !== undefined) {\n          dom.style.top = position.top + 'px';\n        }\n\n        self.doOnStateChange();\n      },\n      end: function () {\n        if (self.doOnDragEnd) {\n          self.doOnDragEnd();\n        }\n      }\n    });\n  };\n\n  var bars = dom.querySelectorAll('.resize-bar');\n  var handles = dom.querySelectorAll('.resize-handle');\n\n  var i, j;\n\n  for (i = 0, j = bars.length; i < j; i++) {\n    makeResizable(bars[i]);\n  }\n\n  for (i = 0, j = handles.length; i < j; i++) {\n    makeResizable(handles[i]);\n  }\n};\n\nResizer.prototype.render = function(container) {\n  var self = this;\n\n  var dom = buildDom({\n    tag: 'div',\n    className: 'resizer',\n    content: [\n      { tag: 'div', className: 'ord-n resize-bar' },\n      { tag: 'div', className: 'ord-s resize-bar' },\n      { tag: 'div', className: 'ord-w resize-bar' },\n      { tag: 'div', className: 'ord-e resize-bar' },\n      { tag: 'div', className: 'ord-nw resize-handle' },\n      { tag: 'div', className: 'ord-n resize-handle' },\n      { tag: 'div', className: 'ord-ne resize-handle' },\n      { tag: 'div', className: 'ord-w resize-handle' },\n      { tag: 'div', className: 'ord-e resize-handle' },\n      { tag: 'div', className: 'ord-sw resize-handle' },\n      { tag: 'div', className: 'ord-s resize-handle' },\n      { tag: 'div', className: 'ord-se resize-handle' }\n    ]\n  });\n\n  self.dom = dom;\n\n  self.bindResizeEvent(dom);\n  self.makeDraggable(dom);\n\n  if (container) {\n    container.appendChild(dom);\n  }\n\n  return dom;\n};\n\nmodule.exports = Resizer;"
  }
]