Jelajahi Sumber

Update assets

Added jquery-file-upload
jmontoya 9 tahun lalu
induk
melakukan
bad4197bc5
85 mengubah file dengan 8563 tambahan dan 9689 penghapusan
  1. 14 0
      app/Resources/public/assets/blueimp-canvas-to-blob/.bower.json
  2. 2 0
      app/Resources/public/assets/blueimp-canvas-to-blob/.gitignore
  3. 3 0
      app/Resources/public/assets/blueimp-canvas-to-blob/.travis.yml
  4. 84 0
      app/Resources/public/assets/blueimp-canvas-to-blob/README.md
  5. 111 0
      app/Resources/public/assets/blueimp-canvas-to-blob/js/canvas-to-blob.js
  6. 2 0
      app/Resources/public/assets/blueimp-canvas-to-blob/js/canvas-to-blob.min.js
  7. 1 0
      app/Resources/public/assets/blueimp-canvas-to-blob/js/canvas-to-blob.min.js.map
  8. 36 0
      app/Resources/public/assets/blueimp-canvas-to-blob/package.json
  9. 40 0
      app/Resources/public/assets/blueimp-canvas-to-blob/test/index.html
  10. 92 0
      app/Resources/public/assets/blueimp-canvas-to-blob/test/test.js
  11. 14 0
      app/Resources/public/assets/blueimp-load-image/.bower.json
  12. 2 0
      app/Resources/public/assets/blueimp-load-image/.gitignore
  13. 3 0
      app/Resources/public/assets/blueimp-load-image/.travis.yml
  14. 315 0
      app/Resources/public/assets/blueimp-load-image/README.md
  15. 74 0
      app/Resources/public/assets/blueimp-load-image/css/demo.css
  16. 69 0
      app/Resources/public/assets/blueimp-load-image/index.html
  17. 149 0
      app/Resources/public/assets/blueimp-load-image/js/demo.js
  18. 384 0
      app/Resources/public/assets/blueimp-load-image/js/load-image-exif-map.js
  19. 300 0
      app/Resources/public/assets/blueimp-load-image/js/load-image-exif.js
  20. 143 0
      app/Resources/public/assets/blueimp-load-image/js/load-image-meta.js
  21. 171 0
      app/Resources/public/assets/blueimp-load-image/js/load-image-orientation.js
  22. 0 0
      app/Resources/public/assets/blueimp-load-image/js/load-image.all.min.js
  23. 0 0
      app/Resources/public/assets/blueimp-load-image/js/load-image.all.min.js.map
  24. 348 0
      app/Resources/public/assets/blueimp-load-image/js/load-image.js
  25. 45 0
      app/Resources/public/assets/blueimp-load-image/package.json
  26. 44 0
      app/Resources/public/assets/blueimp-load-image/test/index.html
  27. 510 0
      app/Resources/public/assets/blueimp-load-image/test/test.js
  28. 14 0
      app/Resources/public/assets/blueimp-tmpl/.bower.json
  29. 2 0
      app/Resources/public/assets/blueimp-tmpl/.gitignore
  30. 3 0
      app/Resources/public/assets/blueimp-tmpl/.travis.yml
  31. 402 0
      app/Resources/public/assets/blueimp-tmpl/README.md
  32. 75 0
      app/Resources/public/assets/blueimp-tmpl/css/demo.css
  33. 81 0
      app/Resources/public/assets/blueimp-tmpl/index.html
  34. 83 0
      app/Resources/public/assets/blueimp-tmpl/js/compile.js
  35. 68 0
      app/Resources/public/assets/blueimp-tmpl/js/demo.js
  36. 48 0
      app/Resources/public/assets/blueimp-tmpl/js/runtime.js
  37. 86 0
      app/Resources/public/assets/blueimp-tmpl/js/tmpl.js
  38. 2 0
      app/Resources/public/assets/blueimp-tmpl/js/tmpl.min.js
  39. 1 0
      app/Resources/public/assets/blueimp-tmpl/js/tmpl.min.js.map
  40. 38 0
      app/Resources/public/assets/blueimp-tmpl/package.json
  41. 39 0
      app/Resources/public/assets/blueimp-tmpl/test/index.html
  42. 305 0
      app/Resources/public/assets/blueimp-tmpl/test/test.js
  43. 73 0
      app/Resources/public/assets/jquery-file-upload/.bower.json
  44. 64 0
      app/Resources/public/assets/jquery-file-upload/bower.json
  45. 22 0
      app/Resources/public/assets/jquery-file-upload/css/jquery.fileupload-noscript.css
  46. 17 0
      app/Resources/public/assets/jquery-file-upload/css/jquery.fileupload-ui-noscript.css
  47. 57 0
      app/Resources/public/assets/jquery-file-upload/css/jquery.fileupload-ui.css
  48. 37 0
      app/Resources/public/assets/jquery-file-upload/css/jquery.fileupload.css
  49. TEMPAT SAMPAH
      app/Resources/public/assets/jquery-file-upload/img/loading.gif
  50. 0 0
      app/Resources/public/assets/jquery-file-upload/img/progressbar.gif
  51. 120 0
      app/Resources/public/assets/jquery-file-upload/js/cors/jquery.postmessage-transport.js
  52. 89 0
      app/Resources/public/assets/jquery-file-upload/js/cors/jquery.xdr-transport.js
  53. 425 0
      app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-angular.js
  54. 112 0
      app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-audio.js
  55. 324 0
      app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-image.js
  56. 155 0
      app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-jquery-ui.js
  57. 175 0
      app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-process.js
  58. 710 0
      app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-ui.js
  59. 122 0
      app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-validate.js
  60. 112 0
      app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-video.js
  61. 1477 0
      app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload.js
  62. 217 0
      app/Resources/public/assets/jquery-file-upload/js/jquery.iframe-transport.js
  63. 5 5
      app/Resources/public/assets/jquery/.bower.json
  64. 0 3
      app/Resources/public/assets/jquery/AUTHORS.txt
  65. 3 63
      app/Resources/public/assets/jquery/README.md
  66. 22 22
      app/Resources/public/assets/jquery/dist/jquery.js
  67. 0 1
      app/Resources/public/assets/jquery/dist/jquery.min.js
  68. 0 0
      app/Resources/public/assets/jquery/dist/jquery.min.map
  69. 0 7877
      app/Resources/public/assets/jquery/dist/jquery.slim.js
  70. 0 1
      app/Resources/public/assets/jquery/dist/jquery.slim.min.js
  71. 0 0
      app/Resources/public/assets/jquery/dist/jquery.slim.min.map
  72. 0 14
      app/Resources/public/assets/jquery/src/core/DOMEval.js
  73. 5 8
      app/Resources/public/assets/jquery/src/css/curCSS.js
  74. 1 1
      app/Resources/public/assets/jquery/src/css/var/getStyles.js
  75. 0 19
      app/Resources/public/assets/jquery/src/deferred/exceptionHook.js
  76. 1 1
      app/Resources/public/assets/jquery/src/event.js
  77. 1 1
      app/Resources/public/assets/jquery/src/event/trigger.js
  78. 6 6
      app/Resources/public/assets/jquery/src/manipulation.js
  79. 5 2
      app/Resources/public/assets/jquery/src/offset.js
  80. 1 1
      app/Resources/public/assets/jquery/src/selector-sizzle.js
  81. 0 73
      main/inc/lib/javascript/jquery-upload/README.txt
  82. 0 82
      main/inc/lib/javascript/jquery-upload/jquery.fileupload-ui.css
  83. 0 533
      main/inc/lib/javascript/jquery-upload/jquery.fileupload-ui.js
  84. 0 975
      main/inc/lib/javascript/jquery-upload/jquery.fileupload.js
  85. 2 1
      src/Chamilo/CoreBundle/Resources/config/bower.json

+ 14 - 0
app/Resources/public/assets/blueimp-canvas-to-blob/.bower.json

@@ -0,0 +1,14 @@
+{
+  "name": "blueimp-canvas-to-blob",
+  "homepage": "https://github.com/blueimp/JavaScript-Canvas-to-Blob",
+  "version": "3.3.0",
+  "_release": "3.3.0",
+  "_resolution": {
+    "type": "version",
+    "tag": "v3.3.0",
+    "commit": "443806e20bf0cc6e5b812b0cf686f3109d791074"
+  },
+  "_source": "git://github.com/blueimp/JavaScript-Canvas-to-Blob.git",
+  "_target": ">=2.1.1",
+  "_originalSource": "blueimp-canvas-to-blob"
+}

+ 2 - 0
app/Resources/public/assets/blueimp-canvas-to-blob/.gitignore

@@ -0,0 +1,2 @@
+.DS_Store
+node_modules

+ 3 - 0
app/Resources/public/assets/blueimp-canvas-to-blob/.travis.yml

@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "stable"

+ 84 - 0
app/Resources/public/assets/blueimp-canvas-to-blob/README.md

@@ -0,0 +1,84 @@
+# JavaScript Canvas to Blob
+
+## Description
+Canvas to Blob is a polyfill for the standard JavaScript
+[canvas.toBlob](http://www.w3.org/TR/html5/scripting-1.html#dom-canvas-toblob)
+method.
+
+It can be used to create
+[Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
+objects from an HTML
+[canvas](https://developer.mozilla.org/en-US/docs/HTML/Canvas) element.
+
+## Usage
+Include the (minified) JavaScript Canvas to Blob script in your HTML markup:
+
+```html
+<script src="js/canvas-to-blob.min.js"></script>
+```
+
+Then use the *canvas.toBlob()* method in the same way as the native
+implementation:
+
+```js
+var canvas = document.createElement('canvas');
+/* ... your canvas manipulations ... */
+if (canvas.toBlob) {
+    canvas.toBlob(
+        function (blob) {
+            // Do something with the blob object,
+            // e.g. creating a multipart form for file uploads:
+            var formData = new FormData();
+            formData.append('file', blob, fileName);
+            /* ... */
+        },
+        'image/jpeg'
+    );
+}
+```
+
+## Requirements
+The JavaScript Canvas to Blob function has zero dependencies.
+
+However, Canvas to Blob is a very suitable complement to the
+[JavaScript Load Image](https://github.com/blueimp/JavaScript-Load-Image)
+function.
+
+## API
+In addition to the **canvas.toBlob** polyfill, the JavaScript Canvas to Blob
+script provides one additional function called **dataURLtoBlob**, which is added
+to the global window object, unless the library is loaded via a module loader
+like RequireJS, Browserify or webpack:
+
+```js
+// 80x60px GIF image (color black, base64 data):
+var b64Data = 'R0lGODdhUAA8AIABAAAAAP///ywAAAAAUAA8AAACS4SPqcvtD6' +
+        'OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofE' +
+        'ovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5PKsAAA7',
+    imageUrl = 'data:image/gif;base64,' + b64Data,
+    blob = window.dataURLtoBlob && window.dataURLtoBlob(imageUrl);
+```
+
+## Browsers
+The following browsers support either the native or the polyfill
+*canvas.toBlob()* method:
+
+### Desktop browsers
+
+* Google Chrome (see [Chromium issue #67587](https://code.google.com/p/chromium/issues/detail?id=67587))
+* Apple Safari 6.0+ (see [Mozilla issue #648610](https://bugzilla.mozilla.org/show_bug.cgi?id=648610))
+* Mozilla Firefox 4.0+
+* Microsoft Internet Explorer 10.0+
+
+### Mobile browsers
+
+* Apple Safari Mobile on iOS 6.0+
+* Google Chrome on iOS 6.0+
+* Google Chrome on Android 4.0+
+
+## Test
+[JavaScript Canvas to Blob Test](https://blueimp.github.io/JavaScript-Canvas-to-Blob/test/)
+
+## License
+The JavaScript Canvas to Blob script is released under the
+[MIT license](http://www.opensource.org/licenses/MIT).

+ 111 - 0
app/Resources/public/assets/blueimp-canvas-to-blob/js/canvas-to-blob.js

@@ -0,0 +1,111 @@
+/*
+ * JavaScript Canvas to Blob
+ * https://github.com/blueimp/JavaScript-Canvas-to-Blob
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ *
+ * Based on stackoverflow user Stoive's code snippet:
+ * http://stackoverflow.com/q/4998908
+ */
+
+/*global window, atob, Blob, ArrayBuffer, Uint8Array, define, module */
+
+;(function (window) {
+  'use strict'
+
+  var CanvasPrototype = window.HTMLCanvasElement &&
+                          window.HTMLCanvasElement.prototype
+  var hasBlobConstructor = window.Blob && (function () {
+    try {
+      return Boolean(new Blob())
+    } catch (e) {
+      return false
+    }
+  }())
+  var hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array &&
+    (function () {
+      try {
+        return new Blob([new Uint8Array(100)]).size === 100
+      } catch (e) {
+        return false
+      }
+    }())
+  var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
+                      window.MozBlobBuilder || window.MSBlobBuilder
+  var dataURIPattern = /^data:((.*?)(;charset=.*?)?)(;base64)?,/
+  var dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob &&
+    window.ArrayBuffer && window.Uint8Array &&
+    function (dataURI) {
+      var matches,
+        mediaType,
+        isBase64,
+        dataString,
+        byteString,
+        arrayBuffer,
+        intArray,
+        i,
+        bb
+      // Parse the dataURI components as per RFC 2397
+      matches = dataURI.match(dataURIPattern)
+      if (!matches) {
+        throw new Error('invalid data URI')
+      }
+      // Default to text/plain;charset=US-ASCII
+      mediaType = matches[2]
+        ? matches[1]
+        : 'text/plain' + (matches[3] || ';charset=US-ASCII')
+      isBase64 = !!matches[4]
+      dataString = dataURI.slice(matches[0].length)
+      if (isBase64) {
+        // Convert base64 to raw binary data held in a string:
+        byteString = atob(dataString)
+      } else {
+        // Convert base64/URLEncoded data component to raw binary:
+        byteString = decodeURIComponent(dataString)
+      }
+      // Write the bytes of the string to an ArrayBuffer:
+      arrayBuffer = new ArrayBuffer(byteString.length)
+      intArray = new Uint8Array(arrayBuffer)
+      for (i = 0; i < byteString.length; i += 1) {
+        intArray[i] = byteString.charCodeAt(i)
+      }
+      // Write the ArrayBuffer (or ArrayBufferView) to a blob:
+      if (hasBlobConstructor) {
+        return new Blob(
+          [hasArrayBufferViewSupport ? intArray : arrayBuffer],
+          {type: mediaType}
+        )
+      }
+      bb = new BlobBuilder()
+      bb.append(arrayBuffer)
+      return bb.getBlob(mediaType)
+    }
+  if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) {
+    if (CanvasPrototype.mozGetAsFile) {
+      CanvasPrototype.toBlob = function (callback, type, quality) {
+        if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) {
+          callback(dataURLtoBlob(this.toDataURL(type, quality)))
+        } else {
+          callback(this.mozGetAsFile('blob', type))
+        }
+      }
+    } else if (CanvasPrototype.toDataURL && dataURLtoBlob) {
+      CanvasPrototype.toBlob = function (callback, type, quality) {
+        callback(dataURLtoBlob(this.toDataURL(type, quality)))
+      }
+    }
+  }
+  if (typeof define === 'function' && define.amd) {
+    define(function () {
+      return dataURLtoBlob
+    })
+  } else if (typeof module === 'object' && module.exports) {
+    module.exports = dataURLtoBlob
+  } else {
+    window.dataURLtoBlob = dataURLtoBlob
+  }
+}(window))

+ 2 - 0
app/Resources/public/assets/blueimp-canvas-to-blob/js/canvas-to-blob.min.js

@@ -0,0 +1,2 @@
+!function(t){"use strict";var e=t.HTMLCanvasElement&&t.HTMLCanvasElement.prototype,o=t.Blob&&function(){try{return Boolean(new Blob)}catch(t){return!1}}(),n=o&&t.Uint8Array&&function(){try{return 100===new Blob([new Uint8Array(100)]).size}catch(t){return!1}}(),r=t.BlobBuilder||t.WebKitBlobBuilder||t.MozBlobBuilder||t.MSBlobBuilder,a=/^data:((.*?)(;charset=.*?)?)(;base64)?,/,i=(o||r)&&t.atob&&t.ArrayBuffer&&t.Uint8Array&&function(t){var e,i,l,u,b,c,d,B,f;if(e=t.match(a),!e)throw new Error("invalid data URI");for(i=e[2]?e[1]:"text/plain"+(e[3]||";charset=US-ASCII"),l=!!e[4],u=t.slice(e[0].length),b=l?atob(u):decodeURIComponent(u),c=new ArrayBuffer(b.length),d=new Uint8Array(c),B=0;B<b.length;B+=1)d[B]=b.charCodeAt(B);return o?new Blob([n?d:c],{type:i}):(f=new r,f.append(c),f.getBlob(i))};t.HTMLCanvasElement&&!e.toBlob&&(e.mozGetAsFile?e.toBlob=function(t,o,n){t(n&&e.toDataURL&&i?i(this.toDataURL(o,n)):this.mozGetAsFile("blob",o))}:e.toDataURL&&i&&(e.toBlob=function(t,e,o){t(i(this.toDataURL(e,o)))})),"function"==typeof define&&define.amd?define(function(){return i}):"object"==typeof module&&module.exports?module.exports=i:t.dataURLtoBlob=i}(window);
+//# sourceMappingURL=canvas-to-blob.min.js.map

+ 1 - 0
app/Resources/public/assets/blueimp-canvas-to-blob/js/canvas-to-blob.min.js.map

@@ -0,0 +1 @@
+{"version":3,"sources":["canvas-to-blob.js"],"names":["window","CanvasPrototype","HTMLCanvasElement","prototype","hasBlobConstructor","Blob","Boolean","e","hasArrayBufferViewSupport","Uint8Array","size","BlobBuilder","WebKitBlobBuilder","MozBlobBuilder","MSBlobBuilder","dataURIPattern","dataURLtoBlob","atob","ArrayBuffer","dataURI","matches","mediaType","isBase64","dataString","byteString","arrayBuffer","intArray","i","bb","match","Error","slice","length","decodeURIComponent","charCodeAt","type","append","getBlob","toBlob","mozGetAsFile","callback","quality","toDataURL","this","define","amd","module","exports"],"mappings":"CAgBE,SAAUA,GACV,YAEA,IAAIC,GAAkBD,EAAOE,mBACLF,EAAOE,kBAAkBC,UAC7CC,EAAqBJ,EAAOK,MAAS,WACvC,IACE,MAAOC,SAAQ,GAAID,OACnB,MAAOE,GACP,OAAO,MAGPC,EAA4BJ,GAAsBJ,EAAOS,YAC1D,WACC,IACE,MAAgD,OAAzC,GAAIJ,OAAM,GAAII,YAAW,OAAOC,KACvC,MAAOH,GACP,OAAO,MAGTI,EAAcX,EAAOW,aAAeX,EAAOY,mBAC3BZ,EAAOa,gBAAkBb,EAAOc,cAChDC,EAAiB,0CACjBC,GAAiBZ,GAAsBO,IAAgBX,EAAOiB,MAChEjB,EAAOkB,aAAelB,EAAOS,YAC7B,SAAUU,GACR,GAAIC,GACFC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,CAGF,IADAR,EAAUD,EAAQU,MAAMd,IACnBK,EACH,KAAM,IAAIU,OAAM,mBAkBlB,KAfAT,EAAYD,EAAQ,GAChBA,EAAQ,GACR,cAAgBA,EAAQ,IAAM,qBAClCE,IAAaF,EAAQ,GACrBG,EAAaJ,EAAQY,MAAMX,EAAQ,GAAGY,QAGpCR,EAFEF,EAEWL,KAAKM,GAGLU,mBAAmBV,GAGlCE,EAAc,GAAIP,aAAYM,EAAWQ,QACzCN,EAAW,GAAIjB,YAAWgB,GACrBE,EAAI,EAAGA,EAAIH,EAAWQ,OAAQL,GAAK,EACtCD,EAASC,GAAKH,EAAWU,WAAWP,EAGtC,OAAIvB,GACK,GAAIC,OACRG,EAA4BkB,EAAWD,IACvCU,KAAMd,KAGXO,EAAK,GAAIjB,GACTiB,EAAGQ,OAAOX,GACHG,EAAGS,QAAQhB,IAElBrB,GAAOE,oBAAsBD,EAAgBqC,SAC3CrC,EAAgBsC,aAClBtC,EAAgBqC,OAAS,SAAUE,EAAUL,EAAMM,GAE/CD,EADEC,GAAWxC,EAAgByC,WAAa1B,EACjCA,EAAc2B,KAAKD,UAAUP,EAAMM,IAEnCE,KAAKJ,aAAa,OAAQJ,KAG9BlC,EAAgByC,WAAa1B,IACtCf,EAAgBqC,OAAS,SAAUE,EAAUL,EAAMM,GACjDD,EAASxB,EAAc2B,KAAKD,UAAUP,EAAMM,QAI5B,kBAAXG,SAAyBA,OAAOC,IACzCD,OAAO,WACL,MAAO5B,KAEkB,gBAAX8B,SAAuBA,OAAOC,QAC9CD,OAAOC,QAAU/B,EAEjBhB,EAAOgB,cAAgBA,GAEzBhB","file":"canvas-to-blob.min.js"}

+ 36 - 0
app/Resources/public/assets/blueimp-canvas-to-blob/package.json

@@ -0,0 +1,36 @@
+{
+  "name": "blueimp-canvas-to-blob",
+  "version": "3.3.0",
+  "title": "JavaScript Canvas to Blob",
+  "description": "Canvas to Blob is a polyfill for the standard JavaScript canvas.toBlob method. It can be used to create Blob objects from an HTML canvas element.",
+  "keywords": [
+    "javascript",
+    "canvas",
+    "blob",
+    "convert",
+    "conversion"
+  ],
+  "homepage": "https://github.com/blueimp/JavaScript-Canvas-to-Blob",
+  "author": {
+    "name": "Sebastian Tschan",
+    "url": "https://blueimp.net"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/blueimp/JavaScript-Canvas-to-Blob.git"
+  },
+  "license": "MIT",
+  "main": "./js/canvas-to-blob.js",
+  "devDependencies": {
+    "mocha-phantomjs": "4.0.1",
+    "standard": "6.0.7",
+    "uglify-js": "2.6.1"
+  },
+  "scripts": {
+    "test": "standard js/*.js test/*.js && mocha-phantomjs test/index.html",
+    "build": "cd js && uglifyjs canvas-to-blob.js -c -m -o canvas-to-blob.min.js --source-map canvas-to-blob.min.js.map",
+    "preversion": "npm test",
+    "version": "npm run build && git add -A js",
+    "postversion": "git push --tags origin master master:gh-pages && npm publish"
+  }
+}

+ 40 - 0
app/Resources/public/assets/blueimp-canvas-to-blob/test/index.html

@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<!--
+/*
+ * JavaScript Canvas to Blob Test
+ * https://github.com/blueimp/JavaScript-Canvas-to-Blob
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+-->
+<html lang="en">
+<head>
+<!--[if IE]>
+<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+<![endif]-->
+<meta charset="utf-8">
+<title>JavaScript Canvas to Blob Test</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link rel="stylesheet" href="vendor/mocha.css">
+</head>
+<body>
+<div id="mocha"></div>
+<script src="vendor/mocha.js"></script>
+<script src="vendor/expect.js"></script>
+<script>
+window.initMochaPhantomJS && initMochaPhantomJS();
+mocha.setup('bdd');
+</script>
+<script src="vendor/load-image.js"></script>
+<script src="../js/canvas-to-blob.js"></script>
+<script src="test.js"></script>
+<script>
+mocha.checkLeaks();
+mocha.run();
+</script>
+</body>
+</html>

+ 92 - 0
app/Resources/public/assets/blueimp-canvas-to-blob/test/test.js

@@ -0,0 +1,92 @@
+/*
+ * JavaScript Canvas to Blob Test
+ * https://github.com/blueimp/JavaScript-Canvas-to-Blob
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global window, describe, it, Blob */
+
+;(function (expect) {
+  'use strict'
+
+  // 80x60px GIF image (color black, base64 data):
+  var b64Data = 'R0lGODdhUAA8AIABAAAAAP///ywAAAAAUAA8AAACS4SPqcvtD6' +
+      'OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofE' +
+      'ovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5PKsAAA7'
+  var imageUrl = 'data:image/gif;base64,' + b64Data
+  var blob = window.dataURLtoBlob && window.dataURLtoBlob(imageUrl)
+
+  describe('canvas.toBlob', function () {
+    it('Converts a canvas element to a blob and passes it to the callback function', function (done) {
+      window.loadImage(blob, function (canvas) {
+        canvas.toBlob(
+          function (newBlob) {
+            expect(newBlob).to.be.a(Blob)
+            done()
+          }
+        )
+      }, {canvas: true})
+    })
+
+    it('Converts a canvas element to a PNG blob', function (done) {
+      window.loadImage(blob, function (canvas) {
+        canvas.toBlob(
+          function (newBlob) {
+            expect(newBlob.type).to.be('image/png')
+            done()
+          },
+          'image/png'
+        )
+      }, {canvas: true})
+    })
+
+    it('Converts a canvas element to a JPG blob', function (done) {
+      window.loadImage(blob, function (canvas) {
+        canvas.toBlob(
+          function (newBlob) {
+            expect(newBlob.type).to.be('image/jpeg')
+            done()
+          },
+          'image/jpeg'
+        )
+      }, {canvas: true})
+    })
+
+    it('Keeps the aspect ratio of the canvas image', function (done) {
+      window.loadImage(blob, function (canvas) {
+        canvas.toBlob(
+          function (newBlob) {
+            window.loadImage(newBlob, function (img) {
+              expect(img.width).to.be(canvas.width)
+              expect(img.height).to.be(canvas.height)
+              done()
+            })
+          }
+        )
+      }, {canvas: true})
+    })
+
+    it('Keeps the image data of the canvas image', function (done) {
+      window.loadImage(blob, function (canvas) {
+        canvas.toBlob(
+          function (newBlob) {
+            window.loadImage(newBlob, function (newCanvas) {
+              var canvasData = canvas.getContext('2d')
+                  .getImageData(0, 0, canvas.width, canvas.height)
+              var newCanvasData = newCanvas.getContext('2d')
+                  .getImageData(0, 0, newCanvas.width, newCanvas.height)
+              expect(canvasData.width).to.be(newCanvasData.width)
+              expect(canvasData.height).to.be(newCanvasData.height)
+              done()
+            }, {canvas: true})
+          }
+        )
+      }, {canvas: true})
+    })
+  })
+}(this.expect))

+ 14 - 0
app/Resources/public/assets/blueimp-load-image/.bower.json

@@ -0,0 +1,14 @@
+{
+  "name": "blueimp-load-image",
+  "homepage": "https://github.com/blueimp/JavaScript-Load-Image",
+  "version": "2.6.0",
+  "_release": "2.6.0",
+  "_resolution": {
+    "type": "version",
+    "tag": "v2.6.0",
+    "commit": "e6864b8a8be83ff88a1596899fc96c6d6cde19c0"
+  },
+  "_source": "git://github.com/blueimp/JavaScript-Load-Image.git",
+  "_target": ">=1.13.0",
+  "_originalSource": "blueimp-load-image"
+}

+ 2 - 0
app/Resources/public/assets/blueimp-load-image/.gitignore

@@ -0,0 +1,2 @@
+.DS_Store
+node_modules

+ 3 - 0
app/Resources/public/assets/blueimp-load-image/.travis.yml

@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "stable"

+ 315 - 0
app/Resources/public/assets/blueimp-load-image/README.md

@@ -0,0 +1,315 @@
+# JavaScript Load Image
+
+> A JavaScript library to load and transform image files.
+
+## Table of contents
+
+- [Demo](#demo)
+- [Description](#description)
+- [Setup](#setup)
+- [Usage](#usage)
+- [Image loading](#image-loading)
+- [Image scaling](#image-scaling)
+- [Requirements](#requirements)
+- [API](#api)
+- [Options](#options)
+- [Meta data parsing](#meta-data-parsing)
+- [Exif parser](#exif-parser)
+- [License](#license)
+- [Credits](#credits)
+
+## Demo
+[JavaScript Load Image Demo](https://blueimp.github.io/JavaScript-Load-Image/)
+
+## Description
+JavaScript Load Image is a library to load images provided as File or Blob
+objects or via URL.  
+It returns an optionally scaled and/or cropped HTML img or canvas element via an
+asynchronous callback.  
+It also provides a method to parse image meta data to extract Exif tags and
+thumbnails and to restore the complete image header after resizing.
+
+## Setup
+Include the (combined and minified) JavaScript Load Image script in your HTML
+markup:
+
+```html
+<script src="js/load-image.all.min.js"></script>
+```
+
+Or alternatively, choose which components you want to include:
+
+```html
+<script src="js/load-image.js"></script>
+<script src="js/load-image-orientation.js"></script>
+<script src="js/load-image-meta.js"></script>
+<script src="js/load-image-exif.js"></script>
+<script src="js/load-image-exif-map.js"></script>
+```
+
+## Usage
+
+### Image loading
+In your application code, use the **loadImage()** function like this:
+
+```js
+document.getElementById('file-input').onchange = function (e) {
+    loadImage(
+        e.target.files[0],
+        function (img) {
+            document.body.appendChild(img);
+        },
+        {maxWidth: 600} // Options
+    );
+};
+```
+
+### Image scaling
+It is also possible to use the image scaling functionality with an existing
+image:
+
+```js
+var scaledImage = loadImage.scale(
+    img, // img or canvas element
+    {maxWidth: 600}
+);
+```
+
+## Requirements
+The JavaScript Load Image library has zero dependencies.
+
+However, JavaScript Load Image is a very suitable complement to the
+[Canvas to Blob](https://github.com/blueimp/JavaScript-Canvas-to-Blob) library.
+
+## API
+The **loadImage()** function accepts a
+[File](https://developer.mozilla.org/en/DOM/File) or
+[Blob](https://developer.mozilla.org/en/DOM/Blob) object or a simple image URL
+(e.g. `'https://example.org/image.png'`) as first argument.
+
+If a [File](https://developer.mozilla.org/en/DOM/File) or
+[Blob](https://developer.mozilla.org/en/DOM/Blob) is passed as parameter, it
+returns a HTML **img** element if the browser supports the
+[URL](https://developer.mozilla.org/en/DOM/window.URL) API or a
+[FileReader](https://developer.mozilla.org/en/DOM/FileReader) object if
+supported, or **false**.  
+It always returns a HTML
+[img](https://developer.mozilla.org/en/docs/HTML/Element/Img) element when
+passing an image URL:
+
+```js
+document.getElementById('file-input').onchange = function (e) {
+    var loadingImage = loadImage(
+        e.target.files[0],
+        function (img) {
+            document.body.appendChild(img);
+        },
+        {maxWidth: 600}
+    );
+    if (!loadingImage) {
+        // Alternative code ...
+    }
+};
+```
+
+The **img** element or
+[FileReader](https://developer.mozilla.org/en/DOM/FileReader) object returned by
+the **loadImage()** function allows to abort the loading process by setting the
+**onload** and **onerror** event handlers to null:
+
+```js
+document.getElementById('file-input').onchange = function (e) {
+    var loadingImage = loadImage(
+        e.target.files[0],
+        function (img) {
+            document.body.appendChild(img);
+        },
+        {maxWidth: 600}
+    );
+    loadingImage.onload = loadingImage.onerror = null;
+};
+```
+
+The second argument must be a **callback** function, which is called when the
+image has been loaded or an error occurred while loading the image. The callback
+function is passed one argument, which is either a HTML **img** element, a
+[canvas](https://developer.mozilla.org/en/HTML/Canvas) element, or an
+[Event](https://developer.mozilla.org/en/DOM/event) object of type **error**:
+
+```js
+var imageUrl = "https://example.org/image.png";
+loadImage(
+    imageUrl,
+    function (img) {
+        if(img.type === "error") {
+            console.log("Error loading image " + imageUrl);
+        } else {
+            document.body.appendChild(img);
+        }
+    },
+    {maxWidth: 600}
+);
+```
+
+## Options
+The optional third argument to **loadImage()** is a map of options:
+
+* **maxWidth**: Defines the maximum width of the img/canvas element.
+* **maxHeight**: Defines the maximum height of the img/canvas element.
+* **minWidth**: Defines the minimum width of the img/canvas element.
+* **minHeight**: Defines the minimum height of the img/canvas element.
+* **sourceWidth**: The width of the sub-rectangle of the source image to draw
+into the destination canvas.  
+Defaults to the source image width and requires `canvas: true`.
+* **sourceHeight**: The height of the sub-rectangle of the source image to draw
+into the destination canvas.  
+Defaults to the source image height and requires `canvas: true`.
+* **top**: The top margin of the sub-rectangle of the source image.  
+Defaults to `0` and requires `canvas: true`.
+* **right**: The right margin of the sub-rectangle of the source image.  
+Defaults to `0` and requires `canvas: true`.
+* **bottom**: The bottom margin of the sub-rectangle of the source image.  
+Defaults to `0` and requires `canvas: true`.
+* **left**: The left margin of the sub-rectangle of the source image.  
+Defaults to `0` and requires `canvas: true`.
+* **contain**: Scales the image up/down to contain it in the max dimensions if
+set to `true`.  
+This emulates the CSS feature
+[background-image: contain](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Scaling_background_images#contain).
+* **cover**: Scales the image up/down to cover the max dimensions with the image
+dimensions if set to `true`.  
+This emulates the CSS feature
+[background-image: cover](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Scaling_background_images#cover).
+* **aspectRatio**: Crops the image to the given aspect ratio (e.g. `16/9`).  
+Setting the `aspectRatio` also enables the `crop` option.
+* **pixelRatio**: Defines the ratio of the canvas pixels to the physical image
+pixels on the screen.  
+Should be set to `window.devicePixelRatio` unless the scaled image is not
+rendered on screen.  
+Defaults to `1` and requires `canvas: true`.
+* **downsamplingRatio**: Defines the ratio in which the image is downsampled.  
+By default, images are downsampled in one step. With a ratio of `0.5`, each step
+scales the image to half the size, before reaching the target dimensions.  
+Requires `canvas: true`.
+* **crop**: Crops the image to the maxWidth/maxHeight constraints if set to
+`true`.  
+Enabling the `crop` option also enables the `canvas` option.
+* **orientation**: Allows to transform the canvas coordinates according to the
+EXIF orientation specification.  
+Setting the `orientation` also enables the `canvas` option.
+* **canvas**: Returns the image as
+[canvas](https://developer.mozilla.org/en/HTML/Canvas) element if set to `true`.
+* **crossOrigin**: Sets the crossOrigin property on the img element for loading
+[CORS enabled images](https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image).
+* **noRevoke**: By default, the
+[created object URL](https://developer.mozilla.org/en/DOM/window.URL.createObjectURL)
+is revoked after the image has been loaded, except when this option is set to
+`true`.
+
+They can be used the following way:
+
+```js
+loadImage(
+    fileOrBlobOrUrl,
+    function (img) {
+        document.body.appendChild(img);
+    },
+    {
+        maxWidth: 600,
+        maxHeight: 300,
+        minWidth: 100,
+        minHeight: 50,
+        canvas: true
+    }
+);
+```
+
+All settings are optional. By default, the image is returned as HTML **img**
+element without any image size restrictions.
+
+## Meta data parsing
+If the Load Image Meta extension is included, it is also possible to parse image
+meta data.  
+The extension provides the method **loadImage.parseMetaData**, which can be used
+the following way:
+
+```js
+loadImage.parseMetaData(
+    fileOrBlob,
+    function (data) {
+        if (!data.imageHead) {
+            return;
+        }
+        // Combine data.imageHead with the image body of a resized file
+        // to create scaled images with the original image meta data, e.g.:
+        var blob = new Blob([
+            data.imageHead,
+            // Resized images always have a head size of 20 bytes,
+            // including the JPEG marker and a minimal JFIF header:
+            loadImage.blobSlice.call(resizedImage, 20)
+        ], {type: resizedImage.type});
+    },
+    {
+        maxMetaDataSize: 262144,
+        disableImageHead: false
+    }
+);
+```
+
+The third argument is an options object which defines the maximum number of
+bytes to parse for the image meta data, allows to disable the imageHead creation
+and is also passed along to segment parsers registered via loadImage extensions,
+e.g. the Exif parser.
+
+**Note:**  
+Blob objects of resized images can be created via
+[canvas.toBlob()](https://github.com/blueimp/JavaScript-Canvas-to-Blob).
+
+### Exif parser
+If you include the Load Image Exif Parser extension, the **parseMetaData**
+callback **data** contains the additional property **exif** if Exif data could
+be found in the given image.  
+The **exif** object stores the parsed Exif tags:
+
+```js
+var orientation = data.exif[0x0112];
+```
+
+It also provides an **exif.get()** method to retrieve the tag value via the
+tag's mapped name:
+
+```js
+var orientation = data.exif.get('Orientation');
+```
+
+By default, the only available mapped names are **Orientation** and
+**Thumbnail**.  
+If you also include the Load Image Exif Map library, additional tag mappings
+become available, as well as two additional methods, **exif.getText()** and
+**exif.getAll()**:
+
+```js
+var flashText = data.exif.getText('Flash'); // e.g.: 'Flash fired, auto mode',
+
+// A map of all parsed tags with their mapped names as keys and their text values:
+var allTags = data.exif.getAll();
+```
+
+The Exif parser also adds additional options for the parseMetaData method, to
+disable certain aspects of the parser:
+
+* **disableExif**: Disables Exif parsing.
+* **disableExifThumbnail**: Disables parsing of the Exif Thumbnail.
+* **disableExifSub**: Disables parsing of the Exif Sub IFD.
+* **disableExifGps**: Disables parsing of the Exif GPS Info IFD.
+
+## License
+The JavaScript Load Image script is released under the
+[MIT license](http://www.opensource.org/licenses/MIT).
+
+## Credits
+
+* Image meta data handling implementation based on the help and contribution of
+Achim Stöhr.
+* Exif tags mapping based on Jacob Seidelin's
+[exif-js](https://github.com/jseidelin/exif-js).

+ 74 - 0
app/Resources/public/assets/blueimp-load-image/css/demo.css

@@ -0,0 +1,74 @@
+/*
+ * JavaScript Load Image Demo CSS
+ * https://github.com/blueimp/JavaScript-Load-Image
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+body {
+  max-width: 750px;
+  margin: 0 auto;
+  padding: 1em;
+  font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, sans-serif;
+  font-size: 1em;
+  line-height: 1.4em;
+  background: #222;
+  color: #fff;
+  -webkit-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+}
+a {
+  color: orange;
+  text-decoration: none;
+}
+img {
+  border: 0;
+  vertical-align: middle;
+}
+h1 {
+  line-height: 1em;
+}
+table {
+  width: 100%;
+  word-wrap: break-word;
+  table-layout: fixed;
+  border-collapse: collapse;
+}
+tr {
+  background: #fff;
+  color: #222;
+}
+tr:nth-child(odd) {
+  background: #eee;
+  color: #222;
+}
+td {
+  padding: 10px;
+}
+.result,
+.thumbnail {
+  padding: 20px;
+  background: #fff;
+  color: #222;
+  text-align: center;
+}
+.jcrop-holder {
+  margin: 0 auto;
+}
+
+@media (min-width: 481px) {
+  .navigation {
+    list-style: none;
+    padding: 0;
+  }
+  .navigation li {
+    display: inline-block;
+  }
+  .navigation li:not(:first-child):before {
+    content: '| ';
+  }
+}

+ 69 - 0
app/Resources/public/assets/blueimp-load-image/index.html

@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<!--
+/*
+ * JavaScript Load Image Demo
+ * https://github.com/blueimp/JavaScript-Load-Image
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+-->
+<html lang="en">
+<head>
+<!--[if IE]>
+<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+<![endif]-->
+<meta charset="utf-8">
+<title>JavaScript Load Image</title>
+<meta name="description" content="JavaScript Load Image is a library to load images provided as File or Blob objects or via URL. It returns an optionally scaled and/or cropped HTML img or canvas element. It also provides a method to parse image meta data to extract Exif tags and thumbnails and to restore the complete image header after resizing.">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<!-- Jcrop is not required by JavaScript Load Image, but included for the demo -->
+<link rel="stylesheet" href="css/vendor/jquery.Jcrop.css">
+<link rel="stylesheet" href="css/demo.css">
+</head>
+<body>
+<h1>JavaScript Load Image Demo</h1>
+<p><a href="https://github.com/blueimp/JavaScript-Load-Image">JavaScript Load Image</a> is a library to load images provided as <a href="https://developer.mozilla.org/en/DOM/File">File</a> or <a href="https://developer.mozilla.org/en/DOM/Blob">Blob</a> objects or via URL.<br>
+It returns an optionally <strong>scaled</strong> and/or <strong>cropped</strong> HTML <a href="https://developer.mozilla.org/en/docs/HTML/Element/Img">img</a> or <a href="https://developer.mozilla.org/en/docs/HTML/Canvas">canvas</a> element.<br>
+It also provides a method to parse image meta data to extract <a href="https://en.wikipedia.org/wiki/Exif">Exif</a> tags and thumbnails and to restore the complete image header after resizing.</p>
+<ul class="navigation">
+    <li><a href="https://github.com/blueimp/JavaScript-Load-Image/tags">Download</a></li>
+    <li><a href="https://github.com/blueimp/JavaScript-Load-Image">Source Code</a></li>
+    <li><a href="https://github.com/blueimp/JavaScript-Load-Image/blob/master/README.md">Documentation</a></li>
+    <li><a href="test/">Test</a></li>
+    <li><a href="https://blueimp.net">&copy; Sebastian Tschan</a></li>
+</ul>
+<br>
+<h2>Select an image file</h2>
+<p><input type="file" id="file-input"></p>
+<p>Or <strong>drag &amp; drop</strong> an image file onto this webpage.</p>
+<br>
+<h2>Result</h2>
+<p id="actions" style="display:none;">
+	<button type="button" id="edit">Edit</button>
+	<button type="button" id="crop">Crop</button>
+</p>
+<div id="result" class="result">
+    <p>This demo works only in browsers with support for the <a href="https://developer.mozilla.org/en/DOM/window.URL">URL</a> or <a href="https://developer.mozilla.org/en/DOM/FileReader">FileReader</a> API.</p>
+</div>
+<br>
+<div id="exif" class="exif" style="display:none;">
+    <h2>Exif meta data</h2>
+    <p id="thumbnail" class="thumbnail" style="display:none;"></p>
+    <table></table>
+</div>
+<br>
+<script src="js/load-image.js"></script>
+<script src="js/load-image-orientation.js"></script>
+<script src="js/load-image-meta.js"></script>
+<script src="js/load-image-exif.js"></script>
+<script src="js/load-image-exif-map.js"></script>
+<!-- jQuery and Jcrop are not required by JavaScript Load Image, but included for the demo -->
+<script src="js/vendor/jquery.js"></script>
+<script src="js/vendor/jquery.Jcrop.js"></script>
+<script src="js/demo.js"></script>
+</body>
+</html>

+ 149 - 0
app/Resources/public/assets/blueimp-load-image/js/demo.js

@@ -0,0 +1,149 @@
+/*
+ * JavaScript Load Image Demo JS
+ * https://github.com/blueimp/JavaScript-Load-Image
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global window, document, loadImage, HTMLCanvasElement, $ */
+
+$(function () {
+  'use strict'
+
+  var result = $('#result')
+  var exifNode = $('#exif')
+  var thumbNode = $('#thumbnail')
+  var actionsNode = $('#actions')
+  var currentFile
+  var coordinates
+  function replaceResults (img) {
+    var content
+    if (!(img.src || img instanceof HTMLCanvasElement)) {
+      content = $('<span>Loading image file failed</span>')
+    } else {
+      content = $('<a target="_blank">').append(img)
+        .attr('download', currentFile.name)
+        .attr('href', img.src || img.toDataURL())
+    }
+    result.children().replaceWith(content)
+    if (img.getContext) {
+      actionsNode.show()
+    }
+  }
+  function displayImage (file, options) {
+    currentFile = file
+    if (!loadImage(
+        file,
+        replaceResults,
+        options
+      )) {
+      result.children().replaceWith(
+        $('<span>Your browser does not support the URL or FileReader API.</span>')
+      )
+    }
+  }
+  function displayExifData (exif) {
+    var thumbnail = exif.get('Thumbnail')
+    var tags = exif.getAll()
+    var table = exifNode.find('table').empty()
+    var row = $('<tr></tr>')
+    var cell = $('<td></td>')
+    var prop
+    if (thumbnail) {
+      thumbNode.empty()
+      loadImage(thumbnail, function (img) {
+        thumbNode.append(img).show()
+      }, {orientation: exif.get('Orientation')})
+    }
+    for (prop in tags) {
+      if (tags.hasOwnProperty(prop)) {
+        table.append(
+          row.clone()
+            .append(cell.clone().text(prop))
+            .append(cell.clone().text(tags[prop]))
+        )
+      }
+    }
+    exifNode.show()
+  }
+  function dropChangeHandler (e) {
+    e.preventDefault()
+    e = e.originalEvent
+    var target = e.dataTransfer || e.target
+    var file = target && target.files && target.files[0]
+    var options = {
+      maxWidth: result.width(),
+      canvas: true,
+      pixelRatio: window.devicePixelRatio,
+      downsamplingRatio: 0.5
+    }
+    if (!file) {
+      return
+    }
+    exifNode.hide()
+    thumbNode.hide()
+    loadImage.parseMetaData(file, function (data) {
+      if (data.exif) {
+        options.orientation = data.exif.get('Orientation')
+        displayExifData(data.exif)
+      }
+      displayImage(file, options)
+    })
+  }
+  // Hide URL/FileReader API requirement message in capable browsers:
+  if (window.createObjectURL || window.URL || window.webkitURL || window.FileReader) {
+    result.children().hide()
+  }
+  $(document)
+    .on('dragover', function (e) {
+      e.preventDefault()
+      e = e.originalEvent
+      e.dataTransfer.dropEffect = 'copy'
+    })
+    .on('drop', dropChangeHandler)
+  $('#file-input').on('change', dropChangeHandler)
+  $('#edit').on('click', function (event) {
+    event.preventDefault()
+    var imgNode = result.find('img, canvas')
+    var img = imgNode[0]
+    var pixelRatio = window.devicePixelRatio || 1
+    imgNode.Jcrop({
+      setSelect: [
+        40,
+        40,
+        (img.width / pixelRatio) - 40,
+        (img.height / pixelRatio) - 40
+      ],
+      onSelect: function (coords) {
+        coordinates = coords
+      },
+      onRelease: function () {
+        coordinates = null
+      }
+    }).parent().on('click', function (event) {
+      event.preventDefault()
+    })
+  })
+  $('#crop').on('click', function (event) {
+    event.preventDefault()
+    var img = result.find('img, canvas')[0]
+    var pixelRatio = window.devicePixelRatio || 1
+    if (img && coordinates) {
+      replaceResults(loadImage.scale(img, {
+        left: coordinates.x * pixelRatio,
+        top: coordinates.y * pixelRatio,
+        sourceWidth: coordinates.w * pixelRatio,
+        sourceHeight: coordinates.h * pixelRatio,
+        minWidth: result.width(),
+        maxWidth: result.width(),
+        pixelRatio: pixelRatio,
+        downsamplingRatio: 0.5
+      }))
+      coordinates = null
+    }
+  })
+})

+ 384 - 0
app/Resources/public/assets/blueimp-load-image/js/load-image-exif-map.js

@@ -0,0 +1,384 @@
+/*
+ * JavaScript Load Image Exif Map
+ * https://github.com/blueimp/JavaScript-Load-Image
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Exif tags mapping based on
+ * https://github.com/jseidelin/exif-js
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global define, module, require, window */
+
+;(function (factory) {
+  'use strict'
+  if (typeof define === 'function' && define.amd) {
+    // Register as an anonymous AMD module:
+    define(['load-image', 'load-image-exif'], factory)
+  } else if (typeof module === 'object' && module.exports) {
+    factory(require('./load-image'), require('./load-image-exif'))
+  } else {
+    // Browser globals:
+    factory(window.loadImage)
+  }
+}(function (loadImage) {
+  'use strict'
+
+  loadImage.ExifMap.prototype.tags = {
+    // =================
+    // TIFF tags (IFD0):
+    // =================
+    0x0100: 'ImageWidth',
+    0x0101: 'ImageHeight',
+    0x8769: 'ExifIFDPointer',
+    0x8825: 'GPSInfoIFDPointer',
+    0xA005: 'InteroperabilityIFDPointer',
+    0x0102: 'BitsPerSample',
+    0x0103: 'Compression',
+    0x0106: 'PhotometricInterpretation',
+    0x0112: 'Orientation',
+    0x0115: 'SamplesPerPixel',
+    0x011C: 'PlanarConfiguration',
+    0x0212: 'YCbCrSubSampling',
+    0x0213: 'YCbCrPositioning',
+    0x011A: 'XResolution',
+    0x011B: 'YResolution',
+    0x0128: 'ResolutionUnit',
+    0x0111: 'StripOffsets',
+    0x0116: 'RowsPerStrip',
+    0x0117: 'StripByteCounts',
+    0x0201: 'JPEGInterchangeFormat',
+    0x0202: 'JPEGInterchangeFormatLength',
+    0x012D: 'TransferFunction',
+    0x013E: 'WhitePoint',
+    0x013F: 'PrimaryChromaticities',
+    0x0211: 'YCbCrCoefficients',
+    0x0214: 'ReferenceBlackWhite',
+    0x0132: 'DateTime',
+    0x010E: 'ImageDescription',
+    0x010F: 'Make',
+    0x0110: 'Model',
+    0x0131: 'Software',
+    0x013B: 'Artist',
+    0x8298: 'Copyright',
+    // ==================
+    // Exif Sub IFD tags:
+    // ==================
+    0x9000: 'ExifVersion', // EXIF version
+    0xA000: 'FlashpixVersion', // Flashpix format version
+    0xA001: 'ColorSpace', // Color space information tag
+    0xA002: 'PixelXDimension', // Valid width of meaningful image
+    0xA003: 'PixelYDimension', // Valid height of meaningful image
+    0xA500: 'Gamma',
+    0x9101: 'ComponentsConfiguration', // Information about channels
+    0x9102: 'CompressedBitsPerPixel', // Compressed bits per pixel
+    0x927C: 'MakerNote', // Any desired information written by the manufacturer
+    0x9286: 'UserComment', // Comments by user
+    0xA004: 'RelatedSoundFile', // Name of related sound file
+    0x9003: 'DateTimeOriginal', // Date and time when the original image was generated
+    0x9004: 'DateTimeDigitized', // Date and time when the image was stored digitally
+    0x9290: 'SubSecTime', // Fractions of seconds for DateTime
+    0x9291: 'SubSecTimeOriginal', // Fractions of seconds for DateTimeOriginal
+    0x9292: 'SubSecTimeDigitized', // Fractions of seconds for DateTimeDigitized
+    0x829A: 'ExposureTime', // Exposure time (in seconds)
+    0x829D: 'FNumber',
+    0x8822: 'ExposureProgram', // Exposure program
+    0x8824: 'SpectralSensitivity', // Spectral sensitivity
+    0x8827: 'PhotographicSensitivity', // EXIF 2.3, ISOSpeedRatings in EXIF 2.2
+    0x8828: 'OECF', // Optoelectric conversion factor
+    0x8830: 'SensitivityType',
+    0x8831: 'StandardOutputSensitivity',
+    0x8832: 'RecommendedExposureIndex',
+    0x8833: 'ISOSpeed',
+    0x8834: 'ISOSpeedLatitudeyyy',
+    0x8835: 'ISOSpeedLatitudezzz',
+    0x9201: 'ShutterSpeedValue', // Shutter speed
+    0x9202: 'ApertureValue', // Lens aperture
+    0x9203: 'BrightnessValue', // Value of brightness
+    0x9204: 'ExposureBias', // Exposure bias
+    0x9205: 'MaxApertureValue', // Smallest F number of lens
+    0x9206: 'SubjectDistance', // Distance to subject in meters
+    0x9207: 'MeteringMode', // Metering mode
+    0x9208: 'LightSource', // Kind of light source
+    0x9209: 'Flash', // Flash status
+    0x9214: 'SubjectArea', // Location and area of main subject
+    0x920A: 'FocalLength', // Focal length of the lens in mm
+    0xA20B: 'FlashEnergy', // Strobe energy in BCPS
+    0xA20C: 'SpatialFrequencyResponse',
+    0xA20E: 'FocalPlaneXResolution', // Number of pixels in width direction per FPRUnit
+    0xA20F: 'FocalPlaneYResolution', // Number of pixels in height direction per FPRUnit
+    0xA210: 'FocalPlaneResolutionUnit', // Unit for measuring the focal plane resolution
+    0xA214: 'SubjectLocation', // Location of subject in image
+    0xA215: 'ExposureIndex', // Exposure index selected on camera
+    0xA217: 'SensingMethod', // Image sensor type
+    0xA300: 'FileSource', // Image source (3 == DSC)
+    0xA301: 'SceneType', // Scene type (1 == directly photographed)
+    0xA302: 'CFAPattern', // Color filter array geometric pattern
+    0xA401: 'CustomRendered', // Special processing
+    0xA402: 'ExposureMode', // Exposure mode
+    0xA403: 'WhiteBalance', // 1 = auto white balance, 2 = manual
+    0xA404: 'DigitalZoomRatio', // Digital zoom ratio
+    0xA405: 'FocalLengthIn35mmFilm',
+    0xA406: 'SceneCaptureType', // Type of scene
+    0xA407: 'GainControl', // Degree of overall image gain adjustment
+    0xA408: 'Contrast', // Direction of contrast processing applied by camera
+    0xA409: 'Saturation', // Direction of saturation processing applied by camera
+    0xA40A: 'Sharpness', // Direction of sharpness processing applied by camera
+    0xA40B: 'DeviceSettingDescription',
+    0xA40C: 'SubjectDistanceRange', // Distance to subject
+    0xA420: 'ImageUniqueID', // Identifier assigned uniquely to each image
+    0xA430: 'CameraOwnerName',
+    0xA431: 'BodySerialNumber',
+    0xA432: 'LensSpecification',
+    0xA433: 'LensMake',
+    0xA434: 'LensModel',
+    0xA435: 'LensSerialNumber',
+    // ==============
+    // GPS Info tags:
+    // ==============
+    0x0000: 'GPSVersionID',
+    0x0001: 'GPSLatitudeRef',
+    0x0002: 'GPSLatitude',
+    0x0003: 'GPSLongitudeRef',
+    0x0004: 'GPSLongitude',
+    0x0005: 'GPSAltitudeRef',
+    0x0006: 'GPSAltitude',
+    0x0007: 'GPSTimeStamp',
+    0x0008: 'GPSSatellites',
+    0x0009: 'GPSStatus',
+    0x000A: 'GPSMeasureMode',
+    0x000B: 'GPSDOP',
+    0x000C: 'GPSSpeedRef',
+    0x000D: 'GPSSpeed',
+    0x000E: 'GPSTrackRef',
+    0x000F: 'GPSTrack',
+    0x0010: 'GPSImgDirectionRef',
+    0x0011: 'GPSImgDirection',
+    0x0012: 'GPSMapDatum',
+    0x0013: 'GPSDestLatitudeRef',
+    0x0014: 'GPSDestLatitude',
+    0x0015: 'GPSDestLongitudeRef',
+    0x0016: 'GPSDestLongitude',
+    0x0017: 'GPSDestBearingRef',
+    0x0018: 'GPSDestBearing',
+    0x0019: 'GPSDestDistanceRef',
+    0x001A: 'GPSDestDistance',
+    0x001B: 'GPSProcessingMethod',
+    0x001C: 'GPSAreaInformation',
+    0x001D: 'GPSDateStamp',
+    0x001E: 'GPSDifferential',
+    0x001F: 'GPSHPositioningError'
+  }
+
+  loadImage.ExifMap.prototype.stringValues = {
+    ExposureProgram: {
+      0: 'Undefined',
+      1: 'Manual',
+      2: 'Normal program',
+      3: 'Aperture priority',
+      4: 'Shutter priority',
+      5: 'Creative program',
+      6: 'Action program',
+      7: 'Portrait mode',
+      8: 'Landscape mode'
+    },
+    MeteringMode: {
+      0: 'Unknown',
+      1: 'Average',
+      2: 'CenterWeightedAverage',
+      3: 'Spot',
+      4: 'MultiSpot',
+      5: 'Pattern',
+      6: 'Partial',
+      255: 'Other'
+    },
+    LightSource: {
+      0: 'Unknown',
+      1: 'Daylight',
+      2: 'Fluorescent',
+      3: 'Tungsten (incandescent light)',
+      4: 'Flash',
+      9: 'Fine weather',
+      10: 'Cloudy weather',
+      11: 'Shade',
+      12: 'Daylight fluorescent (D 5700 - 7100K)',
+      13: 'Day white fluorescent (N 4600 - 5400K)',
+      14: 'Cool white fluorescent (W 3900 - 4500K)',
+      15: 'White fluorescent (WW 3200 - 3700K)',
+      17: 'Standard light A',
+      18: 'Standard light B',
+      19: 'Standard light C',
+      20: 'D55',
+      21: 'D65',
+      22: 'D75',
+      23: 'D50',
+      24: 'ISO studio tungsten',
+      255: 'Other'
+    },
+    Flash: {
+      0x0000: 'Flash did not fire',
+      0x0001: 'Flash fired',
+      0x0005: 'Strobe return light not detected',
+      0x0007: 'Strobe return light detected',
+      0x0009: 'Flash fired, compulsory flash mode',
+      0x000D: 'Flash fired, compulsory flash mode, return light not detected',
+      0x000F: 'Flash fired, compulsory flash mode, return light detected',
+      0x0010: 'Flash did not fire, compulsory flash mode',
+      0x0018: 'Flash did not fire, auto mode',
+      0x0019: 'Flash fired, auto mode',
+      0x001D: 'Flash fired, auto mode, return light not detected',
+      0x001F: 'Flash fired, auto mode, return light detected',
+      0x0020: 'No flash function',
+      0x0041: 'Flash fired, red-eye reduction mode',
+      0x0045: 'Flash fired, red-eye reduction mode, return light not detected',
+      0x0047: 'Flash fired, red-eye reduction mode, return light detected',
+      0x0049: 'Flash fired, compulsory flash mode, red-eye reduction mode',
+      0x004D: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',
+      0x004F: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',
+      0x0059: 'Flash fired, auto mode, red-eye reduction mode',
+      0x005D: 'Flash fired, auto mode, return light not detected, red-eye reduction mode',
+      0x005F: 'Flash fired, auto mode, return light detected, red-eye reduction mode'
+    },
+    SensingMethod: {
+      1: 'Undefined',
+      2: 'One-chip color area sensor',
+      3: 'Two-chip color area sensor',
+      4: 'Three-chip color area sensor',
+      5: 'Color sequential area sensor',
+      7: 'Trilinear sensor',
+      8: 'Color sequential linear sensor'
+    },
+    SceneCaptureType: {
+      0: 'Standard',
+      1: 'Landscape',
+      2: 'Portrait',
+      3: 'Night scene'
+    },
+    SceneType: {
+      1: 'Directly photographed'
+    },
+    CustomRendered: {
+      0: 'Normal process',
+      1: 'Custom process'
+    },
+    WhiteBalance: {
+      0: 'Auto white balance',
+      1: 'Manual white balance'
+    },
+    GainControl: {
+      0: 'None',
+      1: 'Low gain up',
+      2: 'High gain up',
+      3: 'Low gain down',
+      4: 'High gain down'
+    },
+    Contrast: {
+      0: 'Normal',
+      1: 'Soft',
+      2: 'Hard'
+    },
+    Saturation: {
+      0: 'Normal',
+      1: 'Low saturation',
+      2: 'High saturation'
+    },
+    Sharpness: {
+      0: 'Normal',
+      1: 'Soft',
+      2: 'Hard'
+    },
+    SubjectDistanceRange: {
+      0: 'Unknown',
+      1: 'Macro',
+      2: 'Close view',
+      3: 'Distant view'
+    },
+    FileSource: {
+      3: 'DSC'
+    },
+    ComponentsConfiguration: {
+      0: '',
+      1: 'Y',
+      2: 'Cb',
+      3: 'Cr',
+      4: 'R',
+      5: 'G',
+      6: 'B'
+    },
+    Orientation: {
+      1: 'top-left',
+      2: 'top-right',
+      3: 'bottom-right',
+      4: 'bottom-left',
+      5: 'left-top',
+      6: 'right-top',
+      7: 'right-bottom',
+      8: 'left-bottom'
+    }
+  }
+
+  loadImage.ExifMap.prototype.getText = function (id) {
+    var value = this.get(id)
+    switch (id) {
+      case 'LightSource':
+      case 'Flash':
+      case 'MeteringMode':
+      case 'ExposureProgram':
+      case 'SensingMethod':
+      case 'SceneCaptureType':
+      case 'SceneType':
+      case 'CustomRendered':
+      case 'WhiteBalance':
+      case 'GainControl':
+      case 'Contrast':
+      case 'Saturation':
+      case 'Sharpness':
+      case 'SubjectDistanceRange':
+      case 'FileSource':
+      case 'Orientation':
+        return this.stringValues[id][value]
+      case 'ExifVersion':
+      case 'FlashpixVersion':
+        return String.fromCharCode(value[0], value[1], value[2], value[3])
+      case 'ComponentsConfiguration':
+        return this.stringValues[id][value[0]] +
+        this.stringValues[id][value[1]] +
+        this.stringValues[id][value[2]] +
+        this.stringValues[id][value[3]]
+      case 'GPSVersionID':
+        return value[0] + '.' + value[1] + '.' + value[2] + '.' + value[3]
+    }
+    return String(value)
+  }
+
+  ;(function (exifMapPrototype) {
+    var tags = exifMapPrototype.tags
+    var map = exifMapPrototype.map
+    var prop
+    // Map the tag names to tags:
+    for (prop in tags) {
+      if (tags.hasOwnProperty(prop)) {
+        map[tags[prop]] = prop
+      }
+    }
+  }(loadImage.ExifMap.prototype))
+
+  loadImage.ExifMap.prototype.getAll = function () {
+    var map = {}
+    var prop
+    var id
+    for (prop in this) {
+      if (this.hasOwnProperty(prop)) {
+        id = this.tags[prop]
+        if (id) {
+          map[id] = this.getText(id)
+        }
+      }
+    }
+    return map
+  }
+}))

+ 300 - 0
app/Resources/public/assets/blueimp-load-image/js/load-image-exif.js

@@ -0,0 +1,300 @@
+/*
+ * JavaScript Load Image Exif Parser
+ * https://github.com/blueimp/JavaScript-Load-Image
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global define, module, require, window, console */
+
+;(function (factory) {
+  'use strict'
+  if (typeof define === 'function' && define.amd) {
+    // Register as an anonymous AMD module:
+    define(['load-image', 'load-image-meta'], factory)
+  } else if (typeof module === 'object' && module.exports) {
+    factory(require('./load-image'), require('./load-image-meta'))
+  } else {
+    // Browser globals:
+    factory(window.loadImage)
+  }
+}(function (loadImage) {
+  'use strict'
+
+  loadImage.ExifMap = function () {
+    return this
+  }
+
+  loadImage.ExifMap.prototype.map = {
+    'Orientation': 0x0112
+  }
+
+  loadImage.ExifMap.prototype.get = function (id) {
+    return this[id] || this[this.map[id]]
+  }
+
+  loadImage.getExifThumbnail = function (dataView, offset, length) {
+    var hexData,
+      i,
+      b
+    if (!length || offset + length > dataView.byteLength) {
+      console.log('Invalid Exif data: Invalid thumbnail data.')
+      return
+    }
+    hexData = []
+    for (i = 0; i < length; i += 1) {
+      b = dataView.getUint8(offset + i)
+      hexData.push((b < 16 ? '0' : '') + b.toString(16))
+    }
+    return 'data:image/jpeg,%' + hexData.join('%')
+  }
+
+  loadImage.exifTagTypes = {
+    // byte, 8-bit unsigned int:
+    1: {
+      getValue: function (dataView, dataOffset) {
+        return dataView.getUint8(dataOffset)
+      },
+      size: 1
+    },
+    // ascii, 8-bit byte:
+    2: {
+      getValue: function (dataView, dataOffset) {
+        return String.fromCharCode(dataView.getUint8(dataOffset))
+      },
+      size: 1,
+      ascii: true
+    },
+    // short, 16 bit int:
+    3: {
+      getValue: function (dataView, dataOffset, littleEndian) {
+        return dataView.getUint16(dataOffset, littleEndian)
+      },
+      size: 2
+    },
+    // long, 32 bit int:
+    4: {
+      getValue: function (dataView, dataOffset, littleEndian) {
+        return dataView.getUint32(dataOffset, littleEndian)
+      },
+      size: 4
+    },
+    // rational = two long values, first is numerator, second is denominator:
+    5: {
+      getValue: function (dataView, dataOffset, littleEndian) {
+        return dataView.getUint32(dataOffset, littleEndian) /
+        dataView.getUint32(dataOffset + 4, littleEndian)
+      },
+      size: 8
+    },
+    // slong, 32 bit signed int:
+    9: {
+      getValue: function (dataView, dataOffset, littleEndian) {
+        return dataView.getInt32(dataOffset, littleEndian)
+      },
+      size: 4
+    },
+    // srational, two slongs, first is numerator, second is denominator:
+    10: {
+      getValue: function (dataView, dataOffset, littleEndian) {
+        return dataView.getInt32(dataOffset, littleEndian) /
+        dataView.getInt32(dataOffset + 4, littleEndian)
+      },
+      size: 8
+    }
+  }
+  // undefined, 8-bit byte, value depending on field:
+  loadImage.exifTagTypes[7] = loadImage.exifTagTypes[1]
+
+  loadImage.getExifValue = function (dataView, tiffOffset, offset, type, length, littleEndian) {
+    var tagType = loadImage.exifTagTypes[type]
+    var tagSize
+    var dataOffset
+    var values
+    var i
+    var str
+    var c
+    if (!tagType) {
+      console.log('Invalid Exif data: Invalid tag type.')
+      return
+    }
+    tagSize = tagType.size * length
+    // Determine if the value is contained in the dataOffset bytes,
+    // or if the value at the dataOffset is a pointer to the actual data:
+    dataOffset = tagSize > 4
+      ? tiffOffset + dataView.getUint32(offset + 8, littleEndian)
+      : (offset + 8)
+    if (dataOffset + tagSize > dataView.byteLength) {
+      console.log('Invalid Exif data: Invalid data offset.')
+      return
+    }
+    if (length === 1) {
+      return tagType.getValue(dataView, dataOffset, littleEndian)
+    }
+    values = []
+    for (i = 0; i < length; i += 1) {
+      values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian)
+    }
+    if (tagType.ascii) {
+      str = ''
+      // Concatenate the chars:
+      for (i = 0; i < values.length; i += 1) {
+        c = values[i]
+        // Ignore the terminating NULL byte(s):
+        if (c === '\u0000') {
+          break
+        }
+        str += c
+      }
+      return str
+    }
+    return values
+  }
+
+  loadImage.parseExifTag = function (dataView, tiffOffset, offset, littleEndian, data) {
+    var tag = dataView.getUint16(offset, littleEndian)
+    data.exif[tag] = loadImage.getExifValue(
+      dataView,
+      tiffOffset,
+      offset,
+      dataView.getUint16(offset + 2, littleEndian), // tag type
+      dataView.getUint32(offset + 4, littleEndian), // tag length
+      littleEndian
+    )
+  }
+
+  loadImage.parseExifTags = function (dataView, tiffOffset, dirOffset, littleEndian, data) {
+    var tagsNumber,
+      dirEndOffset,
+      i
+    if (dirOffset + 6 > dataView.byteLength) {
+      console.log('Invalid Exif data: Invalid directory offset.')
+      return
+    }
+    tagsNumber = dataView.getUint16(dirOffset, littleEndian)
+    dirEndOffset = dirOffset + 2 + 12 * tagsNumber
+    if (dirEndOffset + 4 > dataView.byteLength) {
+      console.log('Invalid Exif data: Invalid directory size.')
+      return
+    }
+    for (i = 0; i < tagsNumber; i += 1) {
+      this.parseExifTag(
+        dataView,
+        tiffOffset,
+        dirOffset + 2 + 12 * i, // tag offset
+        littleEndian,
+        data
+      )
+    }
+    // Return the offset to the next directory:
+    return dataView.getUint32(dirEndOffset, littleEndian)
+  }
+
+  loadImage.parseExifData = function (dataView, offset, length, data, options) {
+    if (options.disableExif) {
+      return
+    }
+    var tiffOffset = offset + 10
+    var littleEndian
+    var dirOffset
+    var thumbnailData
+    // Check for the ASCII code for "Exif" (0x45786966):
+    if (dataView.getUint32(offset + 4) !== 0x45786966) {
+      // No Exif data, might be XMP data instead
+      return
+    }
+    if (tiffOffset + 8 > dataView.byteLength) {
+      console.log('Invalid Exif data: Invalid segment size.')
+      return
+    }
+    // Check for the two null bytes:
+    if (dataView.getUint16(offset + 8) !== 0x0000) {
+      console.log('Invalid Exif data: Missing byte alignment offset.')
+      return
+    }
+    // Check the byte alignment:
+    switch (dataView.getUint16(tiffOffset)) {
+      case 0x4949:
+        littleEndian = true
+        break
+      case 0x4D4D:
+        littleEndian = false
+        break
+      default:
+        console.log('Invalid Exif data: Invalid byte alignment marker.')
+        return
+    }
+    // Check for the TIFF tag marker (0x002A):
+    if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
+      console.log('Invalid Exif data: Missing TIFF marker.')
+      return
+    }
+    // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
+    dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian)
+    // Create the exif object to store the tags:
+    data.exif = new loadImage.ExifMap()
+    // Parse the tags of the main image directory and retrieve the
+    // offset to the next directory, usually the thumbnail directory:
+    dirOffset = loadImage.parseExifTags(
+      dataView,
+      tiffOffset,
+      tiffOffset + dirOffset,
+      littleEndian,
+      data
+    )
+    if (dirOffset && !options.disableExifThumbnail) {
+      thumbnailData = {exif: {}}
+      dirOffset = loadImage.parseExifTags(
+        dataView,
+        tiffOffset,
+        tiffOffset + dirOffset,
+        littleEndian,
+        thumbnailData
+      )
+      // Check for JPEG Thumbnail offset:
+      if (thumbnailData.exif[0x0201]) {
+        data.exif.Thumbnail = loadImage.getExifThumbnail(
+          dataView,
+          tiffOffset + thumbnailData.exif[0x0201],
+          thumbnailData.exif[0x0202] // Thumbnail data length
+        )
+      }
+    }
+    // Check for Exif Sub IFD Pointer:
+    if (data.exif[0x8769] && !options.disableExifSub) {
+      loadImage.parseExifTags(
+        dataView,
+        tiffOffset,
+        tiffOffset + data.exif[0x8769], // directory offset
+        littleEndian,
+        data
+      )
+    }
+    // Check for GPS Info IFD Pointer:
+    if (data.exif[0x8825] && !options.disableExifGps) {
+      loadImage.parseExifTags(
+        dataView,
+        tiffOffset,
+        tiffOffset + data.exif[0x8825], // directory offset
+        littleEndian,
+        data
+      )
+    }
+  }
+
+  // Registers the Exif parser for the APP1 JPEG meta data segment:
+  loadImage.metaDataParsers.jpeg[0xffe1].push(loadImage.parseExifData)
+
+  // Adds the following properties to the parseMetaData callback data:
+  // * exif: The exif tags, parsed by the parseExifData method
+
+  // Adds the following options to the parseMetaData method:
+  // * disableExif: Disables Exif parsing.
+  // * disableExifThumbnail: Disables parsing of the Exif Thumbnail.
+  // * disableExifSub: Disables parsing of the Exif Sub IFD.
+  // * disableExifGps: Disables parsing of the Exif GPS Info IFD.
+}))

+ 143 - 0
app/Resources/public/assets/blueimp-load-image/js/load-image-meta.js

@@ -0,0 +1,143 @@
+/*
+ * JavaScript Load Image Meta
+ * https://github.com/blueimp/JavaScript-Load-Image
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Image meta data handling implementation
+ * based on the help and contribution of
+ * Achim Stöhr.
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global define, module, require, window, DataView, Blob, Uint8Array, console */
+
+;(function (factory) {
+  'use strict'
+  if (typeof define === 'function' && define.amd) {
+    // Register as an anonymous AMD module:
+    define(['load-image'], factory)
+  } else if (typeof module === 'object' && module.exports) {
+    factory(require('./load-image'))
+  } else {
+    // Browser globals:
+    factory(window.loadImage)
+  }
+}(function (loadImage) {
+  'use strict'
+
+  var hasblobSlice = window.Blob && (Blob.prototype.slice ||
+  Blob.prototype.webkitSlice || Blob.prototype.mozSlice)
+
+  loadImage.blobSlice = hasblobSlice && function () {
+    var slice = this.slice || this.webkitSlice || this.mozSlice
+    return slice.apply(this, arguments)
+  }
+
+  loadImage.metaDataParsers = {
+    jpeg: {
+      0xffe1: [] // APP1 marker
+    }
+  }
+
+  // Parses image meta data and calls the callback with an object argument
+  // with the following properties:
+  // * imageHead: The complete image head as ArrayBuffer (Uint8Array for IE10)
+  // The options arguments accepts an object and supports the following properties:
+  // * maxMetaDataSize: Defines the maximum number of bytes to parse.
+  // * disableImageHead: Disables creating the imageHead property.
+  loadImage.parseMetaData = function (file, callback, options) {
+    options = options || {}
+    var that = this
+    // 256 KiB should contain all EXIF/ICC/IPTC segments:
+    var maxMetaDataSize = options.maxMetaDataSize || 262144
+    var data = {}
+    var noMetaData = !(window.DataView && file && file.size >= 12 &&
+                      file.type === 'image/jpeg' && loadImage.blobSlice)
+    if (noMetaData || !loadImage.readFile(
+        loadImage.blobSlice.call(file, 0, maxMetaDataSize),
+        function (e) {
+          if (e.target.error) {
+            // FileReader error
+            console.log(e.target.error)
+            callback(data)
+            return
+          }
+          // Note on endianness:
+          // Since the marker and length bytes in JPEG files are always
+          // stored in big endian order, we can leave the endian parameter
+          // of the DataView methods undefined, defaulting to big endian.
+          var buffer = e.target.result
+          var dataView = new DataView(buffer)
+          var offset = 2
+          var maxOffset = dataView.byteLength - 4
+          var headLength = offset
+          var markerBytes
+          var markerLength
+          var parsers
+          var i
+          // Check for the JPEG marker (0xffd8):
+          if (dataView.getUint16(0) === 0xffd8) {
+            while (offset < maxOffset) {
+              markerBytes = dataView.getUint16(offset)
+              // Search for APPn (0xffeN) and COM (0xfffe) markers,
+              // which contain application-specific meta-data like
+              // Exif, ICC and IPTC data and text comments:
+              if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) ||
+                markerBytes === 0xfffe) {
+                // The marker bytes (2) are always followed by
+                // the length bytes (2), indicating the length of the
+                // marker segment, which includes the length bytes,
+                // but not the marker bytes, so we add 2:
+                markerLength = dataView.getUint16(offset + 2) + 2
+                if (offset + markerLength > dataView.byteLength) {
+                  console.log('Invalid meta data: Invalid segment size.')
+                  break
+                }
+                parsers = loadImage.metaDataParsers.jpeg[markerBytes]
+                if (parsers) {
+                  for (i = 0; i < parsers.length; i += 1) {
+                    parsers[i].call(
+                      that,
+                      dataView,
+                      offset,
+                      markerLength,
+                      data,
+                      options
+                    )
+                  }
+                }
+                offset += markerLength
+                headLength = offset
+              } else {
+                // Not an APPn or COM marker, probably safe to
+                // assume that this is the end of the meta data
+                break
+              }
+            }
+            // Meta length must be longer than JPEG marker (2)
+            // plus APPn marker (2), followed by length bytes (2):
+            if (!options.disableImageHead && headLength > 6) {
+              if (buffer.slice) {
+                data.imageHead = buffer.slice(0, headLength)
+              } else {
+                // Workaround for IE10, which does not yet
+                // support ArrayBuffer.slice:
+                data.imageHead = new Uint8Array(buffer)
+                  .subarray(0, headLength)
+              }
+            }
+          } else {
+            console.log('Invalid JPEG file: Missing JPEG marker.')
+          }
+          callback(data)
+        },
+        'readAsArrayBuffer'
+      )) {
+      callback(data)
+    }
+  }
+}))

+ 171 - 0
app/Resources/public/assets/blueimp-load-image/js/load-image-orientation.js

@@ -0,0 +1,171 @@
+/*
+ * JavaScript Load Image Orientation
+ * https://github.com/blueimp/JavaScript-Load-Image
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global define, module, require, window */
+
+;(function (factory) {
+  'use strict'
+  if (typeof define === 'function' && define.amd) {
+    // Register as an anonymous AMD module:
+    define(['load-image'], factory)
+  } else if (typeof module === 'object' && module.exports) {
+    factory(require('./load-image'))
+  } else {
+    // Browser globals:
+    factory(window.loadImage)
+  }
+}(function (loadImage) {
+  'use strict'
+
+  var originalHasCanvasOption = loadImage.hasCanvasOption
+  var originalTransformCoordinates = loadImage.transformCoordinates
+  var originalGetTransformedOptions = loadImage.getTransformedOptions
+
+  // This method is used to determine if the target image
+  // should be a canvas element:
+  loadImage.hasCanvasOption = function (options) {
+    return !!options.orientation ||
+      originalHasCanvasOption.call(loadImage, options)
+  }
+
+  // Transform image orientation based on
+  // the given EXIF orientation option:
+  loadImage.transformCoordinates = function (canvas, options) {
+    originalTransformCoordinates.call(loadImage, canvas, options)
+    var ctx = canvas.getContext('2d')
+    var width = canvas.width
+    var height = canvas.height
+    var styleWidth = canvas.style.width
+    var styleHeight = canvas.style.height
+    var orientation = options.orientation
+    if (!orientation || orientation > 8) {
+      return
+    }
+    if (orientation > 4) {
+      canvas.width = height
+      canvas.height = width
+      canvas.style.width = styleHeight
+      canvas.style.height = styleWidth
+    }
+    switch (orientation) {
+      case 2:
+        // horizontal flip
+        ctx.translate(width, 0)
+        ctx.scale(-1, 1)
+        break
+      case 3:
+        // 180° rotate left
+        ctx.translate(width, height)
+        ctx.rotate(Math.PI)
+        break
+      case 4:
+        // vertical flip
+        ctx.translate(0, height)
+        ctx.scale(1, -1)
+        break
+      case 5:
+        // vertical flip + 90 rotate right
+        ctx.rotate(0.5 * Math.PI)
+        ctx.scale(1, -1)
+        break
+      case 6:
+        // 90° rotate right
+        ctx.rotate(0.5 * Math.PI)
+        ctx.translate(0, -height)
+        break
+      case 7:
+        // horizontal flip + 90 rotate right
+        ctx.rotate(0.5 * Math.PI)
+        ctx.translate(width, -height)
+        ctx.scale(-1, 1)
+        break
+      case 8:
+        // 90° rotate left
+        ctx.rotate(-0.5 * Math.PI)
+        ctx.translate(-width, 0)
+        break
+    }
+  }
+
+  // Transforms coordinate and dimension options
+  // based on the given orientation option:
+  loadImage.getTransformedOptions = function (img, opts) {
+    var options = originalGetTransformedOptions.call(loadImage, img, opts)
+    var orientation = options.orientation
+    var newOptions
+    var i
+    if (!orientation || orientation > 8 || orientation === 1) {
+      return options
+    }
+    newOptions = {}
+    for (i in options) {
+      if (options.hasOwnProperty(i)) {
+        newOptions[i] = options[i]
+      }
+    }
+    switch (options.orientation) {
+      case 2:
+        // horizontal flip
+        newOptions.left = options.right
+        newOptions.right = options.left
+        break
+      case 3:
+        // 180° rotate left
+        newOptions.left = options.right
+        newOptions.top = options.bottom
+        newOptions.right = options.left
+        newOptions.bottom = options.top
+        break
+      case 4:
+        // vertical flip
+        newOptions.top = options.bottom
+        newOptions.bottom = options.top
+        break
+      case 5:
+        // vertical flip + 90 rotate right
+        newOptions.left = options.top
+        newOptions.top = options.left
+        newOptions.right = options.bottom
+        newOptions.bottom = options.right
+        break
+      case 6:
+        // 90° rotate right
+        newOptions.left = options.top
+        newOptions.top = options.right
+        newOptions.right = options.bottom
+        newOptions.bottom = options.left
+        break
+      case 7:
+        // horizontal flip + 90 rotate right
+        newOptions.left = options.bottom
+        newOptions.top = options.right
+        newOptions.right = options.top
+        newOptions.bottom = options.left
+        break
+      case 8:
+        // 90° rotate left
+        newOptions.left = options.bottom
+        newOptions.top = options.left
+        newOptions.right = options.top
+        newOptions.bottom = options.right
+        break
+    }
+    if (options.orientation > 4) {
+      newOptions.maxWidth = options.maxHeight
+      newOptions.maxHeight = options.maxWidth
+      newOptions.minWidth = options.minHeight
+      newOptions.minHeight = options.minWidth
+      newOptions.sourceWidth = options.sourceHeight
+      newOptions.sourceHeight = options.sourceWidth
+    }
+    return newOptions
+  }
+}))

File diff ditekan karena terlalu besar
+ 0 - 0
app/Resources/public/assets/blueimp-load-image/js/load-image.all.min.js


File diff ditekan karena terlalu besar
+ 0 - 0
app/Resources/public/assets/blueimp-load-image/js/load-image.all.min.js.map


+ 348 - 0
app/Resources/public/assets/blueimp-load-image/js/load-image.js

@@ -0,0 +1,348 @@
+/*
+ * JavaScript Load Image
+ * https://github.com/blueimp/JavaScript-Load-Image
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global define, module, window, document, URL, webkitURL, FileReader */
+
+;(function ($) {
+  'use strict'
+
+  // Loads an image for a given File object.
+  // Invokes the callback with an img or optional canvas
+  // element (if supported by the browser) as parameter:
+  var loadImage = function (file, callback, options) {
+    var img = document.createElement('img')
+    var url
+    var oUrl
+    img.onerror = callback
+    img.onload = function () {
+      if (oUrl && !(options && options.noRevoke)) {
+        loadImage.revokeObjectURL(oUrl)
+      }
+      if (callback) {
+        callback(loadImage.scale(img, options))
+      }
+    }
+    if (loadImage.isInstanceOf('Blob', file) ||
+      // Files are also Blob instances, but some browsers
+      // (Firefox 3.6) support the File API but not Blobs:
+      loadImage.isInstanceOf('File', file)) {
+      url = oUrl = loadImage.createObjectURL(file)
+      // Store the file type for resize processing:
+      img._type = file.type
+    } else if (typeof file === 'string') {
+      url = file
+      if (options && options.crossOrigin) {
+        img.crossOrigin = options.crossOrigin
+      }
+    } else {
+      return false
+    }
+    if (url) {
+      img.src = url
+      return img
+    }
+    return loadImage.readFile(file, function (e) {
+      var target = e.target
+      if (target && target.result) {
+        img.src = target.result
+      } else {
+        if (callback) {
+          callback(e)
+        }
+      }
+    })
+  }
+  // The check for URL.revokeObjectURL fixes an issue with Opera 12,
+  // which provides URL.createObjectURL but doesn't properly implement it:
+  var urlAPI = (window.createObjectURL && window) ||
+                (window.URL && URL.revokeObjectURL && URL) ||
+                (window.webkitURL && webkitURL)
+
+  loadImage.isInstanceOf = function (type, obj) {
+    // Cross-frame instanceof check
+    return Object.prototype.toString.call(obj) === '[object ' + type + ']'
+  }
+
+  // Transform image coordinates, allows to override e.g.
+  // the canvas orientation based on the orientation option,
+  // gets canvas, options passed as arguments:
+  loadImage.transformCoordinates = function () {
+    return
+  }
+
+  // Returns transformed options, allows to override e.g.
+  // maxWidth, maxHeight and crop options based on the aspectRatio.
+  // gets img, options passed as arguments:
+  loadImage.getTransformedOptions = function (img, options) {
+    var aspectRatio = options.aspectRatio
+    var newOptions
+    var i
+    var width
+    var height
+    if (!aspectRatio) {
+      return options
+    }
+    newOptions = {}
+    for (i in options) {
+      if (options.hasOwnProperty(i)) {
+        newOptions[i] = options[i]
+      }
+    }
+    newOptions.crop = true
+    width = img.naturalWidth || img.width
+    height = img.naturalHeight || img.height
+    if (width / height > aspectRatio) {
+      newOptions.maxWidth = height * aspectRatio
+      newOptions.maxHeight = height
+    } else {
+      newOptions.maxWidth = width
+      newOptions.maxHeight = width / aspectRatio
+    }
+    return newOptions
+  }
+
+  // Canvas render method, allows to implement a different rendering algorithm:
+  loadImage.renderImageToCanvas = function (
+    canvas,
+    img,
+    sourceX,
+    sourceY,
+    sourceWidth,
+    sourceHeight,
+    destX,
+    destY,
+    destWidth,
+    destHeight
+  ) {
+    canvas.getContext('2d').drawImage(
+      img,
+      sourceX,
+      sourceY,
+      sourceWidth,
+      sourceHeight,
+      destX,
+      destY,
+      destWidth,
+      destHeight
+    )
+    return canvas
+  }
+
+  // This method is used to determine if the target image
+  // should be a canvas element:
+  loadImage.hasCanvasOption = function (options) {
+    return options.canvas || options.crop || !!options.aspectRatio
+  }
+
+  // Scales and/or crops the given image (img or canvas HTML element)
+  // using the given options.
+  // Returns a canvas object if the browser supports canvas
+  // and the hasCanvasOption method returns true or a canvas
+  // object is passed as image, else the scaled image:
+  loadImage.scale = function (img, options) {
+    options = options || {}
+    var canvas = document.createElement('canvas')
+    var useCanvas = img.getContext ||
+                    (loadImage.hasCanvasOption(options) && canvas.getContext)
+    var width = img.naturalWidth || img.width
+    var height = img.naturalHeight || img.height
+    var destWidth = width
+    var destHeight = height
+    var maxWidth
+    var maxHeight
+    var minWidth
+    var minHeight
+    var sourceWidth
+    var sourceHeight
+    var sourceX
+    var sourceY
+    var pixelRatio
+    var downsamplingRatio
+    var tmp
+    function scaleUp () {
+      var scale = Math.max(
+        (minWidth || destWidth) / destWidth,
+        (minHeight || destHeight) / destHeight
+      )
+      if (scale > 1) {
+        destWidth *= scale
+        destHeight *= scale
+      }
+    }
+    function scaleDown () {
+      var scale = Math.min(
+        (maxWidth || destWidth) / destWidth,
+        (maxHeight || destHeight) / destHeight
+      )
+      if (scale < 1) {
+        destWidth *= scale
+        destHeight *= scale
+      }
+    }
+    if (useCanvas) {
+      options = loadImage.getTransformedOptions(img, options)
+      sourceX = options.left || 0
+      sourceY = options.top || 0
+      if (options.sourceWidth) {
+        sourceWidth = options.sourceWidth
+        if (options.right !== undefined && options.left === undefined) {
+          sourceX = width - sourceWidth - options.right
+        }
+      } else {
+        sourceWidth = width - sourceX - (options.right || 0)
+      }
+      if (options.sourceHeight) {
+        sourceHeight = options.sourceHeight
+        if (options.bottom !== undefined && options.top === undefined) {
+          sourceY = height - sourceHeight - options.bottom
+        }
+      } else {
+        sourceHeight = height - sourceY - (options.bottom || 0)
+      }
+      destWidth = sourceWidth
+      destHeight = sourceHeight
+    }
+    maxWidth = options.maxWidth
+    maxHeight = options.maxHeight
+    minWidth = options.minWidth
+    minHeight = options.minHeight
+    if (useCanvas && maxWidth && maxHeight && options.crop) {
+      destWidth = maxWidth
+      destHeight = maxHeight
+      tmp = sourceWidth / sourceHeight - maxWidth / maxHeight
+      if (tmp < 0) {
+        sourceHeight = maxHeight * sourceWidth / maxWidth
+        if (options.top === undefined && options.bottom === undefined) {
+          sourceY = (height - sourceHeight) / 2
+        }
+      } else if (tmp > 0) {
+        sourceWidth = maxWidth * sourceHeight / maxHeight
+        if (options.left === undefined && options.right === undefined) {
+          sourceX = (width - sourceWidth) / 2
+        }
+      }
+    } else {
+      if (options.contain || options.cover) {
+        minWidth = maxWidth = maxWidth || minWidth
+        minHeight = maxHeight = maxHeight || minHeight
+      }
+      if (options.cover) {
+        scaleDown()
+        scaleUp()
+      } else {
+        scaleUp()
+        scaleDown()
+      }
+    }
+    if (useCanvas) {
+      pixelRatio = options.pixelRatio
+      if (pixelRatio > 1) {
+        canvas.style.width = destWidth + 'px'
+        canvas.style.height = destHeight + 'px'
+        destWidth *= pixelRatio
+        destHeight *= pixelRatio
+        canvas.getContext('2d').scale(pixelRatio, pixelRatio)
+      }
+      downsamplingRatio = options.downsamplingRatio
+      if (downsamplingRatio > 0 && downsamplingRatio < 1 &&
+            destWidth < sourceWidth && destHeight < sourceHeight) {
+        while (sourceWidth * downsamplingRatio > destWidth) {
+          canvas.width = sourceWidth * downsamplingRatio
+          canvas.height = sourceHeight * downsamplingRatio
+          loadImage.renderImageToCanvas(
+            canvas,
+            img,
+            sourceX,
+            sourceY,
+            sourceWidth,
+            sourceHeight,
+            0,
+            0,
+            canvas.width,
+            canvas.height
+          )
+          sourceWidth = canvas.width
+          sourceHeight = canvas.height
+          img = document.createElement('canvas')
+          img.width = sourceWidth
+          img.height = sourceHeight
+          loadImage.renderImageToCanvas(
+            img,
+            canvas,
+            0,
+            0,
+            sourceWidth,
+            sourceHeight,
+            0,
+            0,
+            sourceWidth,
+            sourceHeight
+          )
+        }
+      }
+      canvas.width = destWidth
+      canvas.height = destHeight
+      loadImage.transformCoordinates(
+        canvas,
+        options
+      )
+      return loadImage.renderImageToCanvas(
+        canvas,
+        img,
+        sourceX,
+        sourceY,
+        sourceWidth,
+        sourceHeight,
+        0,
+        0,
+        destWidth,
+        destHeight
+      )
+    }
+    img.width = destWidth
+    img.height = destHeight
+    return img
+  }
+
+  loadImage.createObjectURL = function (file) {
+    return urlAPI ? urlAPI.createObjectURL(file) : false
+  }
+
+  loadImage.revokeObjectURL = function (url) {
+    return urlAPI ? urlAPI.revokeObjectURL(url) : false
+  }
+
+  // Loads a given File object via FileReader interface,
+  // invokes the callback with the event object (load or error).
+  // The result can be read via event.target.result:
+  loadImage.readFile = function (file, callback, method) {
+    if (window.FileReader) {
+      var fileReader = new FileReader()
+      fileReader.onload = fileReader.onerror = callback
+      method = method || 'readAsDataURL'
+      if (fileReader[method]) {
+        fileReader[method](file)
+        return fileReader
+      }
+    }
+    return false
+  }
+
+  if (typeof define === 'function' && define.amd) {
+    define(function () {
+      return loadImage
+    })
+  } else if (typeof module === 'object' && module.exports) {
+    module.exports = loadImage
+  } else {
+    $.loadImage = loadImage
+  }
+}(window))

+ 45 - 0
app/Resources/public/assets/blueimp-load-image/package.json

@@ -0,0 +1,45 @@
+{
+  "name": "blueimp-load-image",
+  "version": "2.6.0",
+  "title": "JavaScript Load Image",
+  "description": "JavaScript Load Image is a library to load images provided as File or Blob objects or via URL. It returns an optionally scaled and/or cropped HTML img or canvas element. It also provides a method to parse image meta data to extract Exif tags and thumbnails and to restore the complete image header after resizing.",
+  "keywords": [
+    "javascript",
+    "load",
+    "loading",
+    "image",
+    "file",
+    "blob",
+    "url",
+    "scale",
+    "crop",
+    "img",
+    "canvas",
+    "meta",
+    "exif",
+    "thumbnail",
+    "resizing"
+  ],
+  "homepage": "https://github.com/blueimp/JavaScript-Load-Image",
+  "author": {
+    "name": "Sebastian Tschan",
+    "url": "https://blueimp.net"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/blueimp/JavaScript-Load-Image.git"
+  },
+  "devDependencies": {
+    "mocha-phantomjs": "4.0.1",
+    "standard": "6.0.7",
+    "uglify-js": "2.6.1"
+  },
+  "scripts": {
+    "test": "standard js/*.js test/*.js && mocha-phantomjs test/index.html",
+    "build": "cd js && uglifyjs load-image.js load-image-orientation.js load-image-meta.js load-image-exif.js load-image-exif-map.js -c -m -o load-image.all.min.js --source-map load-image.all.min.js.map",
+    "preversion": "npm test",
+    "version": "npm run build && git add -A js",
+    "postversion": "git push --tags origin master master:gh-pages && npm publish"
+  },
+  "license": "MIT"
+}

+ 44 - 0
app/Resources/public/assets/blueimp-load-image/test/index.html

@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<!--
+/*
+ * JavaScript Load Image Test
+ * https://github.com/blueimp/JavaScript-Load-Image
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+-->
+<html lang="en">
+<head>
+<!--[if IE]>
+<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+<![endif]-->
+<meta charset="utf-8">
+<title>JavaScript Load Image Test</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link rel="stylesheet" href="vendor/mocha.css">
+</head>
+<body>
+<div id="mocha"></div>
+<script src="vendor/mocha.js"></script>
+<script src="vendor/expect.js"></script>
+<script>
+window.initMochaPhantomJS && initMochaPhantomJS();
+mocha.setup('bdd');
+</script>
+<script src="vendor/canvas-to-blob.js"></script>
+<script src="../js/load-image.js"></script>
+<script src="../js/load-image-orientation.js"></script>
+<script src="../js/load-image-meta.js"></script>
+<script src="../js/load-image-exif.js"></script>
+<script src="../js/load-image-exif-map.js"></script>
+<script src="test.js"></script>
+<script>
+mocha.checkLeaks();
+mocha.run();
+</script>
+</body>
+</html>

+ 510 - 0
app/Resources/public/assets/blueimp-load-image/test/test.js

@@ -0,0 +1,510 @@
+/*
+ * JavaScript Load Image Test
+ * https://github.com/blueimp/JavaScript-Load-Image
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global window, describe, it, Blob */
+
+;(function (expect, loadImage) {
+  'use strict'
+
+  var canCreateBlob = !!window.dataURLtoBlob
+  // 80x60px GIF image (color black, base64 data):
+  var b64DataGIF = 'R0lGODdhUAA8AIABAAAAAP///ywAAAAAUAA8AAACS4SPqcvtD6' +
+                    'OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofE' +
+                    'ovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5PKsAAA7'
+  var imageUrlGIF = 'data:image/gif;base64,' + b64DataGIF
+  var blobGIF = canCreateBlob && window.dataURLtoBlob(imageUrlGIF)
+  // 1x2px JPEG (color white, with the Exif orientation flag set to 6):
+  var b64DataJPEG = '/9j/4AAQSkZJRgABAQEAYABgAAD/4QAiRXhpZgAASUkqAAgAAA' +
+                    'ABABIBAwABAAAABgASAAAAAAD/2wBDAAEBAQEBAQEBAQEBAQEB' +
+                    'AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ' +
+                    'EBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEB' +
+                    'AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ' +
+                    'EBAQEBAQH/wAARCAABAAIDASIAAhEBAxEB/8QAHwAAAQUBAQEB' +
+                    'AQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBA' +
+                    'QAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAk' +
+                    'M2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1' +
+                    'hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKj' +
+                    'pKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+' +
+                    'Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAA' +
+                    'AAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAx' +
+                    'EEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl' +
+                    '8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2' +
+                    'hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq' +
+                    'srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8v' +
+                    'P09fb3+Pn6/9oADAMBAAIRAxEAPwD+/iiiigD/2Q=='
+  var imageUrlJPEG = 'data:image/jpeg;base64,' + b64DataJPEG
+  var blobJPEG = canCreateBlob && window.dataURLtoBlob(imageUrlJPEG)
+  function createBlob (data, type) {
+    try {
+      return new Blob([data], {type: type})
+    } catch (e) {
+      var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
+      window.MozBlobBuilder || window.MSBlobBuilder
+      var builder = new BlobBuilder()
+      builder.append(data.buffer || data)
+      return builder.getBlob(type)
+    }
+  }
+
+  describe('Loading', function () {
+    it('Return the img element or FileReader object to allow aborting the image load', function () {
+      var img = loadImage(blobGIF, function () {
+        return
+      })
+      expect(img).to.be.an(Object)
+      expect(img.onload).to.be.a('function')
+      expect(img.onerror).to.be.a('function')
+    })
+
+    it('Load image url', function (done) {
+      expect(loadImage(imageUrlGIF, function (img) {
+        expect(img.width).to.be(80)
+        expect(img.height).to.be(60)
+        done()
+      })).to.be.ok()
+    })
+
+    it('Load image blob', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(80)
+        expect(img.height).to.be(60)
+        done()
+      })).to.be.ok()
+    })
+
+    it('Return image loading error to callback', function (done) {
+      expect(loadImage('404', function (img) {
+        expect(img).to.be.a(window.Event)
+        expect(img.type).to.be('error')
+        done()
+      })).to.be.ok()
+    })
+
+    it('Keep object URL if noRevoke is true', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        loadImage(img.src, function (img2) {
+          expect(img.width).to.be(img2.width)
+          expect(img.height).to.be(img2.height)
+          done()
+        })
+      }, {noRevoke: true})).to.be.ok()
+    })
+
+    it('Discard object URL if noRevoke is undefined or false', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        loadImage(img.src, function (img2) {
+          if (!window.callPhantom) {
+            // revokeObjectUrl doesn't seem to have an effect in PhantomJS
+            expect(img2).to.be.a(window.Event)
+            expect(img2.type).to.be('error')
+          }
+          done()
+        })
+      })).to.be.ok()
+    })
+  })
+
+  describe('Scaling', function () {
+    describe('max/min', function () {
+      it('Scale to maxWidth', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(40)
+          expect(img.height).to.be(30)
+          done()
+        }, {maxWidth: 40})).to.be.ok()
+      })
+
+      it('Scale to maxHeight', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(20)
+          expect(img.height).to.be(15)
+          done()
+        }, {maxHeight: 15})).to.be.ok()
+      })
+
+      it('Scale to minWidth', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(160)
+          expect(img.height).to.be(120)
+          done()
+        }, {minWidth: 160})).to.be.ok()
+      })
+
+      it('Scale to minHeight', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(320)
+          expect(img.height).to.be(240)
+          done()
+        }, {minHeight: 240})).to.be.ok()
+      })
+
+      it('Scale to minWidth but respect maxWidth', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(160)
+          expect(img.height).to.be(120)
+          done()
+        }, {minWidth: 240, maxWidth: 160})).to.be.ok()
+      })
+
+      it('Scale to minHeight but respect maxHeight', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(160)
+          expect(img.height).to.be(120)
+          done()
+        }, {minHeight: 180, maxHeight: 120})).to.be.ok()
+      })
+
+      it('Scale to minWidth but respect maxHeight', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(160)
+          expect(img.height).to.be(120)
+          done()
+        }, {minWidth: 240, maxHeight: 120})).to.be.ok()
+      })
+
+      it('Scale to minHeight but respect maxWidth', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(160)
+          expect(img.height).to.be(120)
+          done()
+        }, {minHeight: 180, maxWidth: 160})).to.be.ok()
+      })
+
+      it('Scale up with the given pixelRatio', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(320)
+          expect(img.height).to.be(240)
+          expect(img.style.width).to.be('160px')
+          expect(img.style.height).to.be('120px')
+          done()
+        }, {minWidth: 160, canvas: true, pixelRatio: 2})).to.be.ok()
+      })
+
+      it('Scale down with the given pixelRatio', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(80)
+          expect(img.height).to.be(60)
+          expect(img.style.width).to.be('40px')
+          expect(img.style.height).to.be('30px')
+          done()
+        }, {maxWidth: 40, canvas: true, pixelRatio: 2})).to.be.ok()
+      })
+
+      it('Scale down with the given downsamplingRatio', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(20)
+          expect(img.height).to.be(15)
+          done()
+        }, {maxWidth: 20, canvas: true, downsamplingRatio: 0.5})).to.be.ok()
+      })
+
+      it('Ignore max settings if image dimensions are smaller', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(80)
+          expect(img.height).to.be(60)
+          done()
+        }, {maxWidth: 160, maxHeight: 120})).to.be.ok()
+      })
+
+      it('Ignore min settings if image dimensions are larger', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(80)
+          expect(img.height).to.be(60)
+          done()
+        }, {minWidth: 40, minHeight: 30})).to.be.ok()
+      })
+    })
+
+    describe('contain', function () {
+      it('Scale up to contain image in max dimensions', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(160)
+          expect(img.height).to.be(120)
+          done()
+        }, {maxWidth: 160, maxHeight: 160, contain: true})).to.be.ok()
+      })
+
+      it('Scale down to contain image in max dimensions', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(40)
+          expect(img.height).to.be(30)
+          done()
+        }, {maxWidth: 40, maxHeight: 40, contain: true})).to.be.ok()
+      })
+    })
+
+    describe('cover', function () {
+      it('Scale up to cover max dimensions with image dimensions', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(160)
+          expect(img.height).to.be(120)
+          done()
+        }, {maxWidth: 120, maxHeight: 120, cover: true})).to.be.ok()
+      })
+
+      it('Scale down to cover max dimensions with image dimensions', function (done) {
+        expect(loadImage(blobGIF, function (img) {
+          expect(img.width).to.be(40)
+          expect(img.height).to.be(30)
+          done()
+        }, {maxWidth: 30, maxHeight: 30, cover: true})).to.be.ok()
+      })
+    })
+  })
+
+  describe('Cropping', function () {
+    it('Crop to same values for maxWidth and maxHeight', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(40)
+        expect(img.height).to.be(40)
+        done()
+      }, {maxWidth: 40, maxHeight: 40, crop: true})).to.be.ok()
+    })
+
+    it('Crop to different values for maxWidth and maxHeight', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(40)
+        expect(img.height).to.be(60)
+        done()
+      }, {maxWidth: 40, maxHeight: 60, crop: true})).to.be.ok()
+    })
+
+    it('Crop using the given sourceWidth and sourceHeight dimensions', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(40)
+        expect(img.height).to.be(40)
+        done()
+      }, {sourceWidth: 40, sourceHeight: 40, crop: true})).to.be.ok()
+    })
+
+    it('Crop using the given left and top coordinates', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(40)
+        expect(img.height).to.be(20)
+        done()
+      }, {left: 40, top: 40, crop: true})).to.be.ok()
+    })
+
+    it('Crop using the given right and bottom coordinates', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(40)
+        expect(img.height).to.be(20)
+        done()
+      }, {right: 40, bottom: 40, crop: true})).to.be.ok()
+    })
+
+    it('Crop using the given 2:1 aspectRatio', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(80)
+        expect(img.height).to.be(40)
+        done()
+      }, {aspectRatio: 2})).to.be.ok()
+    })
+
+    it('Crop using the given 2:3 aspectRatio', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(40)
+        expect(img.height).to.be(60)
+        done()
+      }, {aspectRatio: 2 / 3})).to.be.ok()
+    })
+
+    it('Crop using maxWidth/maxHeight with the given pixelRatio', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(80)
+        expect(img.height).to.be(80)
+        expect(img.style.width).to.be('40px')
+        expect(img.style.height).to.be('40px')
+        done()
+      }, {maxWidth: 40, maxHeight: 40, crop: true, pixelRatio: 2})).to.be.ok()
+    })
+
+    it('Crop using sourceWidth/sourceHeight with the given pixelRatio', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(80)
+        expect(img.height).to.be(80)
+        expect(img.style.width).to.be('40px')
+        expect(img.style.height).to.be('40px')
+        done()
+      }, {sourceWidth: 40, sourceHeight: 40, crop: true, pixelRatio: 2})).to.be.ok()
+    })
+  })
+
+  describe('Orientation', function () {
+    it('Should keep the orientation', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(80)
+        expect(img.height).to.be(60)
+        done()
+      }, {orientation: 1})).to.be.ok()
+    })
+
+    it('Should rotate left', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(60)
+        expect(img.height).to.be(80)
+        done()
+      }, {orientation: 8})).to.be.ok()
+    })
+
+    it('Should rotate right', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(60)
+        expect(img.height).to.be(80)
+        done()
+      }, {orientation: 6})).to.be.ok()
+    })
+
+    it('Should adjust constraints to new coordinates', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(60)
+        expect(img.height).to.be(80)
+        done()
+      }, {orientation: 6, maxWidth: 60, maxHeight: 80})).to.be.ok()
+    })
+
+    it('Should adjust left and top to new coordinates', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(30)
+        expect(img.height).to.be(60)
+        done()
+      }, {orientation: 5, left: 30, top: 20})).to.be.ok()
+    })
+
+    it('Should adjust right and bottom to new coordinates', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(30)
+        expect(img.height).to.be(60)
+        done()
+      }, {orientation: 5, right: 30, bottom: 20})).to.be.ok()
+    })
+
+    it('Should adjust left and bottom to new coordinates', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(30)
+        expect(img.height).to.be(60)
+        done()
+      }, {orientation: 7, left: 30, bottom: 20})).to.be.ok()
+    })
+
+    it('Should adjust right and top to new coordinates', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(30)
+        expect(img.height).to.be(60)
+        done()
+      }, {orientation: 7, right: 30, top: 20})).to.be.ok()
+    })
+
+    it('Should rotate left with the given pixelRatio', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(120)
+        expect(img.height).to.be(160)
+        expect(img.style.width).to.be('60px')
+        expect(img.style.height).to.be('80px')
+        done()
+      }, {orientation: 8, pixelRatio: 2})).to.be.ok()
+    })
+
+    it('Should rotate right with the given pixelRatio', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(120)
+        expect(img.height).to.be(160)
+        expect(img.style.width).to.be('60px')
+        expect(img.style.height).to.be('80px')
+        done()
+      }, {orientation: 6, pixelRatio: 2})).to.be.ok()
+    })
+
+    it('Should ignore too small orientation value', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(80)
+        expect(img.height).to.be(60)
+        done()
+      }, {orientation: -1})).to.be.ok()
+    })
+
+    it('Should ignore too large orientation value', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.width).to.be(80)
+        expect(img.height).to.be(60)
+        done()
+      }, {orientation: 9})).to.be.ok()
+    })
+  })
+
+  describe('Canvas', function () {
+    it('Return img element to callback if canvas is not true', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.getContext).to.not.be.ok()
+        expect(img.nodeName.toLowerCase()).to.be('img')
+        done()
+      })).to.be.ok()
+    })
+
+    it('Return canvas element to callback if canvas is true', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.getContext).to.be.ok()
+        expect(img.nodeName.toLowerCase()).to.be('canvas')
+        done()
+      }, {canvas: true})).to.be.ok()
+    })
+
+    it('Return scaled canvas element to callback', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        expect(img.getContext).to.be.ok()
+        expect(img.nodeName.toLowerCase()).to.be('canvas')
+        expect(img.width).to.be(40)
+        expect(img.height).to.be(30)
+        done()
+      }, {canvas: true, maxWidth: 40})).to.be.ok()
+    })
+
+    it('Accept a canvas element as parameter for loadImage.scale', function (done) {
+      expect(loadImage(blobGIF, function (img) {
+        img = loadImage.scale(img, {
+          maxWidth: 40
+        })
+        expect(img.getContext).to.be.ok()
+        expect(img.nodeName.toLowerCase()).to.be('canvas')
+        expect(img.width).to.be(40)
+        expect(img.height).to.be(30)
+        done()
+      }, {canvas: true})).to.be.ok()
+    })
+  })
+
+  describe('Metadata', function () {
+    it('Should parse Exif information', function (done) {
+      loadImage.parseMetaData(blobJPEG, function (data) {
+        expect(data.exif).to.be.ok()
+        expect(data.exif.get('Orientation')).to.be(6)
+        done()
+      })
+    })
+
+    it('Should parse the complete image head', function (done) {
+      loadImage.parseMetaData(blobJPEG, function (data) {
+        expect(data.imageHead).to.be.ok()
+        loadImage.parseMetaData(
+          createBlob(data.imageHead, 'image/jpeg'),
+          function (data) {
+            expect(data.exif).to.be.ok()
+            expect(data.exif.get('Orientation')).to.be(6)
+            done()
+          }
+        )
+      })
+    })
+  })
+}(
+  this.expect,
+  this.loadImage
+))

+ 14 - 0
app/Resources/public/assets/blueimp-tmpl/.bower.json

@@ -0,0 +1,14 @@
+{
+  "name": "blueimp-tmpl",
+  "homepage": "https://github.com/blueimp/JavaScript-Templates",
+  "version": "3.3.0",
+  "_release": "3.3.0",
+  "_resolution": {
+    "type": "version",
+    "tag": "v3.3.0",
+    "commit": "f09f47c368e0dc7871be35119ef3eab9bb25bdf2"
+  },
+  "_source": "git://github.com/blueimp/JavaScript-Templates.git",
+  "_target": ">=2.5.4",
+  "_originalSource": "blueimp-tmpl"
+}

+ 2 - 0
app/Resources/public/assets/blueimp-tmpl/.gitignore

@@ -0,0 +1,2 @@
+.DS_Store
+node_modules

+ 3 - 0
app/Resources/public/assets/blueimp-tmpl/.travis.yml

@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - "stable"

+ 402 - 0
app/Resources/public/assets/blueimp-tmpl/README.md

@@ -0,0 +1,402 @@
+# JavaScript Templates
+
+## Demo
+[JavaScript Templates Demo](https://blueimp.github.io/JavaScript-Templates/)
+
+## Description
+1KB lightweight, fast & powerful JavaScript templating engine with zero
+dependencies. Compatible with server-side environments like Node.js, module
+loaders like RequireJS, Browserify or webpack and all web browsers.
+
+## Usage
+
+### Client-side
+Include the (minified) JavaScript Templates script in your HTML markup:
+
+```html
+<script src="js/tmpl.min.js"></script>
+```
+
+Add a script section with type **"text/x-tmpl"**, a unique **id** property and
+your template definition as content:
+
+```html
+<script type="text/x-tmpl" id="tmpl-demo">
+<h3>{%=o.title%}</h3>
+<p>Released under the
+<a href="{%=o.license.url%}">{%=o.license.name%}</a>.</p>
+<h4>Features</h4>
+<ul>
+{% for (var i=0; i<o.features.length; i++) { %}
+    <li>{%=o.features[i]%}</li>
+{% } %}
+</ul>
+</script>
+```
+
+**"o"** (the lowercase letter) is a reference to the data parameter of the
+template function (see the API section on how to modify this identifier).
+
+In your application code, create a JavaScript object to use as data for the
+template:
+
+```js
+var data = {
+    "title": "JavaScript Templates",
+    "license": {
+        "name": "MIT license",
+        "url": "http://www.opensource.org/licenses/MIT"
+    },
+    "features": [
+        "lightweight & fast",
+        "powerful",
+        "zero dependencies"
+    ]
+};
+```
+
+In a real application, this data could be the result of retrieving a
+[JSON](http://json.org/) resource.
+
+Render the result by calling the **tmpl()** method with the id of the template
+and the data object as arguments:
+
+```js
+document.getElementById("result").innerHTML = tmpl("tmpl-demo", data);
+```
+
+### Server-side
+
+The following is an example how to use the JavaScript Templates engine on the
+server-side with [node.js](http://nodejs.org/).
+
+Create a new directory and add the **tmpl.js** file. Or alternatively, install
+the **blueimp-tmpl** package with [npm](https://www.npmjs.org/):
+
+```sh
+npm install blueimp-tmpl
+```
+
+Add a file **template.html** with the following content:
+
+```html
+<!DOCTYPE HTML>
+<title>{%=o.title%}</title>
+<h3><a href="{%=o.url%}">{%=o.title%}</a></h3>
+<h4>Features</h4>
+<ul>
+{% for (var i=0; i<o.features.length; i++) { %}
+    <li>{%=o.features[i]%}</li>
+{% } %}
+</ul>
+```
+
+Add a file **server.js** with the following content:
+
+```js
+require("http").createServer(function (req, res) {
+    var fs = require("fs"),
+        // The tmpl module exports the tmpl() function:
+        tmpl = require("./tmpl"),
+        // Use the following version if you installed the package with npm:
+        // tmpl = require("blueimp-tmpl"),
+        // Sample data:
+        data = {
+            "title": "JavaScript Templates",
+            "url": "https://github.com/blueimp/JavaScript-Templates",
+            "features": [
+                "lightweight & fast",
+                "powerful",
+                "zero dependencies"
+            ]
+        };
+    // Override the template loading method:
+    tmpl.load = function (id) {
+        var filename = id + ".html";
+        console.log("Loading " + filename);
+        return fs.readFileSync(filename, "utf8");
+    };
+    res.writeHead(200, {"Content-Type": "text/x-tmpl"});
+    // Render the content:
+    res.end(tmpl("template", data));
+}).listen(8080, "localhost");
+console.log("Server running at http://localhost:8080/");
+```
+
+Run the application with the following command:
+
+```sh
+node server.js
+```
+
+## Requirements
+The JavaScript Templates script has zero dependencies.
+
+## API
+
+### tmpl() function
+The **tmpl()** function is added to the global **window** object and can be
+called as global function:
+
+```js
+var result = tmpl("tmpl-demo", data);
+```
+
+The **tmpl()** function can be called with the id of a template, or with a
+template string:
+
+```js
+var result = tmpl("<h3>{%=o.title%}</h3>", data);
+```
+
+If called without second argument, **tmpl()** returns a reusable template
+function:
+
+```js
+var func = tmpl("<h3>{%=o.title%}</h3>");
+document.getElementById("result").innerHTML = func(data);
+```
+
+### Templates cache
+Templates loaded by id are cached in the map **tmpl.cache**:
+
+```js
+var func = tmpl("tmpl-demo"), // Loads and parses the template
+    cached = typeof tmpl.cache["tmpl-demo"] === "function", // true
+    result = tmpl("tmpl-demo", data); // Uses cached template function
+
+tmpl.cache["tmpl-demo"] = null;
+result = tmpl("tmpl-demo", data); // Loads and parses the template again
+```
+
+### Output encoding
+The method **tmpl.encode** is used to escape HTML special characters in the
+template output:
+
+```js
+var output = tmpl.encode("<>&\"'\x00"); // Renders "&lt;&gt;&amp;&quot;&#39;"
+```
+
+**tmpl.encode** makes use of the regular expression **tmpl.encReg** and the
+encoding map **tmpl.encMap** to match and replace special characters, which can
+be modified to change the behavior of the output encoding.  
+Strings matched by the regular expression, but not found in the encoding map are
+removed from the output. This allows for example to automatically trim input
+values (removing whitespace from the start and end of the string):
+
+```js
+tmpl.encReg = /(^\s+)|(\s+$)|[<>&"'\x00]/g;
+var output = tmpl.encode("    Banana!    "); // Renders "Banana" (without whitespace)
+```
+
+### Local helper variables
+The local variables available inside the templates are the following:
+
+* **o**: The data object given as parameter to the template function
+(see the next section on how to modify the parameter name).
+* **tmpl**: A reference to the **tmpl** function object.
+* **_s**: The string for the rendered result content.
+* **_e**: A reference to the **tmpl.encode** method.
+* **print**: Helper function to add content to the rendered result string.
+* **include**: Helper function to include the return value of a different
+template in the result.
+
+To introduce additional local helper variables, the string **tmpl.helper** can
+be extended. The following adds a convenience function for *console.log* and a
+streaming function, that streams the template rendering result back to the
+callback argument
+(note the comma at the beginning of each variable declaration):
+
+```js
+tmpl.helper += ",log=function(){console.log.apply(console, arguments)}" +
+    ",st='',stream=function(cb){var l=st.length;st=_s;cb( _s.slice(l));}";
+```
+
+Those new helper functions could be used to stream the template contents to the
+console output:
+
+```html
+<script type="text/x-tmpl" id="tmpl-demo">
+<h3>{%=o.title%}</h3>
+{% stream(log); %}
+<p>Released under the
+<a href="{%=o.license.url%}">{%=o.license.name%}</a>.</p>
+{% stream(log); %}
+<h4>Features</h4>
+<ul>
+{% stream(log); %}
+{% for (var i=0; i<o.features.length; i++) { %}
+    <li>{%=o.features[i]%}</li>
+    {% stream(log); %}
+{% } %}
+</ul>
+{% stream(log); %}
+</script>
+```
+
+### Template function argument
+The generated template functions accept one argument, which is the data object
+given to the **tmpl(id, data)** function. This argument is available inside the
+template definitions as parameter **o** (the lowercase letter).
+
+The argument name can be modified by overriding **tmpl.arg**:
+
+```js
+tmpl.arg = "p";
+
+// Renders "<h3>JavaScript Templates</h3>":
+var result = tmpl("<h3>{%=p.title%}</h3>", {title: "JavaScript Templates"});
+```
+
+### Template parsing
+The template contents are matched and replaced using the regular expression
+**tmpl.regexp** and the replacement function **tmpl.func**.
+The replacement function operates based on the
+[parenthesized submatch strings](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter).
+
+To use different tags for the template syntax, override **tmpl.regexp** with a
+modified regular expression, by exchanging all occurrences of "{%" and "%}",
+e.g. with "[%" and "%]":
+
+```js
+tmpl.regexp = /([\s'\\])(?!(?:[^[]|\[(?!%))*%\])|(?:\[%(=|#)([\s\S]+?)%\])|(\[%)|(%\])/g;
+```
+
+By default, the plugin preserves whitespace
+(newlines, carriage returns, tabs and spaces).
+To strip unnecessary whitespace, you can override the **tmpl.func** function,
+e.g. with the following code:
+
+```js
+var originalFunc = tmpl.func;
+tmpl.func = function (s, p1, p2, p3, p4, p5, offset, str) {
+    if (p1 && /\s/.test(p1)) {
+        if (!offset || /\s/.test(str.charAt(offset - 1)) ||
+                /^\s+$/g.test(str.slice(offset))) {
+            return '';
+        }
+        return ' ';
+    }
+    return originalFunc.apply(tmpl, arguments);
+};
+```
+
+## Templates syntax
+
+### Interpolation
+Print variable with HTML special characters escaped:
+
+```html
+<h3>{%=o.title%}</h3>
+```
+
+Print variable without escaping:
+
+```html
+<h3>{%#o.user_id%}</h3>
+```
+
+Print output of function calls:
+
+```html
+<a href="{%=encodeURI(o.url)%}">Website</a>
+```
+
+Use dot notation to print nested properties:
+
+```html
+<strong>{%=o.author.name%}</strong>
+```
+
+### Evaluation
+Use **print(str)** to add escaped content to the output:
+
+```html
+<span>Year: {% var d=new Date(); print(d.getFullYear()); %}</span>
+```
+
+Use **print(str, true)** to add unescaped content to the output:
+
+```html
+<span>{% print("Fast &amp; powerful", true); %}</span>
+```
+
+Use **include(str, obj)** to include content from a different template:
+
+```html
+<div>
+{% include('tmpl-link', {name: "Website", url: "https://example.org"}); %}
+</div>
+```
+
+**If else condition**:
+
+```html
+{% if (o.author.url) { %}
+    <a href="{%=encodeURI(o.author.url)%}">{%=o.author.name%}</a>
+{% } else { %}
+    <em>No author url.</em>
+{% } %}
+```
+
+**For loop**:
+
+```html
+<ul>
+{% for (var i=0; i<o.features.length; i++) { %}
+    <li>{%=o.features[i]%}</li>
+{% } %}
+</ul>
+```
+
+## Compiled templates
+The JavaScript Templates project comes with a compilation script, that allows
+you to compile your templates into JavaScript code and combine them with a
+minimal Templates runtime into one minified JavaScript file.
+
+The compilation script is built for [node.js](http://nodejs.org/) and also
+requires [UglifyJS](https://github.com/mishoo/UglifyJS).  
+To use it, first install both the JavaScript Templates project and UglifyJS via
+[npm](https://www.npmjs.org/):
+
+```sh
+npm install uglify-js blueimp-tmpl
+```
+
+This will put the executables **uglifyjs** and **tmpl.js** into the folder
+**node_modules/.bin**. It will also make them available on your PATH if you
+install the packages globally
+(by adding the **-g** flag to the install command).
+
+The **tmpl.js** executable accepts the paths to one or multiple template files
+as command line arguments and prints the generated JavaScript code to the
+console output. The following command line shows you how to store the generated
+code in a new JavaScript file that can be included in your project:
+
+```sh
+tmpl.js templates/upload.html templates/download.html > tmpl.min.js
+```
+
+The files given as command line arguments to **tmpl.js** can either be pure
+template files or HTML documents with embedded template script sections.
+For the pure template files, the file names (without extension) serve as
+template ids.  
+The generated file can be included in your project as a replacement for the
+original **tmpl.js** runtime. It provides you with the same API and provides a
+**tmpl(id, data)** function that accepts the id of one of your templates as
+first and a data object as optional second parameter.
+
+## Tests
+The JavaScript Templates project comes with
+[Unit Tests](https://en.wikipedia.org/wiki/Unit_testing).  
+There are two different ways to run the tests:
+
+* Open test/index.html in your browser or
+* run `npm test` in the Terminal in the root path of the repository package.
+
+The first one tests the browser integration,
+the second one the [node.js](http://nodejs.org/) integration.
+
+## License
+The JavaScript Templates script is released under the
+[MIT license](http://www.opensource.org/licenses/MIT).

+ 75 - 0
app/Resources/public/assets/blueimp-tmpl/css/demo.css

@@ -0,0 +1,75 @@
+/*
+ * JavaScript Templates Demo CSS
+ * https://github.com/blueimp/JavaScript-Templates
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+body {
+  max-width: 750px;
+  margin: 0 auto;
+  padding: 1em;
+  font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, sans-serif;
+  font-size: 1em;
+  line-height: 1.4em;
+  background: #222;
+  color: #fff;
+  -webkit-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+}
+a {
+  color: orange;
+  text-decoration: none;
+}
+img {
+  border: 0;
+  vertical-align: middle;
+}
+h1 {
+  line-height: 1em;
+}
+textarea,
+input {
+  display: inline-block;
+  width: 100%;
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+  padding: 10px;
+  margin: 0 0 10px;
+  font-family: "Lucida Console", Monaco, monospace;
+}
+.result {
+  padding: 20px 40px;
+  background: #fff;
+  color: #222;
+}
+.error {
+  color: red;
+}
+
+@media (min-width: 481px) {
+  .navigation {
+    list-style: none;
+    padding: 0;
+  }
+  .navigation li {
+    display: inline-block;
+  }
+  .navigation li:not(:first-child):before {
+    content: '| ';
+  }
+}
+
+/* IE7 fixes */
+*+html textarea,
+*+html input {
+  width: 460px;
+}
+*+html .result {
+  width: 400px;
+}

+ 81 - 0
app/Resources/public/assets/blueimp-tmpl/index.html

@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<!--
+/*
+ * JavaScript Templates Demo
+ * https://github.com/blueimp/JavaScript-Templates
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+-->
+<html lang="en">
+<head>
+<!--[if IE]>
+<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+<![endif]-->
+<meta charset="utf-8">
+<title>JavaScript Templates Demo</title>
+<meta name="description" content="1KB lightweight, fast &amp; powerful JavaScript templating engine with zero dependencies. Compatible with server-side environments like Node.js, module loaders like RequireJS, Browserify or webpack and all web browsers.">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link rel="stylesheet" href="css/demo.css">
+</head>
+<body>
+<h1>JavaScript Templates Demo</h1>
+<p><strong>1KB</strong> lightweight, fast &amp; powerful <a href="https://developer.mozilla.org/en/JavaScript/">JavaScript</a> templating engine with zero dependencies.<br>
+Compatible with server-side environments like <a href="http://nodejs.org/">Node.js</a>, module loaders like <a href="http://requirejs.org/">RequireJS</a>, <a href="http://browserify.org/">Browserify</a> or <a href="https://webpack.github.io/">webpack</a> and all web browsers.</p>
+<ul class="navigation">
+    <li><a href="https://github.com/blueimp/JavaScript-Templates/tags">Download</a></li>
+    <li><a href="https://github.com/blueimp/JavaScript-Templates">Source Code</a></li>
+    <li><a href="https://github.com/blueimp/JavaScript-Templates/blob/master/README.md">Documentation</a></li>
+    <li><a href="test/">Test</a></li>
+    <li><a href="https://blueimp.net">&copy; Sebastian Tschan</a></li>
+</ul>
+<form>
+    <h2>Template</h2>
+    <textarea rows="12" id="template"></textarea>
+    <br>
+    <h2>Data (JSON)</h2>
+    <textarea rows="14" id="data"></textarea>
+    <br>
+    <button type="submit" id="render">Render</button>
+    <button type="reset" id="reset">Reset</button>
+    <h2>Result</h2>
+    <div id="result" class="result"></div>
+    <br>
+</form>
+<script type="text/x-tmpl" id="tmpl-demo">
+<h3>{%=o.title%}</h3>
+<p>Released under the
+<a href="{%=o.license.url%}">{%=o.license.name%}</a>.</p>
+<h4>Features</h4>
+<ul>
+{% for (var i=0; i<o.features.length; i++) { %}
+    <li>{%=o.features[i]%}</li>
+{% } %}
+</ul>
+</script>
+<script type="text/x-tmpl" id="tmpl-data">
+{
+    "title": "JavaScript Templates",
+    "license": {
+        "name": "MIT license",
+        "url": "http://www.opensource.org/licenses/MIT"
+    },
+    "features": [
+        "lightweight & fast",
+        "powerful",
+        "zero dependencies"
+    ]
+}
+</script>
+<script type="text/x-tmpl" id="tmpl-error">
+<h3 class="error">{%=o.title%}</h3>
+<code>{%=o.error%}</code>
+</script>
+<script src="js/tmpl.js"></script>
+<script src="js/demo.js"></script>
+</body>
+</html>

+ 83 - 0
app/Resources/public/assets/blueimp-tmpl/js/compile.js

@@ -0,0 +1,83 @@
+#!/usr/bin/env node
+/*
+ * JavaScript Templates Compiler
+ * https://github.com/blueimp/JavaScript-Templates
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global require, __dirname, process, console */
+
+;(function () {
+  'use strict'
+  var path = require('path')
+  var tmpl = require(path.join(__dirname, 'tmpl.js'))
+  var fs = require('fs')
+  var uglifyJS = require('uglify-js')
+  // Retrieve the content of the minimal runtime:
+  var runtime = fs.readFileSync(path.join(__dirname, 'runtime.js'), 'utf8')
+  // A regular expression to parse templates from script tags in a HTML page:
+  var regexp = /<script( id="([\w\-]+)")? type="text\/x-tmpl"( id="([\w\-]+)")?>([\s\S]+?)<\/script>/gi
+  // A regular expression to match the helper function names:
+  var helperRegexp = new RegExp(
+    tmpl.helper.match(/\w+(?=\s*=\s*function\s*\()/g).join('\\s*\\(|') + '\\s*\\('
+  )
+  // A list to store the function bodies:
+  var list = []
+  var code
+  // Extend the Templating engine with a print method for the generated functions:
+  tmpl.print = function (str) {
+    // Only add helper functions if they are used inside of the template:
+    var helper = helperRegexp.test(str) ? tmpl.helper : ''
+    var body = str.replace(tmpl.regexp, tmpl.func)
+    if (helper || (/_e\s*\(/.test(body))) {
+      helper = '_e=tmpl.encode' + helper + ','
+    }
+    return 'function(' + tmpl.arg + ',tmpl){' +
+    ('var ' + helper + "_s='" + body + "';return _s;")
+      .split("_s+='';").join('') + '}'
+  }
+  // Loop through the command line arguments:
+  process.argv.forEach(function (file, index) {
+    var listLength = list.length
+    var stats
+    var content
+    var result
+    var id
+    // Skip the first two arguments, which are "node" and the script:
+    if (index > 1) {
+      stats = fs.statSync(file)
+      if (!stats.isFile()) {
+        console.error(file + ' is not a file.')
+        return
+      }
+      content = fs.readFileSync(file, 'utf8')
+      while (true) {
+        // Find templates in script tags:
+        result = regexp.exec(content)
+        if (!result) {
+          break
+        }
+        id = result[2] || result[4]
+        list.push("'" + id + "':" + tmpl.print(result[5]))
+      }
+      if (listLength === list.length) {
+        // No template script tags found, use the complete content:
+        id = path.basename(file, path.extname(file))
+        list.push("'" + id + "':" + tmpl.print(content))
+      }
+    }
+  })
+  if (!list.length) {
+    console.error('Missing input file.')
+    return
+  }
+  // Combine the generated functions as cache of the minimal runtime:
+  code = runtime.replace('{}', '{' + list.join(',') + '}')
+  // Generate the minified code and print it to the console output:
+  console.log(uglifyJS.minify(code, {fromString: true}).code)
+}())

+ 68 - 0
app/Resources/public/assets/blueimp-tmpl/js/demo.js

@@ -0,0 +1,68 @@
+/*
+ * JavaScript Templates Demo
+ * https://github.com/blueimp/JavaScript-Templates
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* global document, tmpl */
+
+;(function () {
+  'use strict'
+
+  var templateInput = document.getElementById('template')
+  var dataInput = document.getElementById('data')
+  var resultNode = document.getElementById('result')
+  var templateDemoNode = document.getElementById('tmpl-demo')
+  var templateDataNode = document.getElementById('tmpl-data')
+
+  function renderError (title, error) {
+    resultNode.innerHTML = tmpl(
+      'tmpl-error',
+      {title: title, error: error}
+    )
+  }
+
+  function render (event) {
+    event.preventDefault()
+    var data
+    try {
+      data = JSON.parse(dataInput.value)
+    } catch (e) {
+      renderError('JSON parsing failed', e)
+      return
+    }
+    try {
+      resultNode.innerHTML = tmpl(
+        templateInput.value,
+        data
+      )
+    } catch (e) {
+      renderError('Template rendering failed', e)
+    }
+  }
+
+  function empty (node) {
+    while (node.lastChild) {
+      node.removeChild(node.lastChild)
+    }
+  }
+
+  function init (event) {
+    if (event) {
+      event.preventDefault()
+    }
+    templateInput.value = templateDemoNode.innerHTML.trim()
+    dataInput.value = templateDataNode.innerHTML.trim()
+    empty(resultNode)
+  }
+
+  document.getElementById('render').addEventListener('click', render)
+  document.getElementById('reset').addEventListener('click', init)
+
+  init()
+}())

+ 48 - 0
app/Resources/public/assets/blueimp-tmpl/js/runtime.js

@@ -0,0 +1,48 @@
+/*
+ * JavaScript Templates Runtime
+ * https://github.com/blueimp/JavaScript-Templates
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global define, module */
+
+;(function ($) {
+  'use strict'
+  var tmpl = function (id, data) {
+    var f = tmpl.cache[id]
+    return data ? f(data, tmpl) : function (data) {
+      return f(data, tmpl)
+    }
+  }
+  tmpl.cache = {}
+  tmpl.encReg = /[<>&"'\x00]/g
+  tmpl.encMap = {
+    '<': '&lt;',
+    '>': '&gt;',
+    '&': '&amp;',
+    '"': '&quot;',
+    "'": '&#39;'
+  }
+  tmpl.encode = function (s) {
+    return (s == null ? '' : '' + s).replace(
+      tmpl.encReg,
+      function (c) {
+        return tmpl.encMap[c] || ''
+      }
+    )
+  }
+  if (typeof define === 'function' && define.amd) {
+    define(function () {
+      return tmpl
+    })
+  } else if (typeof module === 'object' && module.exports) {
+    module.exports = tmpl
+  } else {
+    $.tmpl = tmpl
+  }
+}(this))

+ 86 - 0
app/Resources/public/assets/blueimp-tmpl/js/tmpl.js

@@ -0,0 +1,86 @@
+/*
+ * JavaScript Templates
+ * https://github.com/blueimp/JavaScript-Templates
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ *
+ * Inspired by John Resig's JavaScript Micro-Templating:
+ * http://ejohn.org/blog/javascript-micro-templating/
+ */
+
+/*global document, define, module */
+
+;(function ($) {
+  'use strict'
+  var tmpl = function (str, data) {
+    var f = !/[^\w\-\.:]/.test(str)
+      ? tmpl.cache[str] = tmpl.cache[str] || tmpl(tmpl.load(str))
+      : new Function(// eslint-disable-line no-new-func
+        tmpl.arg + ',tmpl',
+        'var _e=tmpl.encode' + tmpl.helper + ",_s='" +
+          str.replace(tmpl.regexp, tmpl.func) + "';return _s;"
+      )
+    return data ? f(data, tmpl) : function (data) {
+      return f(data, tmpl)
+    }
+  }
+  tmpl.cache = {}
+  tmpl.load = function (id) {
+    return document.getElementById(id).innerHTML
+  }
+  tmpl.regexp = /([\s'\\])(?!(?:[^{]|\{(?!%))*%\})|(?:\{%(=|#)([\s\S]+?)%\})|(\{%)|(%\})/g
+  tmpl.func = function (s, p1, p2, p3, p4, p5) {
+    if (p1) { // whitespace, quote and backspace in HTML context
+      return {
+        '\n': '\\n',
+        '\r': '\\r',
+        '\t': '\\t',
+        ' ': ' '
+      }[p1] || '\\' + p1
+    }
+    if (p2) { // interpolation: {%=prop%}, or unescaped: {%#prop%}
+      if (p2 === '=') {
+        return "'+_e(" + p3 + ")+'"
+      }
+      return "'+(" + p3 + "==null?'':" + p3 + ")+'"
+    }
+    if (p4) { // evaluation start tag: {%
+      return "';"
+    }
+    if (p5) { // evaluation end tag: %}
+      return "_s+='"
+    }
+  }
+  tmpl.encReg = /[<>&"'\x00]/g
+  tmpl.encMap = {
+    '<': '&lt;',
+    '>': '&gt;',
+    '&': '&amp;',
+    '"': '&quot;',
+    "'": '&#39;'
+  }
+  tmpl.encode = function (s) {
+    return (s == null ? '' : '' + s).replace(
+      tmpl.encReg,
+      function (c) {
+        return tmpl.encMap[c] || ''
+      }
+    )
+  }
+  tmpl.arg = 'o'
+  tmpl.helper = ",print=function(s,e){_s+=e?(s==null?'':s):_e(s);}" +
+                  ',include=function(s,d){_s+=tmpl(s,d);}'
+  if (typeof define === 'function' && define.amd) {
+    define(function () {
+      return tmpl
+    })
+  } else if (typeof module === 'object' && module.exports) {
+    module.exports = tmpl
+  } else {
+    $.tmpl = tmpl
+  }
+}(this))

+ 2 - 0
app/Resources/public/assets/blueimp-tmpl/js/tmpl.min.js

@@ -0,0 +1,2 @@
+!function(e){"use strict";var n=function(e,t){var r=/[^\w\-\.:]/.test(e)?new Function(n.arg+",tmpl","var _e=tmpl.encode"+n.helper+",_s='"+e.replace(n.regexp,n.func)+"';return _s;"):n.cache[e]=n.cache[e]||n(n.load(e));return t?r(t,n):function(e){return r(e,n)}};n.cache={},n.load=function(e){return document.getElementById(e).innerHTML},n.regexp=/([\s'\\])(?!(?:[^{]|\{(?!%))*%\})|(?:\{%(=|#)([\s\S]+?)%\})|(\{%)|(%\})/g,n.func=function(e,n,t,r,c,u){return n?{"\n":"\\n","\r":"\\r","	":"\\t"," ":" "}[n]||"\\"+n:t?"="===t?"'+_e("+r+")+'":"'+("+r+"==null?'':"+r+")+'":c?"';":u?"_s+='":void 0},n.encReg=/[<>&"'\x00]/g,n.encMap={"<":"&lt;",">":"&gt;","&":"&amp;",'"':"&quot;","'":"&#39;"},n.encode=function(e){return(null==e?"":""+e).replace(n.encReg,function(e){return n.encMap[e]||""})},n.arg="o",n.helper=",print=function(s,e){_s+=e?(s==null?'':s):_e(s);},include=function(s,d){_s+=tmpl(s,d);}","function"==typeof define&&define.amd?define(function(){return n}):"object"==typeof module&&module.exports?module.exports=n:e.tmpl=n}(this);
+//# sourceMappingURL=tmpl.min.js.map

+ 1 - 0
app/Resources/public/assets/blueimp-tmpl/js/tmpl.min.js.map

@@ -0,0 +1 @@
+{"version":3,"sources":["tmpl.js"],"names":["$","tmpl","str","data","f","test","Function","arg","helper","replace","regexp","func","cache","load","id","document","getElementById","innerHTML","s","p1","p2","p3","p4","p5","\n","\r","\t"," ","encReg","encMap","<",">","&","\"","'","encode","c","define","amd","module","exports","this"],"mappings":"CAgBE,SAAUA,GACV,YACA,IAAIC,GAAO,SAAUC,EAAKC,GACxB,GAAIC,GAAK,aAAaC,KAAKH,GAEvB,GAAII,UACJL,EAAKM,IAAM,QACX,qBAAuBN,EAAKO,OAAS,QACnCN,EAAIO,QAAQR,EAAKS,OAAQT,EAAKU,MAAQ,gBAJxCV,EAAKW,MAAMV,GAAOD,EAAKW,MAAMV,IAAQD,EAAKA,EAAKY,KAAKX,GAMxD,OAAOC,GAAOC,EAAED,EAAMF,GAAQ,SAAUE,GACtC,MAAOC,GAAED,EAAMF,IAGnBA,GAAKW,SACLX,EAAKY,KAAO,SAAUC,GACpB,MAAOC,UAASC,eAAeF,GAAIG,WAErChB,EAAKS,OAAS,2EACdT,EAAKU,KAAO,SAAUO,EAAGC,EAAIC,EAAIC,EAAIC,EAAIC,GACvC,MAAIJ,IAEAK,KAAM,MACNC,KAAM,MACNC,IAAM,MACNC,IAAK,KACLR,IAAO,KAAOA,EAEdC,EACS,MAAPA,EACK,QAAUC,EAAK,MAEjB,MAAQA,EAAK,aAAeA,EAAK,MAEtCC,EACK,KAELC,EACK,QADT,QAIFtB,EAAK2B,OAAS,eACd3B,EAAK4B,QACHC,IAAK,OACLC,IAAK,OACLC,IAAK,QACLC,IAAK,SACLC,IAAK,SAEPjC,EAAKkC,OAAS,SAAUjB,GACtB,OAAa,MAALA,EAAY,GAAK,GAAKA,GAAGT,QAC/BR,EAAK2B,OACL,SAAUQ,GACR,MAAOnC,GAAK4B,OAAOO,IAAM,MAI/BnC,EAAKM,IAAM,IACXN,EAAKO,OAAS,0FAEQ,kBAAX6B,SAAyBA,OAAOC,IACzCD,OAAO,WACL,MAAOpC,KAEkB,gBAAXsC,SAAuBA,OAAOC,QAC9CD,OAAOC,QAAUvC,EAEjBD,EAAEC,KAAOA,GAEXwC","file":"tmpl.min.js"}

+ 38 - 0
app/Resources/public/assets/blueimp-tmpl/package.json

@@ -0,0 +1,38 @@
+{
+  "name": "blueimp-tmpl",
+  "version": "3.3.0",
+  "title": "JavaScript Templates",
+  "description": "1KB lightweight, fast & powerful JavaScript templating engine with zero dependencies. Compatible with server-side environments like Node.js, module loaders like RequireJS, Browserify or webpack and all web browsers.",
+  "keywords": [
+    "javascript",
+    "templates",
+    "templating"
+  ],
+  "homepage": "https://github.com/blueimp/JavaScript-Templates",
+  "author": {
+    "name": "Sebastian Tschan",
+    "url": "https://blueimp.net"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/blueimp/JavaScript-Templates.git"
+  },
+  "license": "MIT",
+  "devDependencies": {
+    "expect.js": "0.3.1",
+    "mocha": "2.3.4",
+    "standard": "6.0.7",
+    "uglify-js": "2.6.1"
+  },
+  "scripts": {
+    "test": "standard js/*.js test/*.js && mocha",
+    "build": "cd js && uglifyjs tmpl.js -c -m -o tmpl.min.js --source-map tmpl.min.js.map",
+    "preversion": "npm test",
+    "version": "npm run build && git add -A js",
+    "postversion": "git push --tags origin master master:gh-pages && npm publish"
+  },
+  "bin": {
+    "tmpl.js": "js/compile.js"
+  },
+  "main": "js/tmpl.js"
+}

+ 39 - 0
app/Resources/public/assets/blueimp-tmpl/test/index.html

@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<!--
+/*
+ * JavaScript Templates Test
+ * https://github.com/blueimp/JavaScript-Templates
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+-->
+<html lang="en">
+<head>
+<!--[if IE]>
+<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+<![endif]-->
+<meta charset="utf-8">
+<title>JavaScript Templates Test</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link rel="stylesheet" href="vendor/mocha.css">
+</head>
+<body>
+<div id="mocha"></div>
+<script src="vendor/mocha.js"></script>
+<script src="vendor/expect.js"></script>
+<script>
+mocha.setup('bdd');
+</script>
+<script type="text/x-tmpl" id="template">{%=o.value%}</script>
+<script src="../js/tmpl.js"></script>
+<script src="test.js"></script>
+<script>
+mocha.checkLeaks();
+mocha.run();
+</script>
+</body>
+</html>

+ 305 - 0
app/Resources/public/assets/blueimp-tmpl/test/test.js

@@ -0,0 +1,305 @@
+/*
+ * JavaScript Templates Test
+ * https://github.com/blueimp/JavaScript-Templates
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/*global beforeEach, afterEach, describe, it, require */
+
+;(function (context, expect, tmpl) {
+  'use strict'
+
+  if (context.require === undefined) {
+    // Override the template loading method:
+    tmpl.load = function (id) {
+      switch (id) {
+        case 'template':
+          return '{%=o.value%}'
+      }
+    }
+  }
+
+  var data
+
+  beforeEach(function () {
+    // Initialize the sample data:
+    data = {
+      value: 'value',
+      nullValue: null,
+      falseValue: false,
+      zeroValue: 0,
+      special: '<>&"\'\x00',
+      list: [1, 2, 3, 4, 5],
+      func: function () {
+        return this.value
+      },
+      deep: {
+        value: 'value'
+      }
+    }
+  })
+
+  afterEach(function () {
+    // Purge the template cache:
+    tmpl.cache = {}
+  })
+
+  describe('Template loading', function () {
+    it('String template', function () {
+      expect(
+        tmpl('{%=o.value%}', data)
+      ).to.be(
+        'value'
+      )
+    })
+
+    it('Load template by id', function () {
+      expect(
+        tmpl('template', data)
+      ).to.be(
+        'value'
+      )
+    })
+
+    it('Retun function when called without data parameter', function () {
+      expect(
+        tmpl('{%=o.value%}')(data)
+      ).to.be(
+        'value'
+      )
+    })
+
+    it('Cache templates loaded by id', function () {
+      tmpl('template')
+      expect(
+        tmpl.cache.template
+      ).to.be.a('function')
+    })
+  })
+
+  describe('Interpolation', function () {
+    it('Escape HTML special characters with {%=o.prop%}', function () {
+      expect(
+        tmpl('{%=o.special%}', data)
+      ).to.be(
+        '&lt;&gt;&amp;&quot;&#39;'
+      )
+    })
+
+    it('Allow HTML special characters with {%#o.prop%}', function () {
+      expect(
+        tmpl('{%#o.special%}', data)
+      ).to.be(
+        '<>&"\'\x00'
+      )
+    })
+
+    it('Function call', function () {
+      expect(
+        tmpl('{%=o.func()%}', data)
+      ).to.be(
+        'value'
+      )
+    })
+
+    it('Dot notation', function () {
+      expect(
+        tmpl('{%=o.deep.value%}', data)
+      ).to.be(
+        'value'
+      )
+    })
+
+    it('Handle single quotes', function () {
+      expect(
+        tmpl('\'single quotes\'{%=": \'"%}', data)
+      ).to.be(
+        "'single quotes': &#39;"
+      )
+    })
+
+    it('Handle double quotes', function () {
+      expect(
+        tmpl('"double quotes"{%=": \\""%}', data)
+      ).to.be(
+        '"double quotes": &quot;'
+      )
+    })
+
+    it('Handle backslashes', function () {
+      expect(
+        tmpl('\\backslashes\\{%=": \\\\"%}', data)
+      ).to.be(
+        '\\backslashes\\: \\'
+      )
+    })
+
+    it('Interpolate escaped falsy values except undefined or null', function () {
+      expect(
+        tmpl(
+          '{%=o.undefinedValue%}' +
+          '{%=o.nullValue%}' +
+          '{%=o.falseValue%}' +
+          '{%=o.zeroValue%}',
+          data
+        )
+      ).to.be(
+        'false0'
+      )
+    })
+
+    it('Interpolate unescaped falsy values except undefined or null', function () {
+      expect(
+        tmpl(
+          '{%#o.undefinedValue%}' +
+          '{%#o.nullValue%}' +
+          '{%#o.falseValue%}' +
+          '{%#o.zeroValue%}',
+          data
+        )
+      ).to.be(
+        'false0'
+      )
+    })
+
+    it('Preserve whitespace', function () {
+      expect(
+        tmpl(
+          '\n\r\t{%=o.value%}  \n\r\t{%=o.value%}  ',
+          data
+        )
+      ).to.be(
+        '\n\r\tvalue  \n\r\tvalue  '
+      )
+    })
+  })
+
+  describe('Evaluation', function () {
+    it('Escape HTML special characters with print(data)', function () {
+      expect(
+        tmpl('{% print(o.special); %}', data)
+      ).to.be(
+        '&lt;&gt;&amp;&quot;&#39;'
+      )
+    })
+
+    it('Allow HTML special characters with print(data, true)', function () {
+      expect(
+        tmpl('{% print(o.special, true); %}', data)
+      ).to.be(
+        '<>&"\'\x00'
+      )
+    })
+
+    it('Print out escaped falsy values except undefined or null', function () {
+      expect(
+        tmpl(
+          '{% print(o.undefinedValue); %}' +
+          '{% print(o.nullValue); %}' +
+          '{% print(o.falseValue); %}' +
+          '{% print(o.zeroValue); %}',
+          data
+        )
+      ).to.be(
+        'false0'
+      )
+    })
+
+    it('Print out unescaped falsy values except undefined or null', function () {
+      expect(
+        tmpl(
+          '{% print(o.undefinedValue, true); %}' +
+          '{% print(o.nullValue, true); %}' +
+          '{% print(o.falseValue, true); %}' +
+          '{% print(o.zeroValue, true); %}',
+          data
+        )
+      ).to.be(
+        'false0'
+      )
+    })
+
+    it('Include template', function () {
+      expect(
+        tmpl('{% include("template", {value: "value"}); %}', data)
+      ).to.be(
+        'value'
+      )
+    })
+
+    it('If condition', function () {
+      expect(
+        tmpl('{% if (o.value) { %}true{% } else { %}false{% } %}', data)
+      ).to.be(
+        'true'
+      )
+    })
+
+    it('Else condition', function () {
+      expect(
+        tmpl(
+          '{% if (o.undefinedValue) { %}false{% } else { %}true{% } %}',
+          data
+        )
+      ).to.be(
+        'true'
+      )
+    })
+
+    it('For loop', function () {
+      expect(
+        tmpl(
+          '{% for (var i=0; i<o.list.length; i++) { %}' +
+          '{%=o.list[i]%}{% } %}',
+          data
+        )
+      ).to.be(
+        '12345'
+      )
+    })
+
+    it('For loop print call', function () {
+      expect(
+        tmpl(
+          '{% for (var i=0; i<o.list.length; i++) {' +
+          'print(o.list[i]);} %}',
+          data
+        )
+      ).to.be(
+        '12345'
+      )
+    })
+
+    it('For loop include template', function () {
+      expect(
+        tmpl(
+          '{% for (var i=0; i<o.list.length; i++) {' +
+          'include("template", {value: o.list[i]});} %}',
+          data
+        ).replace(/[\r\n]/g, '')
+      ).to.be(
+        '12345'
+      )
+    })
+
+    it('Modulo operator', function () {
+      expect(
+        tmpl(
+          '{% if (o.list.length % 5 === 0) { %}5 list items{% } %}',
+          data
+        ).replace(/[\r\n]/g, '')
+      ).to.be(
+        '5 list items'
+      )
+    })
+  })
+}(
+  this,
+  this.expect || require('expect.js'),
+  this.tmpl || require('../js/tmpl')
+))

+ 73 - 0
app/Resources/public/assets/jquery-file-upload/.bower.json

@@ -0,0 +1,73 @@
+{
+  "name": "blueimp-file-upload",
+  "version": "9.12.1",
+  "title": "jQuery File Upload",
+  "description": "File Upload widget with multiple file selection, drag&amp;drop support, progress bar, validation and preview images.",
+  "keywords": [
+    "jquery",
+    "file",
+    "upload",
+    "widget",
+    "multiple",
+    "selection",
+    "drag",
+    "drop",
+    "progress",
+    "preview",
+    "cross-domain",
+    "cross-site",
+    "chunk",
+    "resume",
+    "gae",
+    "go",
+    "python",
+    "php",
+    "bootstrap"
+  ],
+  "homepage": "https://github.com/blueimp/jQuery-File-Upload",
+  "author": {
+    "name": "Sebastian Tschan",
+    "url": "https://blueimp.net"
+  },
+  "maintainers": [
+    {
+      "name": "Sebastian Tschan",
+      "url": "https://blueimp.net"
+    }
+  ],
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/blueimp/jQuery-File-Upload.git"
+  },
+  "bugs": "https://github.com/blueimp/jQuery-File-Upload/issues",
+  "license": "MIT",
+  "dependencies": {
+    "jquery": ">=1.6",
+    "blueimp-tmpl": ">=2.5.4",
+    "blueimp-load-image": ">=1.13.0",
+    "blueimp-canvas-to-blob": ">=2.1.1"
+  },
+  "main": [
+    "js/jquery.fileupload.js"
+  ],
+  "ignore": [
+    "/*.*",
+    "/cors",
+    "css/demo-ie8.css",
+    "css/demo.css",
+    "css/style.css",
+    "js/app.js",
+    "js/main.js",
+    "server",
+    "test"
+  ],
+  "_release": "9.12.1",
+  "_resolution": {
+    "type": "version",
+    "tag": "9.12.1",
+    "commit": "39a40c6c8dc9503c896ca2efac65b7340e439724"
+  },
+  "_source": "git://github.com/blueimp/jQuery-File-Upload.git",
+  "_target": "*",
+  "_originalSource": "jquery-file-upload"
+}

+ 64 - 0
app/Resources/public/assets/jquery-file-upload/bower.json

@@ -0,0 +1,64 @@
+{
+  "name": "blueimp-file-upload",
+  "version": "9.12.1",
+  "title": "jQuery File Upload",
+  "description": "File Upload widget with multiple file selection, drag&amp;drop support, progress bar, validation and preview images.",
+  "keywords": [
+    "jquery",
+    "file",
+    "upload",
+    "widget",
+    "multiple",
+    "selection",
+    "drag",
+    "drop",
+    "progress",
+    "preview",
+    "cross-domain",
+    "cross-site",
+    "chunk",
+    "resume",
+    "gae",
+    "go",
+    "python",
+    "php",
+    "bootstrap"
+  ],
+  "homepage": "https://github.com/blueimp/jQuery-File-Upload",
+  "author": {
+    "name": "Sebastian Tschan",
+    "url": "https://blueimp.net"
+  },
+  "maintainers": [
+    {
+      "name": "Sebastian Tschan",
+      "url": "https://blueimp.net"
+    }
+  ],
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/blueimp/jQuery-File-Upload.git"
+  },
+  "bugs": "https://github.com/blueimp/jQuery-File-Upload/issues",
+  "license": "MIT",
+  "dependencies": {
+    "jquery": ">=1.6",
+    "blueimp-tmpl": ">=2.5.4",
+    "blueimp-load-image": ">=1.13.0",
+    "blueimp-canvas-to-blob": ">=2.1.1"
+  },
+  "main": [
+    "js/jquery.fileupload.js"
+  ],
+  "ignore": [
+    "/*.*",
+    "/cors",
+    "css/demo-ie8.css",
+    "css/demo.css",
+    "css/style.css",
+    "js/app.js",
+    "js/main.js",
+    "server",
+    "test"
+  ]
+}

+ 22 - 0
app/Resources/public/assets/jquery-file-upload/css/jquery.fileupload-noscript.css

@@ -0,0 +1,22 @@
+@charset "UTF-8";
+/*
+ * jQuery File Upload Plugin NoScript CSS
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+.fileinput-button input {
+  position: static;
+  opacity: 1;
+  filter: none;
+  font-size: inherit;
+  direction: inherit;
+}
+.fileinput-button span {
+  display: none;
+}

+ 17 - 0
app/Resources/public/assets/jquery-file-upload/css/jquery.fileupload-ui-noscript.css

@@ -0,0 +1,17 @@
+@charset "UTF-8";
+/*
+ * jQuery File Upload UI Plugin NoScript CSS
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+.fileinput-button i,
+.fileupload-buttonbar .delete,
+.fileupload-buttonbar .toggle {
+  display: none;
+}

+ 57 - 0
app/Resources/public/assets/jquery-file-upload/css/jquery.fileupload-ui.css

@@ -0,0 +1,57 @@
+@charset "UTF-8";
+/*
+ * jQuery File Upload UI Plugin CSS
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+.fileupload-buttonbar .btn,
+.fileupload-buttonbar .toggle {
+  margin-bottom: 5px;
+}
+.progress-animated .progress-bar,
+.progress-animated .bar {
+  background: url("../img/progressbar.gif") !important;
+  filter: none;
+}
+.fileupload-process {
+  float: right;
+  display: none;
+}
+.fileupload-processing .fileupload-process,
+.files .processing .preview {
+  display: block;
+  width: 32px;
+  height: 32px;
+  background: url("../img/loading.gif") center no-repeat;
+  background-size: contain;
+}
+.files audio,
+.files video {
+  max-width: 300px;
+}
+
+@media (max-width: 767px) {
+  .fileupload-buttonbar .toggle,
+  .files .toggle,
+  .files .btn span {
+    display: none;
+  }
+  .files .name {
+    width: 80px;
+    word-wrap: break-word;
+  }
+  .files audio,
+  .files video {
+    max-width: 80px;
+  }
+  .files img,
+  .files canvas {
+    max-width: 100%;
+  }
+}

+ 37 - 0
app/Resources/public/assets/jquery-file-upload/css/jquery.fileupload.css

@@ -0,0 +1,37 @@
+@charset "UTF-8";
+/*
+ * jQuery File Upload Plugin CSS
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+.fileinput-button {
+  position: relative;
+  overflow: hidden;
+  display: inline-block;
+}
+.fileinput-button input {
+  position: absolute;
+  top: 0;
+  right: 0;
+  margin: 0;
+  opacity: 0;
+  -ms-filter: 'alpha(opacity=0)';
+  font-size: 200px !important;
+  direction: ltr;
+  cursor: pointer;
+}
+
+/* Fixes for IE < 8 */
+@media screen\9 {
+  .fileinput-button input {
+    filter: alpha(opacity=0);
+    font-size: 100%;
+    height: 100%;
+  }
+}

TEMPAT SAMPAH
app/Resources/public/assets/jquery-file-upload/img/loading.gif


+ 0 - 0
main/inc/lib/javascript/jquery-upload/pbar-ani.gif → app/Resources/public/assets/jquery-file-upload/img/progressbar.gif


+ 120 - 0
app/Resources/public/assets/jquery-file-upload/js/cors/jquery.postmessage-transport.js

@@ -0,0 +1,120 @@
+/*
+ * jQuery postMessage Transport Plugin
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* global define, require, window, document */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define(['jquery'], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS:
+        factory(require('jquery'));
+    } else {
+        // Browser globals:
+        factory(window.jQuery);
+    }
+}(function ($) {
+    'use strict';
+
+    var counter = 0,
+        names = [
+            'accepts',
+            'cache',
+            'contents',
+            'contentType',
+            'crossDomain',
+            'data',
+            'dataType',
+            'headers',
+            'ifModified',
+            'mimeType',
+            'password',
+            'processData',
+            'timeout',
+            'traditional',
+            'type',
+            'url',
+            'username'
+        ],
+        convert = function (p) {
+            return p;
+        };
+
+    $.ajaxSetup({
+        converters: {
+            'postmessage text': convert,
+            'postmessage json': convert,
+            'postmessage html': convert
+        }
+    });
+
+    $.ajaxTransport('postmessage', function (options) {
+        if (options.postMessage && window.postMessage) {
+            var iframe,
+                loc = $('<a>').prop('href', options.postMessage)[0],
+                target = loc.protocol + '//' + loc.host,
+                xhrUpload = options.xhr().upload;
+            return {
+                send: function (_, completeCallback) {
+                    counter += 1;
+                    var message = {
+                            id: 'postmessage-transport-' + counter
+                        },
+                        eventName = 'message.' + message.id;
+                    iframe = $(
+                        '<iframe style="display:none;" src="' +
+                            options.postMessage + '" name="' +
+                            message.id + '"></iframe>'
+                    ).bind('load', function () {
+                        $.each(names, function (i, name) {
+                            message[name] = options[name];
+                        });
+                        message.dataType = message.dataType.replace('postmessage ', '');
+                        $(window).bind(eventName, function (e) {
+                            e = e.originalEvent;
+                            var data = e.data,
+                                ev;
+                            if (e.origin === target && data.id === message.id) {
+                                if (data.type === 'progress') {
+                                    ev = document.createEvent('Event');
+                                    ev.initEvent(data.type, false, true);
+                                    $.extend(ev, data);
+                                    xhrUpload.dispatchEvent(ev);
+                                } else {
+                                    completeCallback(
+                                        data.status,
+                                        data.statusText,
+                                        {postmessage: data.result},
+                                        data.headers
+                                    );
+                                    iframe.remove();
+                                    $(window).unbind(eventName);
+                                }
+                            }
+                        });
+                        iframe[0].contentWindow.postMessage(
+                            message,
+                            target
+                        );
+                    }).appendTo(document.body);
+                },
+                abort: function () {
+                    if (iframe) {
+                        iframe.remove();
+                    }
+                }
+            };
+        }
+    });
+
+}));

+ 89 - 0
app/Resources/public/assets/jquery-file-upload/js/cors/jquery.xdr-transport.js

@@ -0,0 +1,89 @@
+/*
+ * jQuery XDomainRequest Transport Plugin
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ *
+ * Based on Julian Aubourg's ajaxHooks xdr.js:
+ * https://github.com/jaubourg/ajaxHooks/
+ */
+
+/* global define, require, window, XDomainRequest */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define(['jquery'], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS:
+        factory(require('jquery'));
+    } else {
+        // Browser globals:
+        factory(window.jQuery);
+    }
+}(function ($) {
+    'use strict';
+    if (window.XDomainRequest && !$.support.cors) {
+        $.ajaxTransport(function (s) {
+            if (s.crossDomain && s.async) {
+                if (s.timeout) {
+                    s.xdrTimeout = s.timeout;
+                    delete s.timeout;
+                }
+                var xdr;
+                return {
+                    send: function (headers, completeCallback) {
+                        var addParamChar = /\?/.test(s.url) ? '&' : '?';
+                        function callback(status, statusText, responses, responseHeaders) {
+                            xdr.onload = xdr.onerror = xdr.ontimeout = $.noop;
+                            xdr = null;
+                            completeCallback(status, statusText, responses, responseHeaders);
+                        }
+                        xdr = new XDomainRequest();
+                        // XDomainRequest only supports GET and POST:
+                        if (s.type === 'DELETE') {
+                            s.url = s.url + addParamChar + '_method=DELETE';
+                            s.type = 'POST';
+                        } else if (s.type === 'PUT') {
+                            s.url = s.url + addParamChar + '_method=PUT';
+                            s.type = 'POST';
+                        } else if (s.type === 'PATCH') {
+                            s.url = s.url + addParamChar + '_method=PATCH';
+                            s.type = 'POST';
+                        }
+                        xdr.open(s.type, s.url);
+                        xdr.onload = function () {
+                            callback(
+                                200,
+                                'OK',
+                                {text: xdr.responseText},
+                                'Content-Type: ' + xdr.contentType
+                            );
+                        };
+                        xdr.onerror = function () {
+                            callback(404, 'Not Found');
+                        };
+                        if (s.xdrTimeout) {
+                            xdr.ontimeout = function () {
+                                callback(0, 'timeout');
+                            };
+                            xdr.timeout = s.xdrTimeout;
+                        }
+                        xdr.send((s.hasContent && s.data) || null);
+                    },
+                    abort: function () {
+                        if (xdr) {
+                            xdr.onerror = $.noop();
+                            xdr.abort();
+                        }
+                    }
+                };
+            }
+        });
+    }
+}));

+ 425 - 0
app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-angular.js

@@ -0,0 +1,425 @@
+/*
+ * jQuery File Upload AngularJS Plugin
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* jshint nomen:false */
+/* global define, angular */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define([
+            'jquery',
+            'angular',
+            './jquery.fileupload-image',
+            './jquery.fileupload-audio',
+            './jquery.fileupload-video',
+            './jquery.fileupload-validate'
+        ], factory);
+    } else {
+        factory();
+    }
+}(function () {
+    'use strict';
+
+    angular.module('blueimp.fileupload', [])
+
+        // The fileUpload service provides configuration options
+        // for the fileUpload directive and default handlers for
+        // File Upload events:
+        .provider('fileUpload', function () {
+            var scopeEvalAsync = function (expression) {
+                    var scope = angular.element(this)
+                            .fileupload('option', 'scope');
+                    // Schedule a new $digest cycle if not already inside of one
+                    // and evaluate the given expression:
+                    scope.$evalAsync(expression);
+                },
+                addFileMethods = function (scope, data) {
+                    var files = data.files,
+                        file = files[0];
+                    angular.forEach(files, function (file, index) {
+                        file._index = index;
+                        file.$state = function () {
+                            return data.state();
+                        };
+                        file.$processing = function () {
+                            return data.processing();
+                        };
+                        file.$progress = function () {
+                            return data.progress();
+                        };
+                        file.$response = function () {
+                            return data.response();
+                        };
+                    });
+                    file.$submit = function () {
+                        if (!file.error) {
+                            return data.submit();
+                        }
+                    };
+                    file.$cancel = function () {
+                        return data.abort();
+                    };
+                },
+                $config;
+            $config = this.defaults = {
+                handleResponse: function (e, data) {
+                    var files = data.result && data.result.files;
+                    if (files) {
+                        data.scope.replace(data.files, files);
+                    } else if (data.errorThrown ||
+                            data.textStatus === 'error') {
+                        data.files[0].error = data.errorThrown ||
+                            data.textStatus;
+                    }
+                },
+                add: function (e, data) {
+                    if (e.isDefaultPrevented()) {
+                        return false;
+                    }
+                    var scope = data.scope,
+                        filesCopy = [];
+                    angular.forEach(data.files, function (file) {
+                        filesCopy.push(file);
+                    });
+                    scope.$parent.$applyAsync(function () {
+                        addFileMethods(scope, data);
+                        var method = scope.option('prependFiles') ?
+                                'unshift' : 'push';
+                        Array.prototype[method].apply(scope.queue, data.files);
+                    });
+                    data.process(function () {
+                        return scope.process(data);
+                    }).always(function () {
+                        scope.$parent.$applyAsync(function () {
+                            addFileMethods(scope, data);
+                            scope.replace(filesCopy, data.files);
+                        });
+                    }).then(function () {
+                        if ((scope.option('autoUpload') ||
+                                data.autoUpload) &&
+                                data.autoUpload !== false) {
+                            data.submit();
+                        }
+                    });
+                },
+                done: function (e, data) {
+                    if (e.isDefaultPrevented()) {
+                        return false;
+                    }
+                    var that = this;
+                    data.scope.$apply(function () {
+                        data.handleResponse.call(that, e, data);
+                    });
+                },
+                fail: function (e, data) {
+                    if (e.isDefaultPrevented()) {
+                        return false;
+                    }
+                    var that = this,
+                        scope = data.scope;
+                    if (data.errorThrown === 'abort') {
+                        scope.clear(data.files);
+                        return;
+                    }
+                    scope.$apply(function () {
+                        data.handleResponse.call(that, e, data);
+                    });
+                },
+                stop: scopeEvalAsync,
+                processstart: scopeEvalAsync,
+                processstop: scopeEvalAsync,
+                getNumberOfFiles: function () {
+                    var scope = this.scope;
+                    return scope.queue.length - scope.processing();
+                },
+                dataType: 'json',
+                autoUpload: false
+            };
+            this.$get = [
+                function () {
+                    return {
+                        defaults: $config
+                    };
+                }
+            ];
+        })
+
+        // Format byte numbers to readable presentations:
+        .provider('formatFileSizeFilter', function () {
+            var $config = {
+                // Byte units following the IEC format
+                // http://en.wikipedia.org/wiki/Kilobyte
+                units: [
+                    {size: 1000000000, suffix: ' GB'},
+                    {size: 1000000, suffix: ' MB'},
+                    {size: 1000, suffix: ' KB'}
+                ]
+            };
+            this.defaults = $config;
+            this.$get = function () {
+                return function (bytes) {
+                    if (!angular.isNumber(bytes)) {
+                        return '';
+                    }
+                    var unit = true,
+                        i = 0,
+                        prefix,
+                        suffix;
+                    while (unit) {
+                        unit = $config.units[i];
+                        prefix = unit.prefix || '';
+                        suffix = unit.suffix || '';
+                        if (i === $config.units.length - 1 || bytes >= unit.size) {
+                            return prefix + (bytes / unit.size).toFixed(2) + suffix;
+                        }
+                        i += 1;
+                    }
+                };
+            };
+        })
+
+        // The FileUploadController initializes the fileupload widget and
+        // provides scope methods to control the File Upload functionality:
+        .controller('FileUploadController', [
+            '$scope', '$element', '$attrs', '$window', 'fileUpload',
+            function ($scope, $element, $attrs, $window, fileUpload) {
+                var uploadMethods = {
+                    progress: function () {
+                        return $element.fileupload('progress');
+                    },
+                    active: function () {
+                        return $element.fileupload('active');
+                    },
+                    option: function (option, data) {
+                        if (arguments.length === 1) {
+                            return $element.fileupload('option', option);
+                        }
+                        $element.fileupload('option', option, data);
+                    },
+                    add: function (data) {
+                        return $element.fileupload('add', data);
+                    },
+                    send: function (data) {
+                        return $element.fileupload('send', data);
+                    },
+                    process: function (data) {
+                        return $element.fileupload('process', data);
+                    },
+                    processing: function (data) {
+                        return $element.fileupload('processing', data);
+                    }
+                };
+                $scope.disabled = !$window.jQuery.support.fileInput;
+                $scope.queue = $scope.queue || [];
+                $scope.clear = function (files) {
+                    var queue = this.queue,
+                        i = queue.length,
+                        file = files,
+                        length = 1;
+                    if (angular.isArray(files)) {
+                        file = files[0];
+                        length = files.length;
+                    }
+                    while (i) {
+                        i -= 1;
+                        if (queue[i] === file) {
+                            return queue.splice(i, length);
+                        }
+                    }
+                };
+                $scope.replace = function (oldFiles, newFiles) {
+                    var queue = this.queue,
+                        file = oldFiles[0],
+                        i,
+                        j;
+                    for (i = 0; i < queue.length; i += 1) {
+                        if (queue[i] === file) {
+                            for (j = 0; j < newFiles.length; j += 1) {
+                                queue[i + j] = newFiles[j];
+                            }
+                            return;
+                        }
+                    }
+                };
+                $scope.applyOnQueue = function (method) {
+                    var list = this.queue.slice(0),
+                        i,
+                        file;
+                    for (i = 0; i < list.length; i += 1) {
+                        file = list[i];
+                        if (file[method]) {
+                            file[method]();
+                        }
+                    }
+                };
+                $scope.submit = function () {
+                    this.applyOnQueue('$submit');
+                };
+                $scope.cancel = function () {
+                    this.applyOnQueue('$cancel');
+                };
+                // Add upload methods to the scope:
+                angular.extend($scope, uploadMethods);
+                // The fileupload widget will initialize with
+                // the options provided via "data-"-parameters,
+                // as well as those given via options object:
+                $element.fileupload(angular.extend(
+                    {scope: $scope},
+                    fileUpload.defaults
+                )).on('fileuploadadd', function (e, data) {
+                    data.scope = $scope;
+                }).on('fileuploadfail', function (e, data) {
+                    if (data.errorThrown === 'abort') {
+                        return;
+                    }
+                    if (data.dataType &&
+                            data.dataType.indexOf('json') === data.dataType.length - 4) {
+                        try {
+                            data.result = angular.fromJson(data.jqXHR.responseText);
+                        } catch (ignore) {}
+                    }
+                }).on([
+                    'fileuploadadd',
+                    'fileuploadsubmit',
+                    'fileuploadsend',
+                    'fileuploaddone',
+                    'fileuploadfail',
+                    'fileuploadalways',
+                    'fileuploadprogress',
+                    'fileuploadprogressall',
+                    'fileuploadstart',
+                    'fileuploadstop',
+                    'fileuploadchange',
+                    'fileuploadpaste',
+                    'fileuploaddrop',
+                    'fileuploaddragover',
+                    'fileuploadchunksend',
+                    'fileuploadchunkdone',
+                    'fileuploadchunkfail',
+                    'fileuploadchunkalways',
+                    'fileuploadprocessstart',
+                    'fileuploadprocess',
+                    'fileuploadprocessdone',
+                    'fileuploadprocessfail',
+                    'fileuploadprocessalways',
+                    'fileuploadprocessstop'
+                ].join(' '), function (e, data) {
+                    $scope.$parent.$applyAsync(function () {
+                        if ($scope.$emit(e.type, data).defaultPrevented) {
+                            e.preventDefault();
+                        }
+                    });
+                }).on('remove', function () {
+                    // Remove upload methods from the scope,
+                    // when the widget is removed:
+                    var method;
+                    for (method in uploadMethods) {
+                        if (uploadMethods.hasOwnProperty(method)) {
+                            delete $scope[method];
+                        }
+                    }
+                });
+                // Observe option changes:
+                $scope.$watch(
+                    $attrs.fileUpload,
+                    function (newOptions) {
+                        if (newOptions) {
+                            $element.fileupload('option', newOptions);
+                        }
+                    }
+                );
+            }
+        ])
+
+        // Provide File Upload progress feedback:
+        .controller('FileUploadProgressController', [
+            '$scope', '$attrs', '$parse',
+            function ($scope, $attrs, $parse) {
+                var fn = $parse($attrs.fileUploadProgress),
+                    update = function () {
+                        var progress = fn($scope);
+                        if (!progress || !progress.total) {
+                            return;
+                        }
+                        $scope.num = Math.floor(
+                            progress.loaded / progress.total * 100
+                        );
+                    };
+                update();
+                $scope.$watch(
+                    $attrs.fileUploadProgress + '.loaded',
+                    function (newValue, oldValue) {
+                        if (newValue !== oldValue) {
+                            update();
+                        }
+                    }
+                );
+            }
+        ])
+
+        // Display File Upload previews:
+        .controller('FileUploadPreviewController', [
+            '$scope', '$element', '$attrs',
+            function ($scope, $element, $attrs) {
+                $scope.$watch(
+                    $attrs.fileUploadPreview + '.preview',
+                    function (preview) {
+                        $element.empty();
+                        if (preview) {
+                            $element.append(preview);
+                        }
+                    }
+                );
+            }
+        ])
+
+        .directive('fileUpload', function () {
+            return {
+                controller: 'FileUploadController',
+                scope: true
+            };
+        })
+
+        .directive('fileUploadProgress', function () {
+            return {
+                controller: 'FileUploadProgressController',
+                scope: true
+            };
+        })
+
+        .directive('fileUploadPreview', function () {
+            return {
+                controller: 'FileUploadPreviewController'
+            };
+        })
+
+        // Enhance the HTML5 download attribute to
+        // allow drag&drop of files to the desktop:
+        .directive('download', function () {
+            return function (scope, elm) {
+                elm.on('dragstart', function (e) {
+                    try {
+                        e.originalEvent.dataTransfer.setData(
+                            'DownloadURL',
+                            [
+                                'application/octet-stream',
+                                elm.prop('download'),
+                                elm.prop('href')
+                            ].join(':')
+                        );
+                    } catch (ignore) {}
+                });
+            };
+        });
+
+}));

+ 112 - 0
app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-audio.js

@@ -0,0 +1,112 @@
+/*
+ * jQuery File Upload Audio Preview Plugin
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* jshint nomen:false */
+/* global define, require, window, document */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define([
+            'jquery',
+            'load-image',
+            './jquery.fileupload-process'
+        ], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS:
+        factory(
+            require('jquery'),
+            require('load-image')
+        );
+    } else {
+        // Browser globals:
+        factory(
+            window.jQuery,
+            window.loadImage
+        );
+    }
+}(function ($, loadImage) {
+    'use strict';
+
+    // Prepend to the default processQueue:
+    $.blueimp.fileupload.prototype.options.processQueue.unshift(
+        {
+            action: 'loadAudio',
+            // Use the action as prefix for the "@" options:
+            prefix: true,
+            fileTypes: '@',
+            maxFileSize: '@',
+            disabled: '@disableAudioPreview'
+        },
+        {
+            action: 'setAudio',
+            name: '@audioPreviewName',
+            disabled: '@disableAudioPreview'
+        }
+    );
+
+    // The File Upload Audio Preview plugin extends the fileupload widget
+    // with audio preview functionality:
+    $.widget('blueimp.fileupload', $.blueimp.fileupload, {
+
+        options: {
+            // The regular expression for the types of audio files to load,
+            // matched against the file type:
+            loadAudioFileTypes: /^audio\/.*$/
+        },
+
+        _audioElement: document.createElement('audio'),
+
+        processActions: {
+
+            // Loads the audio file given via data.files and data.index
+            // as audio element if the browser supports playing it.
+            // Accepts the options fileTypes (regular expression)
+            // and maxFileSize (integer) to limit the files to load:
+            loadAudio: function (data, options) {
+                if (options.disabled) {
+                    return data;
+                }
+                var file = data.files[data.index],
+                    url,
+                    audio;
+                if (this._audioElement.canPlayType &&
+                        this._audioElement.canPlayType(file.type) &&
+                        ($.type(options.maxFileSize) !== 'number' ||
+                            file.size <= options.maxFileSize) &&
+                        (!options.fileTypes ||
+                            options.fileTypes.test(file.type))) {
+                    url = loadImage.createObjectURL(file);
+                    if (url) {
+                        audio = this._audioElement.cloneNode(false);
+                        audio.src = url;
+                        audio.controls = true;
+                        data.audio = audio;
+                        return data;
+                    }
+                }
+                return data;
+            },
+
+            // Sets the audio element as a property of the file object:
+            setAudio: function (data, options) {
+                if (data.audio && !options.disabled) {
+                    data.files[data.index][options.name || 'preview'] = data.audio;
+                }
+                return data;
+            }
+
+        }
+
+    });
+
+}));

+ 324 - 0
app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-image.js

@@ -0,0 +1,324 @@
+/*
+ * jQuery File Upload Image Preview & Resize Plugin
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* jshint nomen:false */
+/* global define, require, window, Blob */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define([
+            'jquery',
+            'load-image',
+            'load-image-meta',
+            'load-image-exif',
+            'canvas-to-blob',
+            './jquery.fileupload-process'
+        ], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS:
+        factory(
+            require('jquery'),
+            require('blueimp-load-image/js/load-image'),
+            require('blueimp-load-image/js/load-image-meta'),
+            require('blueimp-load-image/js/load-image-exif'),
+            require('blueimp-canvas-to-blob'),
+            require('./jquery.fileupload-process')
+        );
+    } else {
+        // Browser globals:
+        factory(
+            window.jQuery,
+            window.loadImage
+        );
+    }
+}(function ($, loadImage) {
+    'use strict';
+
+    // Prepend to the default processQueue:
+    $.blueimp.fileupload.prototype.options.processQueue.unshift(
+        {
+            action: 'loadImageMetaData',
+            disableImageHead: '@',
+            disableExif: '@',
+            disableExifThumbnail: '@',
+            disableExifSub: '@',
+            disableExifGps: '@',
+            disabled: '@disableImageMetaDataLoad'
+        },
+        {
+            action: 'loadImage',
+            // Use the action as prefix for the "@" options:
+            prefix: true,
+            fileTypes: '@',
+            maxFileSize: '@',
+            noRevoke: '@',
+            disabled: '@disableImageLoad'
+        },
+        {
+            action: 'resizeImage',
+            // Use "image" as prefix for the "@" options:
+            prefix: 'image',
+            maxWidth: '@',
+            maxHeight: '@',
+            minWidth: '@',
+            minHeight: '@',
+            crop: '@',
+            orientation: '@',
+            forceResize: '@',
+            disabled: '@disableImageResize'
+        },
+        {
+            action: 'saveImage',
+            quality: '@imageQuality',
+            type: '@imageType',
+            disabled: '@disableImageResize'
+        },
+        {
+            action: 'saveImageMetaData',
+            disabled: '@disableImageMetaDataSave'
+        },
+        {
+            action: 'resizeImage',
+            // Use "preview" as prefix for the "@" options:
+            prefix: 'preview',
+            maxWidth: '@',
+            maxHeight: '@',
+            minWidth: '@',
+            minHeight: '@',
+            crop: '@',
+            orientation: '@',
+            thumbnail: '@',
+            canvas: '@',
+            disabled: '@disableImagePreview'
+        },
+        {
+            action: 'setImage',
+            name: '@imagePreviewName',
+            disabled: '@disableImagePreview'
+        },
+        {
+            action: 'deleteImageReferences',
+            disabled: '@disableImageReferencesDeletion'
+        }
+    );
+
+    // The File Upload Resize plugin extends the fileupload widget
+    // with image resize functionality:
+    $.widget('blueimp.fileupload', $.blueimp.fileupload, {
+
+        options: {
+            // The regular expression for the types of images to load:
+            // matched against the file type:
+            loadImageFileTypes: /^image\/(gif|jpeg|png|svg\+xml)$/,
+            // The maximum file size of images to load:
+            loadImageMaxFileSize: 10000000, // 10MB
+            // The maximum width of resized images:
+            imageMaxWidth: 1920,
+            // The maximum height of resized images:
+            imageMaxHeight: 1080,
+            // Defines the image orientation (1-8) or takes the orientation
+            // value from Exif data if set to true:
+            imageOrientation: false,
+            // Define if resized images should be cropped or only scaled:
+            imageCrop: false,
+            // Disable the resize image functionality by default:
+            disableImageResize: true,
+            // The maximum width of the preview images:
+            previewMaxWidth: 80,
+            // The maximum height of the preview images:
+            previewMaxHeight: 80,
+            // Defines the preview orientation (1-8) or takes the orientation
+            // value from Exif data if set to true:
+            previewOrientation: true,
+            // Create the preview using the Exif data thumbnail:
+            previewThumbnail: true,
+            // Define if preview images should be cropped or only scaled:
+            previewCrop: false,
+            // Define if preview images should be resized as canvas elements:
+            previewCanvas: true
+        },
+
+        processActions: {
+
+            // Loads the image given via data.files and data.index
+            // as img element, if the browser supports the File API.
+            // Accepts the options fileTypes (regular expression)
+            // and maxFileSize (integer) to limit the files to load:
+            loadImage: function (data, options) {
+                if (options.disabled) {
+                    return data;
+                }
+                var that = this,
+                    file = data.files[data.index],
+                    dfd = $.Deferred();
+                if (($.type(options.maxFileSize) === 'number' &&
+                            file.size > options.maxFileSize) ||
+                        (options.fileTypes &&
+                            !options.fileTypes.test(file.type)) ||
+                        !loadImage(
+                            file,
+                            function (img) {
+                                if (img.src) {
+                                    data.img = img;
+                                }
+                                dfd.resolveWith(that, [data]);
+                            },
+                            options
+                        )) {
+                    return data;
+                }
+                return dfd.promise();
+            },
+
+            // Resizes the image given as data.canvas or data.img
+            // and updates data.canvas or data.img with the resized image.
+            // Also stores the resized image as preview property.
+            // Accepts the options maxWidth, maxHeight, minWidth,
+            // minHeight, canvas and crop:
+            resizeImage: function (data, options) {
+                if (options.disabled || !(data.canvas || data.img)) {
+                    return data;
+                }
+                options = $.extend({canvas: true}, options);
+                var that = this,
+                    dfd = $.Deferred(),
+                    img = (options.canvas && data.canvas) || data.img,
+                    resolve = function (newImg) {
+                        if (newImg && (newImg.width !== img.width ||
+                                newImg.height !== img.height ||
+                                options.forceResize)) {
+                            data[newImg.getContext ? 'canvas' : 'img'] = newImg;
+                        }
+                        data.preview = newImg;
+                        dfd.resolveWith(that, [data]);
+                    },
+                    thumbnail;
+                if (data.exif) {
+                    if (options.orientation === true) {
+                        options.orientation = data.exif.get('Orientation');
+                    }
+                    if (options.thumbnail) {
+                        thumbnail = data.exif.get('Thumbnail');
+                        if (thumbnail) {
+                            loadImage(thumbnail, resolve, options);
+                            return dfd.promise();
+                        }
+                    }
+                    // Prevent orienting the same image twice:
+                    if (data.orientation) {
+                        delete options.orientation;
+                    } else {
+                        data.orientation = options.orientation;
+                    }
+                }
+                if (img) {
+                    resolve(loadImage.scale(img, options));
+                    return dfd.promise();
+                }
+                return data;
+            },
+
+            // Saves the processed image given as data.canvas
+            // inplace at data.index of data.files:
+            saveImage: function (data, options) {
+                if (!data.canvas || options.disabled) {
+                    return data;
+                }
+                var that = this,
+                    file = data.files[data.index],
+                    dfd = $.Deferred();
+                if (data.canvas.toBlob) {
+                    data.canvas.toBlob(
+                        function (blob) {
+                            if (!blob.name) {
+                                if (file.type === blob.type) {
+                                    blob.name = file.name;
+                                } else if (file.name) {
+                                    blob.name = file.name.replace(
+                                        /\.\w+$/,
+                                        '.' + blob.type.substr(6)
+                                    );
+                                }
+                            }
+                            // Don't restore invalid meta data:
+                            if (file.type !== blob.type) {
+                                delete data.imageHead;
+                            }
+                            // Store the created blob at the position
+                            // of the original file in the files list:
+                            data.files[data.index] = blob;
+                            dfd.resolveWith(that, [data]);
+                        },
+                        options.type || file.type,
+                        options.quality
+                    );
+                } else {
+                    return data;
+                }
+                return dfd.promise();
+            },
+
+            loadImageMetaData: function (data, options) {
+                if (options.disabled) {
+                    return data;
+                }
+                var that = this,
+                    dfd = $.Deferred();
+                loadImage.parseMetaData(data.files[data.index], function (result) {
+                    $.extend(data, result);
+                    dfd.resolveWith(that, [data]);
+                }, options);
+                return dfd.promise();
+            },
+
+            saveImageMetaData: function (data, options) {
+                if (!(data.imageHead && data.canvas &&
+                        data.canvas.toBlob && !options.disabled)) {
+                    return data;
+                }
+                var file = data.files[data.index],
+                    blob = new Blob([
+                        data.imageHead,
+                        // Resized images always have a head size of 20 bytes,
+                        // including the JPEG marker and a minimal JFIF header:
+                        this._blobSlice.call(file, 20)
+                    ], {type: file.type});
+                blob.name = file.name;
+                data.files[data.index] = blob;
+                return data;
+            },
+
+            // Sets the resized version of the image as a property of the
+            // file object, must be called after "saveImage":
+            setImage: function (data, options) {
+                if (data.preview && !options.disabled) {
+                    data.files[data.index][options.name || 'preview'] = data.preview;
+                }
+                return data;
+            },
+
+            deleteImageReferences: function (data, options) {
+                if (!options.disabled) {
+                    delete data.img;
+                    delete data.canvas;
+                    delete data.preview;
+                    delete data.imageHead;
+                }
+                return data;
+            }
+
+        }
+
+    });
+
+}));

+ 155 - 0
app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-jquery-ui.js

@@ -0,0 +1,155 @@
+/*
+ * jQuery File Upload jQuery UI Plugin
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* jshint nomen:false */
+/* global define, require, window */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define(['jquery', './jquery.fileupload-ui'], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS:
+        factory(require('jquery'));
+    } else {
+        // Browser globals:
+        factory(window.jQuery);
+    }
+}(function ($) {
+    'use strict';
+
+    $.widget('blueimp.fileupload', $.blueimp.fileupload, {
+
+        options: {
+            processdone: function (e, data) {
+                data.context.find('.start').button('enable');
+            },
+            progress: function (e, data) {
+                if (data.context) {
+                    data.context.find('.progress').progressbar(
+                        'option',
+                        'value',
+                        parseInt(data.loaded / data.total * 100, 10)
+                    );
+                }
+            },
+            progressall: function (e, data) {
+                var $this = $(this);
+                $this.find('.fileupload-progress')
+                    .find('.progress').progressbar(
+                        'option',
+                        'value',
+                        parseInt(data.loaded / data.total * 100, 10)
+                    ).end()
+                    .find('.progress-extended').each(function () {
+                        $(this).html(
+                            ($this.data('blueimp-fileupload') ||
+                                    $this.data('fileupload'))
+                                ._renderExtendedProgress(data)
+                        );
+                    });
+            }
+        },
+
+        _renderUpload: function (func, files) {
+            var node = this._super(func, files),
+                showIconText = $(window).width() > 480;
+            node.find('.progress').empty().progressbar();
+            node.find('.start').button({
+                icons: {primary: 'ui-icon-circle-arrow-e'},
+                text: showIconText
+            });
+            node.find('.cancel').button({
+                icons: {primary: 'ui-icon-cancel'},
+                text: showIconText
+            });
+            if (node.hasClass('fade')) {
+                node.hide();
+            }
+            return node;
+        },
+
+        _renderDownload: function (func, files) {
+            var node = this._super(func, files),
+                showIconText = $(window).width() > 480;
+            node.find('.delete').button({
+                icons: {primary: 'ui-icon-trash'},
+                text: showIconText
+            });
+            if (node.hasClass('fade')) {
+                node.hide();
+            }
+            return node;
+        },
+
+        _startHandler: function (e) {
+            $(e.currentTarget).button('disable');
+            this._super(e);
+        },
+
+        _transition: function (node) {
+            var deferred = $.Deferred();
+            if (node.hasClass('fade')) {
+                node.fadeToggle(
+                    this.options.transitionDuration,
+                    this.options.transitionEasing,
+                    function () {
+                        deferred.resolveWith(node);
+                    }
+                );
+            } else {
+                deferred.resolveWith(node);
+            }
+            return deferred;
+        },
+
+        _create: function () {
+            this._super();
+            this.element
+                .find('.fileupload-buttonbar')
+                .find('.fileinput-button').each(function () {
+                    var input = $(this).find('input:file').detach();
+                    $(this)
+                        .button({icons: {primary: 'ui-icon-plusthick'}})
+                        .append(input);
+                })
+                .end().find('.start')
+                .button({icons: {primary: 'ui-icon-circle-arrow-e'}})
+                .end().find('.cancel')
+                .button({icons: {primary: 'ui-icon-cancel'}})
+                .end().find('.delete')
+                .button({icons: {primary: 'ui-icon-trash'}})
+                .end().find('.progress').progressbar();
+        },
+
+        _destroy: function () {
+            this.element
+                .find('.fileupload-buttonbar')
+                .find('.fileinput-button').each(function () {
+                    var input = $(this).find('input:file').detach();
+                    $(this)
+                        .button('destroy')
+                        .append(input);
+                })
+                .end().find('.start')
+                .button('destroy')
+                .end().find('.cancel')
+                .button('destroy')
+                .end().find('.delete')
+                .button('destroy')
+                .end().find('.progress').progressbar('destroy');
+            this._super();
+        }
+
+    });
+
+}));

+ 175 - 0
app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-process.js

@@ -0,0 +1,175 @@
+/*
+ * jQuery File Upload Processing Plugin
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2012, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* jshint nomen:false */
+/* global define, require, window */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define([
+            'jquery',
+            './jquery.fileupload'
+        ], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS:
+        factory(require('jquery'));
+    } else {
+        // Browser globals:
+        factory(
+            window.jQuery
+        );
+    }
+}(function ($) {
+    'use strict';
+
+    var originalAdd = $.blueimp.fileupload.prototype.options.add;
+
+    // The File Upload Processing plugin extends the fileupload widget
+    // with file processing functionality:
+    $.widget('blueimp.fileupload', $.blueimp.fileupload, {
+
+        options: {
+            // The list of processing actions:
+            processQueue: [
+                /*
+                {
+                    action: 'log',
+                    type: 'debug'
+                }
+                */
+            ],
+            add: function (e, data) {
+                var $this = $(this);
+                data.process(function () {
+                    return $this.fileupload('process', data);
+                });
+                originalAdd.call(this, e, data);
+            }
+        },
+
+        processActions: {
+            /*
+            log: function (data, options) {
+                console[options.type](
+                    'Processing "' + data.files[data.index].name + '"'
+                );
+            }
+            */
+        },
+
+        _processFile: function (data, originalData) {
+            var that = this,
+                dfd = $.Deferred().resolveWith(that, [data]),
+                chain = dfd.promise();
+            this._trigger('process', null, data);
+            $.each(data.processQueue, function (i, settings) {
+                var func = function (data) {
+                    if (originalData.errorThrown) {
+                        return $.Deferred()
+                                .rejectWith(that, [originalData]).promise();
+                    }
+                    return that.processActions[settings.action].call(
+                        that,
+                        data,
+                        settings
+                    );
+                };
+                chain = chain.pipe(func, settings.always && func);
+            });
+            chain
+                .done(function () {
+                    that._trigger('processdone', null, data);
+                    that._trigger('processalways', null, data);
+                })
+                .fail(function () {
+                    that._trigger('processfail', null, data);
+                    that._trigger('processalways', null, data);
+                });
+            return chain;
+        },
+
+        // Replaces the settings of each processQueue item that
+        // are strings starting with an "@", using the remaining
+        // substring as key for the option map,
+        // e.g. "@autoUpload" is replaced with options.autoUpload:
+        _transformProcessQueue: function (options) {
+            var processQueue = [];
+            $.each(options.processQueue, function () {
+                var settings = {},
+                    action = this.action,
+                    prefix = this.prefix === true ? action : this.prefix;
+                $.each(this, function (key, value) {
+                    if ($.type(value) === 'string' &&
+                            value.charAt(0) === '@') {
+                        settings[key] = options[
+                            value.slice(1) || (prefix ? prefix +
+                                key.charAt(0).toUpperCase() + key.slice(1) : key)
+                        ];
+                    } else {
+                        settings[key] = value;
+                    }
+
+                });
+                processQueue.push(settings);
+            });
+            options.processQueue = processQueue;
+        },
+
+        // Returns the number of files currently in the processsing queue:
+        processing: function () {
+            return this._processing;
+        },
+
+        // Processes the files given as files property of the data parameter,
+        // returns a Promise object that allows to bind callbacks:
+        process: function (data) {
+            var that = this,
+                options = $.extend({}, this.options, data);
+            if (options.processQueue && options.processQueue.length) {
+                this._transformProcessQueue(options);
+                if (this._processing === 0) {
+                    this._trigger('processstart');
+                }
+                $.each(data.files, function (index) {
+                    var opts = index ? $.extend({}, options) : options,
+                        func = function () {
+                            if (data.errorThrown) {
+                                return $.Deferred()
+                                        .rejectWith(that, [data]).promise();
+                            }
+                            return that._processFile(opts, data);
+                        };
+                    opts.index = index;
+                    that._processing += 1;
+                    that._processingQueue = that._processingQueue.pipe(func, func)
+                        .always(function () {
+                            that._processing -= 1;
+                            if (that._processing === 0) {
+                                that._trigger('processstop');
+                            }
+                        });
+                });
+            }
+            return this._processingQueue;
+        },
+
+        _create: function () {
+            this._super();
+            this._processing = 0;
+            this._processingQueue = $.Deferred().resolveWith(this)
+                .promise();
+        }
+
+    });
+
+}));

+ 710 - 0
app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-ui.js

@@ -0,0 +1,710 @@
+/*
+ * jQuery File Upload User Interface Plugin
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* jshint nomen:false */
+/* global define, require, window */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define([
+            'jquery',
+            'tmpl',
+            './jquery.fileupload-image',
+            './jquery.fileupload-audio',
+            './jquery.fileupload-video',
+            './jquery.fileupload-validate'
+        ], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS:
+        factory(
+            require('jquery'),
+            require('tmpl')
+        );
+    } else {
+        // Browser globals:
+        factory(
+            window.jQuery,
+            window.tmpl
+        );
+    }
+}(function ($, tmpl) {
+    'use strict';
+
+    $.blueimp.fileupload.prototype._specialOptions.push(
+        'filesContainer',
+        'uploadTemplateId',
+        'downloadTemplateId'
+    );
+
+    // The UI version extends the file upload widget
+    // and adds complete user interface interaction:
+    $.widget('blueimp.fileupload', $.blueimp.fileupload, {
+
+        options: {
+            // By default, files added to the widget are uploaded as soon
+            // as the user clicks on the start buttons. To enable automatic
+            // uploads, set the following option to true:
+            autoUpload: false,
+            // The ID of the upload template:
+            uploadTemplateId: 'template-upload',
+            // The ID of the download template:
+            downloadTemplateId: 'template-download',
+            // The container for the list of files. If undefined, it is set to
+            // an element with class "files" inside of the widget element:
+            filesContainer: undefined,
+            // By default, files are appended to the files container.
+            // Set the following option to true, to prepend files instead:
+            prependFiles: false,
+            // The expected data type of the upload response, sets the dataType
+            // option of the $.ajax upload requests:
+            dataType: 'json',
+
+            // Error and info messages:
+            messages: {
+                unknownError: 'Unknown error'
+            },
+
+            // Function returning the current number of files,
+            // used by the maxNumberOfFiles validation:
+            getNumberOfFiles: function () {
+                return this.filesContainer.children()
+                    .not('.processing').length;
+            },
+
+            // Callback to retrieve the list of files from the server response:
+            getFilesFromResponse: function (data) {
+                if (data.result && $.isArray(data.result.files)) {
+                    return data.result.files;
+                }
+                return [];
+            },
+
+            // The add callback is invoked as soon as files are added to the fileupload
+            // widget (via file input selection, drag & drop or add API call).
+            // See the basic file upload widget for more information:
+            add: function (e, data) {
+                if (e.isDefaultPrevented()) {
+                    return false;
+                }
+                var $this = $(this),
+                    that = $this.data('blueimp-fileupload') ||
+                        $this.data('fileupload'),
+                    options = that.options;
+                data.context = that._renderUpload(data.files)
+                    .data('data', data)
+                    .addClass('processing');
+                options.filesContainer[
+                    options.prependFiles ? 'prepend' : 'append'
+                ](data.context);
+                that._forceReflow(data.context);
+                that._transition(data.context);
+                data.process(function () {
+                    return $this.fileupload('process', data);
+                }).always(function () {
+                    data.context.each(function (index) {
+                        $(this).find('.size').text(
+                            that._formatFileSize(data.files[index].size)
+                        );
+                    }).removeClass('processing');
+                    that._renderPreviews(data);
+                }).done(function () {
+                    data.context.find('.start').prop('disabled', false);
+                    if ((that._trigger('added', e, data) !== false) &&
+                            (options.autoUpload || data.autoUpload) &&
+                            data.autoUpload !== false) {
+                        data.submit();
+                    }
+                }).fail(function () {
+                    if (data.files.error) {
+                        data.context.each(function (index) {
+                            var error = data.files[index].error;
+                            if (error) {
+                                $(this).find('.error').text(error);
+                            }
+                        });
+                    }
+                });
+            },
+            // Callback for the start of each file upload request:
+            send: function (e, data) {
+                if (e.isDefaultPrevented()) {
+                    return false;
+                }
+                var that = $(this).data('blueimp-fileupload') ||
+                        $(this).data('fileupload');
+                if (data.context && data.dataType &&
+                        data.dataType.substr(0, 6) === 'iframe') {
+                    // Iframe Transport does not support progress events.
+                    // In lack of an indeterminate progress bar, we set
+                    // the progress to 100%, showing the full animated bar:
+                    data.context
+                        .find('.progress').addClass(
+                            !$.support.transition && 'progress-animated'
+                        )
+                        .attr('aria-valuenow', 100)
+                        .children().first().css(
+                            'width',
+                            '100%'
+                        );
+                }
+                return that._trigger('sent', e, data);
+            },
+            // Callback for successful uploads:
+            done: function (e, data) {
+                if (e.isDefaultPrevented()) {
+                    return false;
+                }
+                var that = $(this).data('blueimp-fileupload') ||
+                        $(this).data('fileupload'),
+                    getFilesFromResponse = data.getFilesFromResponse ||
+                        that.options.getFilesFromResponse,
+                    files = getFilesFromResponse(data),
+                    template,
+                    deferred;
+                if (data.context) {
+                    data.context.each(function (index) {
+                        var file = files[index] ||
+                                {error: 'Empty file upload result'};
+                        deferred = that._addFinishedDeferreds();
+                        that._transition($(this)).done(
+                            function () {
+                                var node = $(this);
+                                template = that._renderDownload([file])
+                                    .replaceAll(node);
+                                that._forceReflow(template);
+                                that._transition(template).done(
+                                    function () {
+                                        data.context = $(this);
+                                        that._trigger('completed', e, data);
+                                        that._trigger('finished', e, data);
+                                        deferred.resolve();
+                                    }
+                                );
+                            }
+                        );
+                    });
+                } else {
+                    template = that._renderDownload(files)[
+                        that.options.prependFiles ? 'prependTo' : 'appendTo'
+                    ](that.options.filesContainer);
+                    that._forceReflow(template);
+                    deferred = that._addFinishedDeferreds();
+                    that._transition(template).done(
+                        function () {
+                            data.context = $(this);
+                            that._trigger('completed', e, data);
+                            that._trigger('finished', e, data);
+                            deferred.resolve();
+                        }
+                    );
+                }
+            },
+            // Callback for failed (abort or error) uploads:
+            fail: function (e, data) {
+                if (e.isDefaultPrevented()) {
+                    return false;
+                }
+                var that = $(this).data('blueimp-fileupload') ||
+                        $(this).data('fileupload'),
+                    template,
+                    deferred;
+                if (data.context) {
+                    data.context.each(function (index) {
+                        if (data.errorThrown !== 'abort') {
+                            var file = data.files[index];
+                            file.error = file.error || data.errorThrown ||
+                                data.i18n('unknownError');
+                            deferred = that._addFinishedDeferreds();
+                            that._transition($(this)).done(
+                                function () {
+                                    var node = $(this);
+                                    template = that._renderDownload([file])
+                                        .replaceAll(node);
+                                    that._forceReflow(template);
+                                    that._transition(template).done(
+                                        function () {
+                                            data.context = $(this);
+                                            that._trigger('failed', e, data);
+                                            that._trigger('finished', e, data);
+                                            deferred.resolve();
+                                        }
+                                    );
+                                }
+                            );
+                        } else {
+                            deferred = that._addFinishedDeferreds();
+                            that._transition($(this)).done(
+                                function () {
+                                    $(this).remove();
+                                    that._trigger('failed', e, data);
+                                    that._trigger('finished', e, data);
+                                    deferred.resolve();
+                                }
+                            );
+                        }
+                    });
+                } else if (data.errorThrown !== 'abort') {
+                    data.context = that._renderUpload(data.files)[
+                        that.options.prependFiles ? 'prependTo' : 'appendTo'
+                    ](that.options.filesContainer)
+                        .data('data', data);
+                    that._forceReflow(data.context);
+                    deferred = that._addFinishedDeferreds();
+                    that._transition(data.context).done(
+                        function () {
+                            data.context = $(this);
+                            that._trigger('failed', e, data);
+                            that._trigger('finished', e, data);
+                            deferred.resolve();
+                        }
+                    );
+                } else {
+                    that._trigger('failed', e, data);
+                    that._trigger('finished', e, data);
+                    that._addFinishedDeferreds().resolve();
+                }
+            },
+            // Callback for upload progress events:
+            progress: function (e, data) {
+                if (e.isDefaultPrevented()) {
+                    return false;
+                }
+                var progress = Math.floor(data.loaded / data.total * 100);
+                if (data.context) {
+                    data.context.each(function () {
+                        $(this).find('.progress')
+                            .attr('aria-valuenow', progress)
+                            .children().first().css(
+                                'width',
+                                progress + '%'
+                            );
+                    });
+                }
+            },
+            // Callback for global upload progress events:
+            progressall: function (e, data) {
+                if (e.isDefaultPrevented()) {
+                    return false;
+                }
+                var $this = $(this),
+                    progress = Math.floor(data.loaded / data.total * 100),
+                    globalProgressNode = $this.find('.fileupload-progress'),
+                    extendedProgressNode = globalProgressNode
+                        .find('.progress-extended');
+                if (extendedProgressNode.length) {
+                    extendedProgressNode.html(
+                        ($this.data('blueimp-fileupload') || $this.data('fileupload'))
+                            ._renderExtendedProgress(data)
+                    );
+                }
+                globalProgressNode
+                    .find('.progress')
+                    .attr('aria-valuenow', progress)
+                    .children().first().css(
+                        'width',
+                        progress + '%'
+                    );
+            },
+            // Callback for uploads start, equivalent to the global ajaxStart event:
+            start: function (e) {
+                if (e.isDefaultPrevented()) {
+                    return false;
+                }
+                var that = $(this).data('blueimp-fileupload') ||
+                        $(this).data('fileupload');
+                that._resetFinishedDeferreds();
+                that._transition($(this).find('.fileupload-progress')).done(
+                    function () {
+                        that._trigger('started', e);
+                    }
+                );
+            },
+            // Callback for uploads stop, equivalent to the global ajaxStop event:
+            stop: function (e) {
+                if (e.isDefaultPrevented()) {
+                    return false;
+                }
+                var that = $(this).data('blueimp-fileupload') ||
+                        $(this).data('fileupload'),
+                    deferred = that._addFinishedDeferreds();
+                $.when.apply($, that._getFinishedDeferreds())
+                    .done(function () {
+                        that._trigger('stopped', e);
+                    });
+                that._transition($(this).find('.fileupload-progress')).done(
+                    function () {
+                        $(this).find('.progress')
+                            .attr('aria-valuenow', '0')
+                            .children().first().css('width', '0%');
+                        $(this).find('.progress-extended').html('&nbsp;');
+                        deferred.resolve();
+                    }
+                );
+            },
+            processstart: function (e) {
+                if (e.isDefaultPrevented()) {
+                    return false;
+                }
+                $(this).addClass('fileupload-processing');
+            },
+            processstop: function (e) {
+                if (e.isDefaultPrevented()) {
+                    return false;
+                }
+                $(this).removeClass('fileupload-processing');
+            },
+            // Callback for file deletion:
+            destroy: function (e, data) {
+                if (e.isDefaultPrevented()) {
+                    return false;
+                }
+                var that = $(this).data('blueimp-fileupload') ||
+                        $(this).data('fileupload'),
+                    removeNode = function () {
+                        that._transition(data.context).done(
+                            function () {
+                                $(this).remove();
+                                that._trigger('destroyed', e, data);
+                            }
+                        );
+                    };
+                if (data.url) {
+                    data.dataType = data.dataType || that.options.dataType;
+                    $.ajax(data).done(removeNode).fail(function () {
+                        that._trigger('destroyfailed', e, data);
+                    });
+                } else {
+                    removeNode();
+                }
+            }
+        },
+
+        _resetFinishedDeferreds: function () {
+            this._finishedUploads = [];
+        },
+
+        _addFinishedDeferreds: function (deferred) {
+            if (!deferred) {
+                deferred = $.Deferred();
+            }
+            this._finishedUploads.push(deferred);
+            return deferred;
+        },
+
+        _getFinishedDeferreds: function () {
+            return this._finishedUploads;
+        },
+
+        // Link handler, that allows to download files
+        // by drag & drop of the links to the desktop:
+        _enableDragToDesktop: function () {
+            var link = $(this),
+                url = link.prop('href'),
+                name = link.prop('download'),
+                type = 'application/octet-stream';
+            link.bind('dragstart', function (e) {
+                try {
+                    e.originalEvent.dataTransfer.setData(
+                        'DownloadURL',
+                        [type, name, url].join(':')
+                    );
+                } catch (ignore) {}
+            });
+        },
+
+        _formatFileSize: function (bytes) {
+            if (typeof bytes !== 'number') {
+                return '';
+            }
+            if (bytes >= 1000000000) {
+                return (bytes / 1000000000).toFixed(2) + ' GB';
+            }
+            if (bytes >= 1000000) {
+                return (bytes / 1000000).toFixed(2) + ' MB';
+            }
+            return (bytes / 1000).toFixed(2) + ' KB';
+        },
+
+        _formatBitrate: function (bits) {
+            if (typeof bits !== 'number') {
+                return '';
+            }
+            if (bits >= 1000000000) {
+                return (bits / 1000000000).toFixed(2) + ' Gbit/s';
+            }
+            if (bits >= 1000000) {
+                return (bits / 1000000).toFixed(2) + ' Mbit/s';
+            }
+            if (bits >= 1000) {
+                return (bits / 1000).toFixed(2) + ' kbit/s';
+            }
+            return bits.toFixed(2) + ' bit/s';
+        },
+
+        _formatTime: function (seconds) {
+            var date = new Date(seconds * 1000),
+                days = Math.floor(seconds / 86400);
+            days = days ? days + 'd ' : '';
+            return days +
+                ('0' + date.getUTCHours()).slice(-2) + ':' +
+                ('0' + date.getUTCMinutes()).slice(-2) + ':' +
+                ('0' + date.getUTCSeconds()).slice(-2);
+        },
+
+        _formatPercentage: function (floatValue) {
+            return (floatValue * 100).toFixed(2) + ' %';
+        },
+
+        _renderExtendedProgress: function (data) {
+            return this._formatBitrate(data.bitrate) + ' | ' +
+                this._formatTime(
+                    (data.total - data.loaded) * 8 / data.bitrate
+                ) + ' | ' +
+                this._formatPercentage(
+                    data.loaded / data.total
+                ) + ' | ' +
+                this._formatFileSize(data.loaded) + ' / ' +
+                this._formatFileSize(data.total);
+        },
+
+        _renderTemplate: function (func, files) {
+            if (!func) {
+                return $();
+            }
+            var result = func({
+                files: files,
+                formatFileSize: this._formatFileSize,
+                options: this.options
+            });
+            if (result instanceof $) {
+                return result;
+            }
+            return $(this.options.templatesContainer).html(result).children();
+        },
+
+        _renderPreviews: function (data) {
+            data.context.find('.preview').each(function (index, elm) {
+                $(elm).append(data.files[index].preview);
+            });
+        },
+
+        _renderUpload: function (files) {
+            return this._renderTemplate(
+                this.options.uploadTemplate,
+                files
+            );
+        },
+
+        _renderDownload: function (files) {
+            return this._renderTemplate(
+                this.options.downloadTemplate,
+                files
+            ).find('a[download]').each(this._enableDragToDesktop).end();
+        },
+
+        _startHandler: function (e) {
+            e.preventDefault();
+            var button = $(e.currentTarget),
+                template = button.closest('.template-upload'),
+                data = template.data('data');
+            button.prop('disabled', true);
+            if (data && data.submit) {
+                data.submit();
+            }
+        },
+
+        _cancelHandler: function (e) {
+            e.preventDefault();
+            var template = $(e.currentTarget)
+                    .closest('.template-upload,.template-download'),
+                data = template.data('data') || {};
+            data.context = data.context || template;
+            if (data.abort) {
+                data.abort();
+            } else {
+                data.errorThrown = 'abort';
+                this._trigger('fail', e, data);
+            }
+        },
+
+        _deleteHandler: function (e) {
+            e.preventDefault();
+            var button = $(e.currentTarget);
+            this._trigger('destroy', e, $.extend({
+                context: button.closest('.template-download'),
+                type: 'DELETE'
+            }, button.data()));
+        },
+
+        _forceReflow: function (node) {
+            return $.support.transition && node.length &&
+                node[0].offsetWidth;
+        },
+
+        _transition: function (node) {
+            var dfd = $.Deferred();
+            if ($.support.transition && node.hasClass('fade') && node.is(':visible')) {
+                node.bind(
+                    $.support.transition.end,
+                    function (e) {
+                        // Make sure we don't respond to other transitions events
+                        // in the container element, e.g. from button elements:
+                        if (e.target === node[0]) {
+                            node.unbind($.support.transition.end);
+                            dfd.resolveWith(node);
+                        }
+                    }
+                ).toggleClass('in');
+            } else {
+                node.toggleClass('in');
+                dfd.resolveWith(node);
+            }
+            return dfd;
+        },
+
+        _initButtonBarEventHandlers: function () {
+            var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
+                filesList = this.options.filesContainer;
+            this._on(fileUploadButtonBar.find('.start'), {
+                click: function (e) {
+                    e.preventDefault();
+                    filesList.find('.start').click();
+                }
+            });
+            this._on(fileUploadButtonBar.find('.cancel'), {
+                click: function (e) {
+                    e.preventDefault();
+                    filesList.find('.cancel').click();
+                }
+            });
+            this._on(fileUploadButtonBar.find('.delete'), {
+                click: function (e) {
+                    e.preventDefault();
+                    filesList.find('.toggle:checked')
+                        .closest('.template-download')
+                        .find('.delete').click();
+                    fileUploadButtonBar.find('.toggle')
+                        .prop('checked', false);
+                }
+            });
+            this._on(fileUploadButtonBar.find('.toggle'), {
+                change: function (e) {
+                    filesList.find('.toggle').prop(
+                        'checked',
+                        $(e.currentTarget).is(':checked')
+                    );
+                }
+            });
+        },
+
+        _destroyButtonBarEventHandlers: function () {
+            this._off(
+                this.element.find('.fileupload-buttonbar')
+                    .find('.start, .cancel, .delete'),
+                'click'
+            );
+            this._off(
+                this.element.find('.fileupload-buttonbar .toggle'),
+                'change.'
+            );
+        },
+
+        _initEventHandlers: function () {
+            this._super();
+            this._on(this.options.filesContainer, {
+                'click .start': this._startHandler,
+                'click .cancel': this._cancelHandler,
+                'click .delete': this._deleteHandler
+            });
+            this._initButtonBarEventHandlers();
+        },
+
+        _destroyEventHandlers: function () {
+            this._destroyButtonBarEventHandlers();
+            this._off(this.options.filesContainer, 'click');
+            this._super();
+        },
+
+        _enableFileInputButton: function () {
+            this.element.find('.fileinput-button input')
+                .prop('disabled', false)
+                .parent().removeClass('disabled');
+        },
+
+        _disableFileInputButton: function () {
+            this.element.find('.fileinput-button input')
+                .prop('disabled', true)
+                .parent().addClass('disabled');
+        },
+
+        _initTemplates: function () {
+            var options = this.options;
+            options.templatesContainer = this.document[0].createElement(
+                options.filesContainer.prop('nodeName')
+            );
+            if (tmpl) {
+                if (options.uploadTemplateId) {
+                    options.uploadTemplate = tmpl(options.uploadTemplateId);
+                }
+                if (options.downloadTemplateId) {
+                    options.downloadTemplate = tmpl(options.downloadTemplateId);
+                }
+            }
+        },
+
+        _initFilesContainer: function () {
+            var options = this.options;
+            if (options.filesContainer === undefined) {
+                options.filesContainer = this.element.find('.files');
+            } else if (!(options.filesContainer instanceof $)) {
+                options.filesContainer = $(options.filesContainer);
+            }
+        },
+
+        _initSpecialOptions: function () {
+            this._super();
+            this._initFilesContainer();
+            this._initTemplates();
+        },
+
+        _create: function () {
+            this._super();
+            this._resetFinishedDeferreds();
+            if (!$.support.fileInput) {
+                this._disableFileInputButton();
+            }
+        },
+
+        enable: function () {
+            var wasDisabled = false;
+            if (this.options.disabled) {
+                wasDisabled = true;
+            }
+            this._super();
+            if (wasDisabled) {
+                this.element.find('input, button').prop('disabled', false);
+                this._enableFileInputButton();
+            }
+        },
+
+        disable: function () {
+            if (!this.options.disabled) {
+                this.element.find('input, button').prop('disabled', true);
+                this._disableFileInputButton();
+            }
+            this._super();
+        }
+
+    });
+
+}));

+ 122 - 0
app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-validate.js

@@ -0,0 +1,122 @@
+/*
+ * jQuery File Upload Validation Plugin
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* global define, require, window */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define([
+            'jquery',
+            './jquery.fileupload-process'
+        ], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS:
+        factory(require('jquery'));
+    } else {
+        // Browser globals:
+        factory(
+            window.jQuery
+        );
+    }
+}(function ($) {
+    'use strict';
+
+    // Append to the default processQueue:
+    $.blueimp.fileupload.prototype.options.processQueue.push(
+        {
+            action: 'validate',
+            // Always trigger this action,
+            // even if the previous action was rejected:
+            always: true,
+            // Options taken from the global options map:
+            acceptFileTypes: '@',
+            maxFileSize: '@',
+            minFileSize: '@',
+            maxNumberOfFiles: '@',
+            disabled: '@disableValidation'
+        }
+    );
+
+    // The File Upload Validation plugin extends the fileupload widget
+    // with file validation functionality:
+    $.widget('blueimp.fileupload', $.blueimp.fileupload, {
+
+        options: {
+            /*
+            // The regular expression for allowed file types, matches
+            // against either file type or file name:
+            acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
+            // The maximum allowed file size in bytes:
+            maxFileSize: 10000000, // 10 MB
+            // The minimum allowed file size in bytes:
+            minFileSize: undefined, // No minimal file size
+            // The limit of files to be uploaded:
+            maxNumberOfFiles: 10,
+            */
+
+            // Function returning the current number of files,
+            // has to be overriden for maxNumberOfFiles validation:
+            getNumberOfFiles: $.noop,
+
+            // Error and info messages:
+            messages: {
+                maxNumberOfFiles: 'Maximum number of files exceeded',
+                acceptFileTypes: 'File type not allowed',
+                maxFileSize: 'File is too large',
+                minFileSize: 'File is too small'
+            }
+        },
+
+        processActions: {
+
+            validate: function (data, options) {
+                if (options.disabled) {
+                    return data;
+                }
+                var dfd = $.Deferred(),
+                    settings = this.options,
+                    file = data.files[data.index],
+                    fileSize;
+                if (options.minFileSize || options.maxFileSize) {
+                    fileSize = file.size;
+                }
+                if ($.type(options.maxNumberOfFiles) === 'number' &&
+                        (settings.getNumberOfFiles() || 0) + data.files.length >
+                            options.maxNumberOfFiles) {
+                    file.error = settings.i18n('maxNumberOfFiles');
+                } else if (options.acceptFileTypes &&
+                        !(options.acceptFileTypes.test(file.type) ||
+                        options.acceptFileTypes.test(file.name))) {
+                    file.error = settings.i18n('acceptFileTypes');
+                } else if (fileSize > options.maxFileSize) {
+                    file.error = settings.i18n('maxFileSize');
+                } else if ($.type(fileSize) === 'number' &&
+                        fileSize < options.minFileSize) {
+                    file.error = settings.i18n('minFileSize');
+                } else {
+                    delete file.error;
+                }
+                if (file.error || data.files.error) {
+                    data.files.error = true;
+                    dfd.rejectWith(this, [data]);
+                } else {
+                    dfd.resolveWith(this, [data]);
+                }
+                return dfd.promise();
+            }
+
+        }
+
+    });
+
+}));

+ 112 - 0
app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload-video.js

@@ -0,0 +1,112 @@
+/*
+ * jQuery File Upload Video Preview Plugin
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2013, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* jshint nomen:false */
+/* global define, require, window, document */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define([
+            'jquery',
+            'load-image',
+            './jquery.fileupload-process'
+        ], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS:
+        factory(
+            require('jquery'),
+            require('load-image')
+        );
+    } else {
+        // Browser globals:
+        factory(
+            window.jQuery,
+            window.loadImage
+        );
+    }
+}(function ($, loadImage) {
+    'use strict';
+
+    // Prepend to the default processQueue:
+    $.blueimp.fileupload.prototype.options.processQueue.unshift(
+        {
+            action: 'loadVideo',
+            // Use the action as prefix for the "@" options:
+            prefix: true,
+            fileTypes: '@',
+            maxFileSize: '@',
+            disabled: '@disableVideoPreview'
+        },
+        {
+            action: 'setVideo',
+            name: '@videoPreviewName',
+            disabled: '@disableVideoPreview'
+        }
+    );
+
+    // The File Upload Video Preview plugin extends the fileupload widget
+    // with video preview functionality:
+    $.widget('blueimp.fileupload', $.blueimp.fileupload, {
+
+        options: {
+            // The regular expression for the types of video files to load,
+            // matched against the file type:
+            loadVideoFileTypes: /^video\/.*$/
+        },
+
+        _videoElement: document.createElement('video'),
+
+        processActions: {
+
+            // Loads the video file given via data.files and data.index
+            // as video element if the browser supports playing it.
+            // Accepts the options fileTypes (regular expression)
+            // and maxFileSize (integer) to limit the files to load:
+            loadVideo: function (data, options) {
+                if (options.disabled) {
+                    return data;
+                }
+                var file = data.files[data.index],
+                    url,
+                    video;
+                if (this._videoElement.canPlayType &&
+                        this._videoElement.canPlayType(file.type) &&
+                        ($.type(options.maxFileSize) !== 'number' ||
+                            file.size <= options.maxFileSize) &&
+                        (!options.fileTypes ||
+                            options.fileTypes.test(file.type))) {
+                    url = loadImage.createObjectURL(file);
+                    if (url) {
+                        video = this._videoElement.cloneNode(false);
+                        video.src = url;
+                        video.controls = true;
+                        data.video = video;
+                        return data;
+                    }
+                }
+                return data;
+            },
+
+            // Sets the video element as a property of the file object:
+            setVideo: function (data, options) {
+                if (data.video && !options.disabled) {
+                    data.files[data.index][options.name || 'preview'] = data.video;
+                }
+                return data;
+            }
+
+        }
+
+    });
+
+}));

+ 1477 - 0
app/Resources/public/assets/jquery-file-upload/js/jquery.fileupload.js

@@ -0,0 +1,1477 @@
+/*
+ * jQuery File Upload Plugin
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2010, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* jshint nomen:false */
+/* global define, require, window, document, location, Blob, FormData */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define([
+            'jquery',
+            'jquery.ui.widget'
+        ], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS:
+        factory(
+            require('jquery'),
+            require('./vendor/jquery.ui.widget')
+        );
+    } else {
+        // Browser globals:
+        factory(window.jQuery);
+    }
+}(function ($) {
+    'use strict';
+
+    // Detect file input support, based on
+    // http://viljamis.com/blog/2012/file-upload-support-on-mobile/
+    $.support.fileInput = !(new RegExp(
+        // Handle devices which give false positives for the feature detection:
+        '(Android (1\\.[0156]|2\\.[01]))' +
+            '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' +
+            '|(w(eb)?OSBrowser)|(webOS)' +
+            '|(Kindle/(1\\.0|2\\.[05]|3\\.0))'
+    ).test(window.navigator.userAgent) ||
+        // Feature detection for all other devices:
+        $('<input type="file">').prop('disabled'));
+
+    // The FileReader API is not actually used, but works as feature detection,
+    // as some Safari versions (5?) support XHR file uploads via the FormData API,
+    // but not non-multipart XHR file uploads.
+    // window.XMLHttpRequestUpload is not available on IE10, so we check for
+    // window.ProgressEvent instead to detect XHR2 file upload capability:
+    $.support.xhrFileUpload = !!(window.ProgressEvent && window.FileReader);
+    $.support.xhrFormDataFileUpload = !!window.FormData;
+
+    // Detect support for Blob slicing (required for chunked uploads):
+    $.support.blobSlice = window.Blob && (Blob.prototype.slice ||
+        Blob.prototype.webkitSlice || Blob.prototype.mozSlice);
+
+    // Helper function to create drag handlers for dragover/dragenter/dragleave:
+    function getDragHandler(type) {
+        var isDragOver = type === 'dragover';
+        return function (e) {
+            e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
+            var dataTransfer = e.dataTransfer;
+            if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1 &&
+                    this._trigger(
+                        type,
+                        $.Event(type, {delegatedEvent: e})
+                    ) !== false) {
+                e.preventDefault();
+                if (isDragOver) {
+                    dataTransfer.dropEffect = 'copy';
+                }
+            }
+        };
+    }
+
+    // The fileupload widget listens for change events on file input fields defined
+    // via fileInput setting and paste or drop events of the given dropZone.
+    // In addition to the default jQuery Widget methods, the fileupload widget
+    // exposes the "add" and "send" methods, to add or directly send files using
+    // the fileupload API.
+    // By default, files added via file input selection, paste, drag & drop or
+    // "add" method are uploaded immediately, but it is possible to override
+    // the "add" callback option to queue file uploads.
+    $.widget('blueimp.fileupload', {
+
+        options: {
+            // The drop target element(s), by the default the complete document.
+            // Set to null to disable drag & drop support:
+            dropZone: $(document),
+            // The paste target element(s), by the default undefined.
+            // Set to a DOM node or jQuery object to enable file pasting:
+            pasteZone: undefined,
+            // The file input field(s), that are listened to for change events.
+            // If undefined, it is set to the file input fields inside
+            // of the widget element on plugin initialization.
+            // Set to null to disable the change listener.
+            fileInput: undefined,
+            // By default, the file input field is replaced with a clone after
+            // each input field change event. This is required for iframe transport
+            // queues and allows change events to be fired for the same file
+            // selection, but can be disabled by setting the following option to false:
+            replaceFileInput: true,
+            // The parameter name for the file form data (the request argument name).
+            // If undefined or empty, the name property of the file input field is
+            // used, or "files[]" if the file input name property is also empty,
+            // can be a string or an array of strings:
+            paramName: undefined,
+            // By default, each file of a selection is uploaded using an individual
+            // request for XHR type uploads. Set to false to upload file
+            // selections in one request each:
+            singleFileUploads: true,
+            // To limit the number of files uploaded with one XHR request,
+            // set the following option to an integer greater than 0:
+            limitMultiFileUploads: undefined,
+            // The following option limits the number of files uploaded with one
+            // XHR request to keep the request size under or equal to the defined
+            // limit in bytes:
+            limitMultiFileUploadSize: undefined,
+            // Multipart file uploads add a number of bytes to each uploaded file,
+            // therefore the following option adds an overhead for each file used
+            // in the limitMultiFileUploadSize configuration:
+            limitMultiFileUploadSizeOverhead: 512,
+            // Set the following option to true to issue all file upload requests
+            // in a sequential order:
+            sequentialUploads: false,
+            // To limit the number of concurrent uploads,
+            // set the following option to an integer greater than 0:
+            limitConcurrentUploads: undefined,
+            // Set the following option to true to force iframe transport uploads:
+            forceIframeTransport: false,
+            // Set the following option to the location of a redirect url on the
+            // origin server, for cross-domain iframe transport uploads:
+            redirect: undefined,
+            // The parameter name for the redirect url, sent as part of the form
+            // data and set to 'redirect' if this option is empty:
+            redirectParamName: undefined,
+            // Set the following option to the location of a postMessage window,
+            // to enable postMessage transport uploads:
+            postMessage: undefined,
+            // By default, XHR file uploads are sent as multipart/form-data.
+            // The iframe transport is always using multipart/form-data.
+            // Set to false to enable non-multipart XHR uploads:
+            multipart: true,
+            // To upload large files in smaller chunks, set the following option
+            // to a preferred maximum chunk size. If set to 0, null or undefined,
+            // or the browser does not support the required Blob API, files will
+            // be uploaded as a whole.
+            maxChunkSize: undefined,
+            // When a non-multipart upload or a chunked multipart upload has been
+            // aborted, this option can be used to resume the upload by setting
+            // it to the size of the already uploaded bytes. This option is most
+            // useful when modifying the options object inside of the "add" or
+            // "send" callbacks, as the options are cloned for each file upload.
+            uploadedBytes: undefined,
+            // By default, failed (abort or error) file uploads are removed from the
+            // global progress calculation. Set the following option to false to
+            // prevent recalculating the global progress data:
+            recalculateProgress: true,
+            // Interval in milliseconds to calculate and trigger progress events:
+            progressInterval: 100,
+            // Interval in milliseconds to calculate progress bitrate:
+            bitrateInterval: 500,
+            // By default, uploads are started automatically when adding files:
+            autoUpload: true,
+
+            // Error and info messages:
+            messages: {
+                uploadedBytes: 'Uploaded bytes exceed file size'
+            },
+
+            // Translation function, gets the message key to be translated
+            // and an object with context specific data as arguments:
+            i18n: function (message, context) {
+                message = this.messages[message] || message.toString();
+                if (context) {
+                    $.each(context, function (key, value) {
+                        message = message.replace('{' + key + '}', value);
+                    });
+                }
+                return message;
+            },
+
+            // Additional form data to be sent along with the file uploads can be set
+            // using this option, which accepts an array of objects with name and
+            // value properties, a function returning such an array, a FormData
+            // object (for XHR file uploads), or a simple object.
+            // The form of the first fileInput is given as parameter to the function:
+            formData: function (form) {
+                return form.serializeArray();
+            },
+
+            // The add callback is invoked as soon as files are added to the fileupload
+            // widget (via file input selection, drag & drop, paste or add API call).
+            // If the singleFileUploads option is enabled, this callback will be
+            // called once for each file in the selection for XHR file uploads, else
+            // once for each file selection.
+            //
+            // The upload starts when the submit method is invoked on the data parameter.
+            // The data object contains a files property holding the added files
+            // and allows you to override plugin options as well as define ajax settings.
+            //
+            // Listeners for this callback can also be bound the following way:
+            // .bind('fileuploadadd', func);
+            //
+            // data.submit() returns a Promise object and allows to attach additional
+            // handlers using jQuery's Deferred callbacks:
+            // data.submit().done(func).fail(func).always(func);
+            add: function (e, data) {
+                if (e.isDefaultPrevented()) {
+                    return false;
+                }
+                if (data.autoUpload || (data.autoUpload !== false &&
+                        $(this).fileupload('option', 'autoUpload'))) {
+                    data.process().done(function () {
+                        data.submit();
+                    });
+                }
+            },
+
+            // Other callbacks:
+
+            // Callback for the submit event of each file upload:
+            // submit: function (e, data) {}, // .bind('fileuploadsubmit', func);
+
+            // Callback for the start of each file upload request:
+            // send: function (e, data) {}, // .bind('fileuploadsend', func);
+
+            // Callback for successful uploads:
+            // done: function (e, data) {}, // .bind('fileuploaddone', func);
+
+            // Callback for failed (abort or error) uploads:
+            // fail: function (e, data) {}, // .bind('fileuploadfail', func);
+
+            // Callback for completed (success, abort or error) requests:
+            // always: function (e, data) {}, // .bind('fileuploadalways', func);
+
+            // Callback for upload progress events:
+            // progress: function (e, data) {}, // .bind('fileuploadprogress', func);
+
+            // Callback for global upload progress events:
+            // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);
+
+            // Callback for uploads start, equivalent to the global ajaxStart event:
+            // start: function (e) {}, // .bind('fileuploadstart', func);
+
+            // Callback for uploads stop, equivalent to the global ajaxStop event:
+            // stop: function (e) {}, // .bind('fileuploadstop', func);
+
+            // Callback for change events of the fileInput(s):
+            // change: function (e, data) {}, // .bind('fileuploadchange', func);
+
+            // Callback for paste events to the pasteZone(s):
+            // paste: function (e, data) {}, // .bind('fileuploadpaste', func);
+
+            // Callback for drop events of the dropZone(s):
+            // drop: function (e, data) {}, // .bind('fileuploaddrop', func);
+
+            // Callback for dragover events of the dropZone(s):
+            // dragover: function (e) {}, // .bind('fileuploaddragover', func);
+
+            // Callback for the start of each chunk upload request:
+            // chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func);
+
+            // Callback for successful chunk uploads:
+            // chunkdone: function (e, data) {}, // .bind('fileuploadchunkdone', func);
+
+            // Callback for failed (abort or error) chunk uploads:
+            // chunkfail: function (e, data) {}, // .bind('fileuploadchunkfail', func);
+
+            // Callback for completed (success, abort or error) chunk upload requests:
+            // chunkalways: function (e, data) {}, // .bind('fileuploadchunkalways', func);
+
+            // The plugin options are used as settings object for the ajax calls.
+            // The following are jQuery ajax settings required for the file uploads:
+            processData: false,
+            contentType: false,
+            cache: false,
+            timeout: 0
+        },
+
+        // A list of options that require reinitializing event listeners and/or
+        // special initialization code:
+        _specialOptions: [
+            'fileInput',
+            'dropZone',
+            'pasteZone',
+            'multipart',
+            'forceIframeTransport'
+        ],
+
+        _blobSlice: $.support.blobSlice && function () {
+            var slice = this.slice || this.webkitSlice || this.mozSlice;
+            return slice.apply(this, arguments);
+        },
+
+        _BitrateTimer: function () {
+            this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime());
+            this.loaded = 0;
+            this.bitrate = 0;
+            this.getBitrate = function (now, loaded, interval) {
+                var timeDiff = now - this.timestamp;
+                if (!this.bitrate || !interval || timeDiff > interval) {
+                    this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
+                    this.loaded = loaded;
+                    this.timestamp = now;
+                }
+                return this.bitrate;
+            };
+        },
+
+        _isXHRUpload: function (options) {
+            return !options.forceIframeTransport &&
+                ((!options.multipart && $.support.xhrFileUpload) ||
+                $.support.xhrFormDataFileUpload);
+        },
+
+        _getFormData: function (options) {
+            var formData;
+            if ($.type(options.formData) === 'function') {
+                return options.formData(options.form);
+            }
+            if ($.isArray(options.formData)) {
+                return options.formData;
+            }
+            if ($.type(options.formData) === 'object') {
+                formData = [];
+                $.each(options.formData, function (name, value) {
+                    formData.push({name: name, value: value});
+                });
+                return formData;
+            }
+            return [];
+        },
+
+        _getTotal: function (files) {
+            var total = 0;
+            $.each(files, function (index, file) {
+                total += file.size || 1;
+            });
+            return total;
+        },
+
+        _initProgressObject: function (obj) {
+            var progress = {
+                loaded: 0,
+                total: 0,
+                bitrate: 0
+            };
+            if (obj._progress) {
+                $.extend(obj._progress, progress);
+            } else {
+                obj._progress = progress;
+            }
+        },
+
+        _initResponseObject: function (obj) {
+            var prop;
+            if (obj._response) {
+                for (prop in obj._response) {
+                    if (obj._response.hasOwnProperty(prop)) {
+                        delete obj._response[prop];
+                    }
+                }
+            } else {
+                obj._response = {};
+            }
+        },
+
+        _onProgress: function (e, data) {
+            if (e.lengthComputable) {
+                var now = ((Date.now) ? Date.now() : (new Date()).getTime()),
+                    loaded;
+                if (data._time && data.progressInterval &&
+                        (now - data._time < data.progressInterval) &&
+                        e.loaded !== e.total) {
+                    return;
+                }
+                data._time = now;
+                loaded = Math.floor(
+                    e.loaded / e.total * (data.chunkSize || data._progress.total)
+                ) + (data.uploadedBytes || 0);
+                // Add the difference from the previously loaded state
+                // to the global loaded counter:
+                this._progress.loaded += (loaded - data._progress.loaded);
+                this._progress.bitrate = this._bitrateTimer.getBitrate(
+                    now,
+                    this._progress.loaded,
+                    data.bitrateInterval
+                );
+                data._progress.loaded = data.loaded = loaded;
+                data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate(
+                    now,
+                    loaded,
+                    data.bitrateInterval
+                );
+                // Trigger a custom progress event with a total data property set
+                // to the file size(s) of the current upload and a loaded data
+                // property calculated accordingly:
+                this._trigger(
+                    'progress',
+                    $.Event('progress', {delegatedEvent: e}),
+                    data
+                );
+                // Trigger a global progress event for all current file uploads,
+                // including ajax calls queued for sequential file uploads:
+                this._trigger(
+                    'progressall',
+                    $.Event('progressall', {delegatedEvent: e}),
+                    this._progress
+                );
+            }
+        },
+
+        _initProgressListener: function (options) {
+            var that = this,
+                xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
+            // Accesss to the native XHR object is required to add event listeners
+            // for the upload progress event:
+            if (xhr.upload) {
+                $(xhr.upload).bind('progress', function (e) {
+                    var oe = e.originalEvent;
+                    // Make sure the progress event properties get copied over:
+                    e.lengthComputable = oe.lengthComputable;
+                    e.loaded = oe.loaded;
+                    e.total = oe.total;
+                    that._onProgress(e, options);
+                });
+                options.xhr = function () {
+                    return xhr;
+                };
+            }
+        },
+
+        _isInstanceOf: function (type, obj) {
+            // Cross-frame instanceof check
+            return Object.prototype.toString.call(obj) === '[object ' + type + ']';
+        },
+
+        _initXHRData: function (options) {
+            var that = this,
+                formData,
+                file = options.files[0],
+                // Ignore non-multipart setting if not supported:
+                multipart = options.multipart || !$.support.xhrFileUpload,
+                paramName = $.type(options.paramName) === 'array' ?
+                    options.paramName[0] : options.paramName;
+            options.headers = $.extend({}, options.headers);
+            if (options.contentRange) {
+                options.headers['Content-Range'] = options.contentRange;
+            }
+            if (!multipart || options.blob || !this._isInstanceOf('File', file)) {
+                options.headers['Content-Disposition'] = 'attachment; filename="' +
+                    encodeURI(file.name) + '"';
+            }
+            if (!multipart) {
+                options.contentType = file.type || 'application/octet-stream';
+                options.data = options.blob || file;
+            } else if ($.support.xhrFormDataFileUpload) {
+                if (options.postMessage) {
+                    // window.postMessage does not allow sending FormData
+                    // objects, so we just add the File/Blob objects to
+                    // the formData array and let the postMessage window
+                    // create the FormData object out of this array:
+                    formData = this._getFormData(options);
+                    if (options.blob) {
+                        formData.push({
+                            name: paramName,
+                            value: options.blob
+                        });
+                    } else {
+                        $.each(options.files, function (index, file) {
+                            formData.push({
+                                name: ($.type(options.paramName) === 'array' &&
+                                    options.paramName[index]) || paramName,
+                                value: file
+                            });
+                        });
+                    }
+                } else {
+                    if (that._isInstanceOf('FormData', options.formData)) {
+                        formData = options.formData;
+                    } else {
+                        formData = new FormData();
+                        $.each(this._getFormData(options), function (index, field) {
+                            formData.append(field.name, field.value);
+                        });
+                    }
+                    if (options.blob) {
+                        formData.append(paramName, options.blob, file.name);
+                    } else {
+                        $.each(options.files, function (index, file) {
+                            // This check allows the tests to run with
+                            // dummy objects:
+                            if (that._isInstanceOf('File', file) ||
+                                    that._isInstanceOf('Blob', file)) {
+                                formData.append(
+                                    ($.type(options.paramName) === 'array' &&
+                                        options.paramName[index]) || paramName,
+                                    file,
+                                    file.uploadName || file.name
+                                );
+                            }
+                        });
+                    }
+                }
+                options.data = formData;
+            }
+            // Blob reference is not needed anymore, free memory:
+            options.blob = null;
+        },
+
+        _initIframeSettings: function (options) {
+            var targetHost = $('<a></a>').prop('href', options.url).prop('host');
+            // Setting the dataType to iframe enables the iframe transport:
+            options.dataType = 'iframe ' + (options.dataType || '');
+            // The iframe transport accepts a serialized array as form data:
+            options.formData = this._getFormData(options);
+            // Add redirect url to form data on cross-domain uploads:
+            if (options.redirect && targetHost && targetHost !== location.host) {
+                options.formData.push({
+                    name: options.redirectParamName || 'redirect',
+                    value: options.redirect
+                });
+            }
+        },
+
+        _initDataSettings: function (options) {
+            if (this._isXHRUpload(options)) {
+                if (!this._chunkedUpload(options, true)) {
+                    if (!options.data) {
+                        this._initXHRData(options);
+                    }
+                    this._initProgressListener(options);
+                }
+                if (options.postMessage) {
+                    // Setting the dataType to postmessage enables the
+                    // postMessage transport:
+                    options.dataType = 'postmessage ' + (options.dataType || '');
+                }
+            } else {
+                this._initIframeSettings(options);
+            }
+        },
+
+        _getParamName: function (options) {
+            var fileInput = $(options.fileInput),
+                paramName = options.paramName;
+            if (!paramName) {
+                paramName = [];
+                fileInput.each(function () {
+                    var input = $(this),
+                        name = input.prop('name') || 'files[]',
+                        i = (input.prop('files') || [1]).length;
+                    while (i) {
+                        paramName.push(name);
+                        i -= 1;
+                    }
+                });
+                if (!paramName.length) {
+                    paramName = [fileInput.prop('name') || 'files[]'];
+                }
+            } else if (!$.isArray(paramName)) {
+                paramName = [paramName];
+            }
+            return paramName;
+        },
+
+        _initFormSettings: function (options) {
+            // Retrieve missing options from the input field and the
+            // associated form, if available:
+            if (!options.form || !options.form.length) {
+                options.form = $(options.fileInput.prop('form'));
+                // If the given file input doesn't have an associated form,
+                // use the default widget file input's form:
+                if (!options.form.length) {
+                    options.form = $(this.options.fileInput.prop('form'));
+                }
+            }
+            options.paramName = this._getParamName(options);
+            if (!options.url) {
+                options.url = options.form.prop('action') || location.href;
+            }
+            // The HTTP request method must be "POST" or "PUT":
+            options.type = (options.type ||
+                ($.type(options.form.prop('method')) === 'string' &&
+                    options.form.prop('method')) || ''
+                ).toUpperCase();
+            if (options.type !== 'POST' && options.type !== 'PUT' &&
+                    options.type !== 'PATCH') {
+                options.type = 'POST';
+            }
+            if (!options.formAcceptCharset) {
+                options.formAcceptCharset = options.form.attr('accept-charset');
+            }
+        },
+
+        _getAJAXSettings: function (data) {
+            var options = $.extend({}, this.options, data);
+            this._initFormSettings(options);
+            this._initDataSettings(options);
+            return options;
+        },
+
+        // jQuery 1.6 doesn't provide .state(),
+        // while jQuery 1.8+ removed .isRejected() and .isResolved():
+        _getDeferredState: function (deferred) {
+            if (deferred.state) {
+                return deferred.state();
+            }
+            if (deferred.isResolved()) {
+                return 'resolved';
+            }
+            if (deferred.isRejected()) {
+                return 'rejected';
+            }
+            return 'pending';
+        },
+
+        // Maps jqXHR callbacks to the equivalent
+        // methods of the given Promise object:
+        _enhancePromise: function (promise) {
+            promise.success = promise.done;
+            promise.error = promise.fail;
+            promise.complete = promise.always;
+            return promise;
+        },
+
+        // Creates and returns a Promise object enhanced with
+        // the jqXHR methods abort, success, error and complete:
+        _getXHRPromise: function (resolveOrReject, context, args) {
+            var dfd = $.Deferred(),
+                promise = dfd.promise();
+            context = context || this.options.context || promise;
+            if (resolveOrReject === true) {
+                dfd.resolveWith(context, args);
+            } else if (resolveOrReject === false) {
+                dfd.rejectWith(context, args);
+            }
+            promise.abort = dfd.promise;
+            return this._enhancePromise(promise);
+        },
+
+        // Adds convenience methods to the data callback argument:
+        _addConvenienceMethods: function (e, data) {
+            var that = this,
+                getPromise = function (args) {
+                    return $.Deferred().resolveWith(that, args).promise();
+                };
+            data.process = function (resolveFunc, rejectFunc) {
+                if (resolveFunc || rejectFunc) {
+                    data._processQueue = this._processQueue =
+                        (this._processQueue || getPromise([this])).pipe(
+                            function () {
+                                if (data.errorThrown) {
+                                    return $.Deferred()
+                                        .rejectWith(that, [data]).promise();
+                                }
+                                return getPromise(arguments);
+                            }
+                        ).pipe(resolveFunc, rejectFunc);
+                }
+                return this._processQueue || getPromise([this]);
+            };
+            data.submit = function () {
+                if (this.state() !== 'pending') {
+                    data.jqXHR = this.jqXHR =
+                        (that._trigger(
+                            'submit',
+                            $.Event('submit', {delegatedEvent: e}),
+                            this
+                        ) !== false) && that._onSend(e, this);
+                }
+                return this.jqXHR || that._getXHRPromise();
+            };
+            data.abort = function () {
+                if (this.jqXHR) {
+                    return this.jqXHR.abort();
+                }
+                this.errorThrown = 'abort';
+                that._trigger('fail', null, this);
+                return that._getXHRPromise(false);
+            };
+            data.state = function () {
+                if (this.jqXHR) {
+                    return that._getDeferredState(this.jqXHR);
+                }
+                if (this._processQueue) {
+                    return that._getDeferredState(this._processQueue);
+                }
+            };
+            data.processing = function () {
+                return !this.jqXHR && this._processQueue && that
+                    ._getDeferredState(this._processQueue) === 'pending';
+            };
+            data.progress = function () {
+                return this._progress;
+            };
+            data.response = function () {
+                return this._response;
+            };
+        },
+
+        // Parses the Range header from the server response
+        // and returns the uploaded bytes:
+        _getUploadedBytes: function (jqXHR) {
+            var range = jqXHR.getResponseHeader('Range'),
+                parts = range && range.split('-'),
+                upperBytesPos = parts && parts.length > 1 &&
+                    parseInt(parts[1], 10);
+            return upperBytesPos && upperBytesPos + 1;
+        },
+
+        // Uploads a file in multiple, sequential requests
+        // by splitting the file up in multiple blob chunks.
+        // If the second parameter is true, only tests if the file
+        // should be uploaded in chunks, but does not invoke any
+        // upload requests:
+        _chunkedUpload: function (options, testOnly) {
+            options.uploadedBytes = options.uploadedBytes || 0;
+            var that = this,
+                file = options.files[0],
+                fs = file.size,
+                ub = options.uploadedBytes,
+                mcs = options.maxChunkSize || fs,
+                slice = this._blobSlice,
+                dfd = $.Deferred(),
+                promise = dfd.promise(),
+                jqXHR,
+                upload;
+            if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
+                    options.data) {
+                return false;
+            }
+            if (testOnly) {
+                return true;
+            }
+            if (ub >= fs) {
+                file.error = options.i18n('uploadedBytes');
+                return this._getXHRPromise(
+                    false,
+                    options.context,
+                    [null, 'error', file.error]
+                );
+            }
+            // The chunk upload method:
+            upload = function () {
+                // Clone the options object for each chunk upload:
+                var o = $.extend({}, options),
+                    currentLoaded = o._progress.loaded;
+                o.blob = slice.call(
+                    file,
+                    ub,
+                    ub + mcs,
+                    file.type
+                );
+                // Store the current chunk size, as the blob itself
+                // will be dereferenced after data processing:
+                o.chunkSize = o.blob.size;
+                // Expose the chunk bytes position range:
+                o.contentRange = 'bytes ' + ub + '-' +
+                    (ub + o.chunkSize - 1) + '/' + fs;
+                // Process the upload data (the blob and potential form data):
+                that._initXHRData(o);
+                // Add progress listeners for this chunk upload:
+                that._initProgressListener(o);
+                jqXHR = ((that._trigger('chunksend', null, o) !== false && $.ajax(o)) ||
+                        that._getXHRPromise(false, o.context))
+                    .done(function (result, textStatus, jqXHR) {
+                        ub = that._getUploadedBytes(jqXHR) ||
+                            (ub + o.chunkSize);
+                        // Create a progress event if no final progress event
+                        // with loaded equaling total has been triggered
+                        // for this chunk:
+                        if (currentLoaded + o.chunkSize - o._progress.loaded) {
+                            that._onProgress($.Event('progress', {
+                                lengthComputable: true,
+                                loaded: ub - o.uploadedBytes,
+                                total: ub - o.uploadedBytes
+                            }), o);
+                        }
+                        options.uploadedBytes = o.uploadedBytes = ub;
+                        o.result = result;
+                        o.textStatus = textStatus;
+                        o.jqXHR = jqXHR;
+                        that._trigger('chunkdone', null, o);
+                        that._trigger('chunkalways', null, o);
+                        if (ub < fs) {
+                            // File upload not yet complete,
+                            // continue with the next chunk:
+                            upload();
+                        } else {
+                            dfd.resolveWith(
+                                o.context,
+                                [result, textStatus, jqXHR]
+                            );
+                        }
+                    })
+                    .fail(function (jqXHR, textStatus, errorThrown) {
+                        o.jqXHR = jqXHR;
+                        o.textStatus = textStatus;
+                        o.errorThrown = errorThrown;
+                        that._trigger('chunkfail', null, o);
+                        that._trigger('chunkalways', null, o);
+                        dfd.rejectWith(
+                            o.context,
+                            [jqXHR, textStatus, errorThrown]
+                        );
+                    });
+            };
+            this._enhancePromise(promise);
+            promise.abort = function () {
+                return jqXHR.abort();
+            };
+            upload();
+            return promise;
+        },
+
+        _beforeSend: function (e, data) {
+            if (this._active === 0) {
+                // the start callback is triggered when an upload starts
+                // and no other uploads are currently running,
+                // equivalent to the global ajaxStart event:
+                this._trigger('start');
+                // Set timer for global bitrate progress calculation:
+                this._bitrateTimer = new this._BitrateTimer();
+                // Reset the global progress values:
+                this._progress.loaded = this._progress.total = 0;
+                this._progress.bitrate = 0;
+            }
+            // Make sure the container objects for the .response() and
+            // .progress() methods on the data object are available
+            // and reset to their initial state:
+            this._initResponseObject(data);
+            this._initProgressObject(data);
+            data._progress.loaded = data.loaded = data.uploadedBytes || 0;
+            data._progress.total = data.total = this._getTotal(data.files) || 1;
+            data._progress.bitrate = data.bitrate = 0;
+            this._active += 1;
+            // Initialize the global progress values:
+            this._progress.loaded += data.loaded;
+            this._progress.total += data.total;
+        },
+
+        _onDone: function (result, textStatus, jqXHR, options) {
+            var total = options._progress.total,
+                response = options._response;
+            if (options._progress.loaded < total) {
+                // Create a progress event if no final progress event
+                // with loaded equaling total has been triggered:
+                this._onProgress($.Event('progress', {
+                    lengthComputable: true,
+                    loaded: total,
+                    total: total
+                }), options);
+            }
+            response.result = options.result = result;
+            response.textStatus = options.textStatus = textStatus;
+            response.jqXHR = options.jqXHR = jqXHR;
+            this._trigger('done', null, options);
+        },
+
+        _onFail: function (jqXHR, textStatus, errorThrown, options) {
+            var response = options._response;
+            if (options.recalculateProgress) {
+                // Remove the failed (error or abort) file upload from
+                // the global progress calculation:
+                this._progress.loaded -= options._progress.loaded;
+                this._progress.total -= options._progress.total;
+            }
+            response.jqXHR = options.jqXHR = jqXHR;
+            response.textStatus = options.textStatus = textStatus;
+            response.errorThrown = options.errorThrown = errorThrown;
+            this._trigger('fail', null, options);
+        },
+
+        _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
+            // jqXHRorResult, textStatus and jqXHRorError are added to the
+            // options object via done and fail callbacks
+            this._trigger('always', null, options);
+        },
+
+        _onSend: function (e, data) {
+            if (!data.submit) {
+                this._addConvenienceMethods(e, data);
+            }
+            var that = this,
+                jqXHR,
+                aborted,
+                slot,
+                pipe,
+                options = that._getAJAXSettings(data),
+                send = function () {
+                    that._sending += 1;
+                    // Set timer for bitrate progress calculation:
+                    options._bitrateTimer = new that._BitrateTimer();
+                    jqXHR = jqXHR || (
+                        ((aborted || that._trigger(
+                            'send',
+                            $.Event('send', {delegatedEvent: e}),
+                            options
+                        ) === false) &&
+                        that._getXHRPromise(false, options.context, aborted)) ||
+                        that._chunkedUpload(options) || $.ajax(options)
+                    ).done(function (result, textStatus, jqXHR) {
+                        that._onDone(result, textStatus, jqXHR, options);
+                    }).fail(function (jqXHR, textStatus, errorThrown) {
+                        that._onFail(jqXHR, textStatus, errorThrown, options);
+                    }).always(function (jqXHRorResult, textStatus, jqXHRorError) {
+                        that._onAlways(
+                            jqXHRorResult,
+                            textStatus,
+                            jqXHRorError,
+                            options
+                        );
+                        that._sending -= 1;
+                        that._active -= 1;
+                        if (options.limitConcurrentUploads &&
+                                options.limitConcurrentUploads > that._sending) {
+                            // Start the next queued upload,
+                            // that has not been aborted:
+                            var nextSlot = that._slots.shift();
+                            while (nextSlot) {
+                                if (that._getDeferredState(nextSlot) === 'pending') {
+                                    nextSlot.resolve();
+                                    break;
+                                }
+                                nextSlot = that._slots.shift();
+                            }
+                        }
+                        if (that._active === 0) {
+                            // The stop callback is triggered when all uploads have
+                            // been completed, equivalent to the global ajaxStop event:
+                            that._trigger('stop');
+                        }
+                    });
+                    return jqXHR;
+                };
+            this._beforeSend(e, options);
+            if (this.options.sequentialUploads ||
+                    (this.options.limitConcurrentUploads &&
+                    this.options.limitConcurrentUploads <= this._sending)) {
+                if (this.options.limitConcurrentUploads > 1) {
+                    slot = $.Deferred();
+                    this._slots.push(slot);
+                    pipe = slot.pipe(send);
+                } else {
+                    this._sequence = this._sequence.pipe(send, send);
+                    pipe = this._sequence;
+                }
+                // Return the piped Promise object, enhanced with an abort method,
+                // which is delegated to the jqXHR object of the current upload,
+                // and jqXHR callbacks mapped to the equivalent Promise methods:
+                pipe.abort = function () {
+                    aborted = [undefined, 'abort', 'abort'];
+                    if (!jqXHR) {
+                        if (slot) {
+                            slot.rejectWith(options.context, aborted);
+                        }
+                        return send();
+                    }
+                    return jqXHR.abort();
+                };
+                return this._enhancePromise(pipe);
+            }
+            return send();
+        },
+
+        _onAdd: function (e, data) {
+            var that = this,
+                result = true,
+                options = $.extend({}, this.options, data),
+                files = data.files,
+                filesLength = files.length,
+                limit = options.limitMultiFileUploads,
+                limitSize = options.limitMultiFileUploadSize,
+                overhead = options.limitMultiFileUploadSizeOverhead,
+                batchSize = 0,
+                paramName = this._getParamName(options),
+                paramNameSet,
+                paramNameSlice,
+                fileSet,
+                i,
+                j = 0;
+            if (!filesLength) {
+                return false;
+            }
+            if (limitSize && files[0].size === undefined) {
+                limitSize = undefined;
+            }
+            if (!(options.singleFileUploads || limit || limitSize) ||
+                    !this._isXHRUpload(options)) {
+                fileSet = [files];
+                paramNameSet = [paramName];
+            } else if (!(options.singleFileUploads || limitSize) && limit) {
+                fileSet = [];
+                paramNameSet = [];
+                for (i = 0; i < filesLength; i += limit) {
+                    fileSet.push(files.slice(i, i + limit));
+                    paramNameSlice = paramName.slice(i, i + limit);
+                    if (!paramNameSlice.length) {
+                        paramNameSlice = paramName;
+                    }
+                    paramNameSet.push(paramNameSlice);
+                }
+            } else if (!options.singleFileUploads && limitSize) {
+                fileSet = [];
+                paramNameSet = [];
+                for (i = 0; i < filesLength; i = i + 1) {
+                    batchSize += files[i].size + overhead;
+                    if (i + 1 === filesLength ||
+                            ((batchSize + files[i + 1].size + overhead) > limitSize) ||
+                            (limit && i + 1 - j >= limit)) {
+                        fileSet.push(files.slice(j, i + 1));
+                        paramNameSlice = paramName.slice(j, i + 1);
+                        if (!paramNameSlice.length) {
+                            paramNameSlice = paramName;
+                        }
+                        paramNameSet.push(paramNameSlice);
+                        j = i + 1;
+                        batchSize = 0;
+                    }
+                }
+            } else {
+                paramNameSet = paramName;
+            }
+            data.originalFiles = files;
+            $.each(fileSet || files, function (index, element) {
+                var newData = $.extend({}, data);
+                newData.files = fileSet ? element : [element];
+                newData.paramName = paramNameSet[index];
+                that._initResponseObject(newData);
+                that._initProgressObject(newData);
+                that._addConvenienceMethods(e, newData);
+                result = that._trigger(
+                    'add',
+                    $.Event('add', {delegatedEvent: e}),
+                    newData
+                );
+                return result;
+            });
+            return result;
+        },
+
+        _replaceFileInput: function (data) {
+            var input = data.fileInput,
+                inputClone = input.clone(true),
+                restoreFocus = input.is(document.activeElement);
+            // Add a reference for the new cloned file input to the data argument:
+            data.fileInputClone = inputClone;
+            $('<form></form>').append(inputClone)[0].reset();
+            // Detaching allows to insert the fileInput on another form
+            // without loosing the file input value:
+            input.after(inputClone).detach();
+            // If the fileInput had focus before it was detached,
+            // restore focus to the inputClone.
+            if (restoreFocus) {
+                inputClone.focus();
+            }
+            // Avoid memory leaks with the detached file input:
+            $.cleanData(input.unbind('remove'));
+            // Replace the original file input element in the fileInput
+            // elements set with the clone, which has been copied including
+            // event handlers:
+            this.options.fileInput = this.options.fileInput.map(function (i, el) {
+                if (el === input[0]) {
+                    return inputClone[0];
+                }
+                return el;
+            });
+            // If the widget has been initialized on the file input itself,
+            // override this.element with the file input clone:
+            if (input[0] === this.element[0]) {
+                this.element = inputClone;
+            }
+        },
+
+        _handleFileTreeEntry: function (entry, path) {
+            var that = this,
+                dfd = $.Deferred(),
+                errorHandler = function (e) {
+                    if (e && !e.entry) {
+                        e.entry = entry;
+                    }
+                    // Since $.when returns immediately if one
+                    // Deferred is rejected, we use resolve instead.
+                    // This allows valid files and invalid items
+                    // to be returned together in one set:
+                    dfd.resolve([e]);
+                },
+                successHandler = function (entries) {
+                    that._handleFileTreeEntries(
+                        entries,
+                        path + entry.name + '/'
+                    ).done(function (files) {
+                        dfd.resolve(files);
+                    }).fail(errorHandler);
+                },
+                readEntries = function () {
+                    dirReader.readEntries(function (results) {
+                        if (!results.length) {
+                            successHandler(entries);
+                        } else {
+                            entries = entries.concat(results);
+                            readEntries();
+                        }
+                    }, errorHandler);
+                },
+                dirReader, entries = [];
+            path = path || '';
+            if (entry.isFile) {
+                if (entry._file) {
+                    // Workaround for Chrome bug #149735
+                    entry._file.relativePath = path;
+                    dfd.resolve(entry._file);
+                } else {
+                    entry.file(function (file) {
+                        file.relativePath = path;
+                        dfd.resolve(file);
+                    }, errorHandler);
+                }
+            } else if (entry.isDirectory) {
+                dirReader = entry.createReader();
+                readEntries();
+            } else {
+                // Return an empy list for file system items
+                // other than files or directories:
+                dfd.resolve([]);
+            }
+            return dfd.promise();
+        },
+
+        _handleFileTreeEntries: function (entries, path) {
+            var that = this;
+            return $.when.apply(
+                $,
+                $.map(entries, function (entry) {
+                    return that._handleFileTreeEntry(entry, path);
+                })
+            ).pipe(function () {
+                return Array.prototype.concat.apply(
+                    [],
+                    arguments
+                );
+            });
+        },
+
+        _getDroppedFiles: function (dataTransfer) {
+            dataTransfer = dataTransfer || {};
+            var items = dataTransfer.items;
+            if (items && items.length && (items[0].webkitGetAsEntry ||
+                    items[0].getAsEntry)) {
+                return this._handleFileTreeEntries(
+                    $.map(items, function (item) {
+                        var entry;
+                        if (item.webkitGetAsEntry) {
+                            entry = item.webkitGetAsEntry();
+                            if (entry) {
+                                // Workaround for Chrome bug #149735:
+                                entry._file = item.getAsFile();
+                            }
+                            return entry;
+                        }
+                        return item.getAsEntry();
+                    })
+                );
+            }
+            return $.Deferred().resolve(
+                $.makeArray(dataTransfer.files)
+            ).promise();
+        },
+
+        _getSingleFileInputFiles: function (fileInput) {
+            fileInput = $(fileInput);
+            var entries = fileInput.prop('webkitEntries') ||
+                    fileInput.prop('entries'),
+                files,
+                value;
+            if (entries && entries.length) {
+                return this._handleFileTreeEntries(entries);
+            }
+            files = $.makeArray(fileInput.prop('files'));
+            if (!files.length) {
+                value = fileInput.prop('value');
+                if (!value) {
+                    return $.Deferred().resolve([]).promise();
+                }
+                // If the files property is not available, the browser does not
+                // support the File API and we add a pseudo File object with
+                // the input value as name with path information removed:
+                files = [{name: value.replace(/^.*\\/, '')}];
+            } else if (files[0].name === undefined && files[0].fileName) {
+                // File normalization for Safari 4 and Firefox 3:
+                $.each(files, function (index, file) {
+                    file.name = file.fileName;
+                    file.size = file.fileSize;
+                });
+            }
+            return $.Deferred().resolve(files).promise();
+        },
+
+        _getFileInputFiles: function (fileInput) {
+            if (!(fileInput instanceof $) || fileInput.length === 1) {
+                return this._getSingleFileInputFiles(fileInput);
+            }
+            return $.when.apply(
+                $,
+                $.map(fileInput, this._getSingleFileInputFiles)
+            ).pipe(function () {
+                return Array.prototype.concat.apply(
+                    [],
+                    arguments
+                );
+            });
+        },
+
+        _onChange: function (e) {
+            var that = this,
+                data = {
+                    fileInput: $(e.target),
+                    form: $(e.target.form)
+                };
+            this._getFileInputFiles(data.fileInput).always(function (files) {
+                data.files = files;
+                if (that.options.replaceFileInput) {
+                    that._replaceFileInput(data);
+                }
+                if (that._trigger(
+                        'change',
+                        $.Event('change', {delegatedEvent: e}),
+                        data
+                    ) !== false) {
+                    that._onAdd(e, data);
+                }
+            });
+        },
+
+        _onPaste: function (e) {
+            var items = e.originalEvent && e.originalEvent.clipboardData &&
+                    e.originalEvent.clipboardData.items,
+                data = {files: []};
+            if (items && items.length) {
+                $.each(items, function (index, item) {
+                    var file = item.getAsFile && item.getAsFile();
+                    if (file) {
+                        data.files.push(file);
+                    }
+                });
+                if (this._trigger(
+                        'paste',
+                        $.Event('paste', {delegatedEvent: e}),
+                        data
+                    ) !== false) {
+                    this._onAdd(e, data);
+                }
+            }
+        },
+
+        _onDrop: function (e) {
+            e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
+            var that = this,
+                dataTransfer = e.dataTransfer,
+                data = {};
+            if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
+                e.preventDefault();
+                this._getDroppedFiles(dataTransfer).always(function (files) {
+                    data.files = files;
+                    if (that._trigger(
+                            'drop',
+                            $.Event('drop', {delegatedEvent: e}),
+                            data
+                        ) !== false) {
+                        that._onAdd(e, data);
+                    }
+                });
+            }
+        },
+
+        _onDragOver: getDragHandler('dragover'),
+
+        _onDragEnter: getDragHandler('dragenter'),
+
+        _onDragLeave: getDragHandler('dragleave'),
+
+        _initEventHandlers: function () {
+            if (this._isXHRUpload(this.options)) {
+                this._on(this.options.dropZone, {
+                    dragover: this._onDragOver,
+                    drop: this._onDrop,
+                    // event.preventDefault() on dragenter is required for IE10+:
+                    dragenter: this._onDragEnter,
+                    // dragleave is not required, but added for completeness:
+                    dragleave: this._onDragLeave
+                });
+                this._on(this.options.pasteZone, {
+                    paste: this._onPaste
+                });
+            }
+            if ($.support.fileInput) {
+                this._on(this.options.fileInput, {
+                    change: this._onChange
+                });
+            }
+        },
+
+        _destroyEventHandlers: function () {
+            this._off(this.options.dropZone, 'dragenter dragleave dragover drop');
+            this._off(this.options.pasteZone, 'paste');
+            this._off(this.options.fileInput, 'change');
+        },
+
+        _setOption: function (key, value) {
+            var reinit = $.inArray(key, this._specialOptions) !== -1;
+            if (reinit) {
+                this._destroyEventHandlers();
+            }
+            this._super(key, value);
+            if (reinit) {
+                this._initSpecialOptions();
+                this._initEventHandlers();
+            }
+        },
+
+        _initSpecialOptions: function () {
+            var options = this.options;
+            if (options.fileInput === undefined) {
+                options.fileInput = this.element.is('input[type="file"]') ?
+                        this.element : this.element.find('input[type="file"]');
+            } else if (!(options.fileInput instanceof $)) {
+                options.fileInput = $(options.fileInput);
+            }
+            if (!(options.dropZone instanceof $)) {
+                options.dropZone = $(options.dropZone);
+            }
+            if (!(options.pasteZone instanceof $)) {
+                options.pasteZone = $(options.pasteZone);
+            }
+        },
+
+        _getRegExp: function (str) {
+            var parts = str.split('/'),
+                modifiers = parts.pop();
+            parts.shift();
+            return new RegExp(parts.join('/'), modifiers);
+        },
+
+        _isRegExpOption: function (key, value) {
+            return key !== 'url' && $.type(value) === 'string' &&
+                /^\/.*\/[igm]{0,3}$/.test(value);
+        },
+
+        _initDataAttributes: function () {
+            var that = this,
+                options = this.options,
+                data = this.element.data();
+            // Initialize options set via HTML5 data-attributes:
+            $.each(
+                this.element[0].attributes,
+                function (index, attr) {
+                    var key = attr.name.toLowerCase(),
+                        value;
+                    if (/^data-/.test(key)) {
+                        // Convert hyphen-ated key to camelCase:
+                        key = key.slice(5).replace(/-[a-z]/g, function (str) {
+                            return str.charAt(1).toUpperCase();
+                        });
+                        value = data[key];
+                        if (that._isRegExpOption(key, value)) {
+                            value = that._getRegExp(value);
+                        }
+                        options[key] = value;
+                    }
+                }
+            );
+        },
+
+        _create: function () {
+            this._initDataAttributes();
+            this._initSpecialOptions();
+            this._slots = [];
+            this._sequence = this._getXHRPromise(true);
+            this._sending = this._active = 0;
+            this._initProgressObject(this);
+            this._initEventHandlers();
+        },
+
+        // This method is exposed to the widget API and allows to query
+        // the number of active uploads:
+        active: function () {
+            return this._active;
+        },
+
+        // This method is exposed to the widget API and allows to query
+        // the widget upload progress.
+        // It returns an object with loaded, total and bitrate properties
+        // for the running uploads:
+        progress: function () {
+            return this._progress;
+        },
+
+        // This method is exposed to the widget API and allows adding files
+        // using the fileupload API. The data parameter accepts an object which
+        // must have a files property and can contain additional options:
+        // .fileupload('add', {files: filesList});
+        add: function (data) {
+            var that = this;
+            if (!data || this.options.disabled) {
+                return;
+            }
+            if (data.fileInput && !data.files) {
+                this._getFileInputFiles(data.fileInput).always(function (files) {
+                    data.files = files;
+                    that._onAdd(null, data);
+                });
+            } else {
+                data.files = $.makeArray(data.files);
+                this._onAdd(null, data);
+            }
+        },
+
+        // This method is exposed to the widget API and allows sending files
+        // using the fileupload API. The data parameter accepts an object which
+        // must have a files or fileInput property and can contain additional options:
+        // .fileupload('send', {files: filesList});
+        // The method returns a Promise object for the file upload call.
+        send: function (data) {
+            if (data && !this.options.disabled) {
+                if (data.fileInput && !data.files) {
+                    var that = this,
+                        dfd = $.Deferred(),
+                        promise = dfd.promise(),
+                        jqXHR,
+                        aborted;
+                    promise.abort = function () {
+                        aborted = true;
+                        if (jqXHR) {
+                            return jqXHR.abort();
+                        }
+                        dfd.reject(null, 'abort', 'abort');
+                        return promise;
+                    };
+                    this._getFileInputFiles(data.fileInput).always(
+                        function (files) {
+                            if (aborted) {
+                                return;
+                            }
+                            if (!files.length) {
+                                dfd.reject();
+                                return;
+                            }
+                            data.files = files;
+                            jqXHR = that._onSend(null, data);
+                            jqXHR.then(
+                                function (result, textStatus, jqXHR) {
+                                    dfd.resolve(result, textStatus, jqXHR);
+                                },
+                                function (jqXHR, textStatus, errorThrown) {
+                                    dfd.reject(jqXHR, textStatus, errorThrown);
+                                }
+                            );
+                        }
+                    );
+                    return this._enhancePromise(promise);
+                }
+                data.files = $.makeArray(data.files);
+                if (data.files.length) {
+                    return this._onSend(null, data);
+                }
+            }
+            return this._getXHRPromise(false, data && data.context);
+        }
+
+    });
+
+}));

+ 217 - 0
app/Resources/public/assets/jquery-file-upload/js/jquery.iframe-transport.js

@@ -0,0 +1,217 @@
+/*
+ * jQuery Iframe Transport Plugin
+ * https://github.com/blueimp/jQuery-File-Upload
+ *
+ * Copyright 2011, Sebastian Tschan
+ * https://blueimp.net
+ *
+ * Licensed under the MIT license:
+ * http://www.opensource.org/licenses/MIT
+ */
+
+/* global define, require, window, document */
+
+(function (factory) {
+    'use strict';
+    if (typeof define === 'function' && define.amd) {
+        // Register as an anonymous AMD module:
+        define(['jquery'], factory);
+    } else if (typeof exports === 'object') {
+        // Node/CommonJS:
+        factory(require('jquery'));
+    } else {
+        // Browser globals:
+        factory(window.jQuery);
+    }
+}(function ($) {
+    'use strict';
+
+    // Helper variable to create unique names for the transport iframes:
+    var counter = 0;
+
+    // The iframe transport accepts four additional options:
+    // options.fileInput: a jQuery collection of file input fields
+    // options.paramName: the parameter name for the file form data,
+    //  overrides the name property of the file input field(s),
+    //  can be a string or an array of strings.
+    // options.formData: an array of objects with name and value properties,
+    //  equivalent to the return data of .serializeArray(), e.g.:
+    //  [{name: 'a', value: 1}, {name: 'b', value: 2}]
+    // options.initialIframeSrc: the URL of the initial iframe src,
+    //  by default set to "javascript:false;"
+    $.ajaxTransport('iframe', function (options) {
+        if (options.async) {
+            // javascript:false as initial iframe src
+            // prevents warning popups on HTTPS in IE6:
+            /*jshint scripturl: true */
+            var initialIframeSrc = options.initialIframeSrc || 'javascript:false;',
+            /*jshint scripturl: false */
+                form,
+                iframe,
+                addParamChar;
+            return {
+                send: function (_, completeCallback) {
+                    form = $('<form style="display:none;"></form>');
+                    form.attr('accept-charset', options.formAcceptCharset);
+                    addParamChar = /\?/.test(options.url) ? '&' : '?';
+                    // XDomainRequest only supports GET and POST:
+                    if (options.type === 'DELETE') {
+                        options.url = options.url + addParamChar + '_method=DELETE';
+                        options.type = 'POST';
+                    } else if (options.type === 'PUT') {
+                        options.url = options.url + addParamChar + '_method=PUT';
+                        options.type = 'POST';
+                    } else if (options.type === 'PATCH') {
+                        options.url = options.url + addParamChar + '_method=PATCH';
+                        options.type = 'POST';
+                    }
+                    // IE versions below IE8 cannot set the name property of
+                    // elements that have already been added to the DOM,
+                    // so we set the name along with the iframe HTML markup:
+                    counter += 1;
+                    iframe = $(
+                        '<iframe src="' + initialIframeSrc +
+                            '" name="iframe-transport-' + counter + '"></iframe>'
+                    ).bind('load', function () {
+                        var fileInputClones,
+                            paramNames = $.isArray(options.paramName) ?
+                                    options.paramName : [options.paramName];
+                        iframe
+                            .unbind('load')
+                            .bind('load', function () {
+                                var response;
+                                // Wrap in a try/catch block to catch exceptions thrown
+                                // when trying to access cross-domain iframe contents:
+                                try {
+                                    response = iframe.contents();
+                                    // Google Chrome and Firefox do not throw an
+                                    // exception when calling iframe.contents() on
+                                    // cross-domain requests, so we unify the response:
+                                    if (!response.length || !response[0].firstChild) {
+                                        throw new Error();
+                                    }
+                                } catch (e) {
+                                    response = undefined;
+                                }
+                                // The complete callback returns the
+                                // iframe content document as response object:
+                                completeCallback(
+                                    200,
+                                    'success',
+                                    {'iframe': response}
+                                );
+                                // Fix for IE endless progress bar activity bug
+                                // (happens on form submits to iframe targets):
+                                $('<iframe src="' + initialIframeSrc + '"></iframe>')
+                                    .appendTo(form);
+                                window.setTimeout(function () {
+                                    // Removing the form in a setTimeout call
+                                    // allows Chrome's developer tools to display
+                                    // the response result
+                                    form.remove();
+                                }, 0);
+                            });
+                        form
+                            .prop('target', iframe.prop('name'))
+                            .prop('action', options.url)
+                            .prop('method', options.type);
+                        if (options.formData) {
+                            $.each(options.formData, function (index, field) {
+                                $('<input type="hidden"/>')
+                                    .prop('name', field.name)
+                                    .val(field.value)
+                                    .appendTo(form);
+                            });
+                        }
+                        if (options.fileInput && options.fileInput.length &&
+                                options.type === 'POST') {
+                            fileInputClones = options.fileInput.clone();
+                            // Insert a clone for each file input field:
+                            options.fileInput.after(function (index) {
+                                return fileInputClones[index];
+                            });
+                            if (options.paramName) {
+                                options.fileInput.each(function (index) {
+                                    $(this).prop(
+                                        'name',
+                                        paramNames[index] || options.paramName
+                                    );
+                                });
+                            }
+                            // Appending the file input fields to the hidden form
+                            // removes them from their original location:
+                            form
+                                .append(options.fileInput)
+                                .prop('enctype', 'multipart/form-data')
+                                // enctype must be set as encoding for IE:
+                                .prop('encoding', 'multipart/form-data');
+                            // Remove the HTML5 form attribute from the input(s):
+                            options.fileInput.removeAttr('form');
+                        }
+                        form.submit();
+                        // Insert the file input fields at their original location
+                        // by replacing the clones with the originals:
+                        if (fileInputClones && fileInputClones.length) {
+                            options.fileInput.each(function (index, input) {
+                                var clone = $(fileInputClones[index]);
+                                // Restore the original name and form properties:
+                                $(input)
+                                    .prop('name', clone.prop('name'))
+                                    .attr('form', clone.attr('form'));
+                                clone.replaceWith(input);
+                            });
+                        }
+                    });
+                    form.append(iframe).appendTo(document.body);
+                },
+                abort: function () {
+                    if (iframe) {
+                        // javascript:false as iframe src aborts the request
+                        // and prevents warning popups on HTTPS in IE6.
+                        // concat is used to avoid the "Script URL" JSLint error:
+                        iframe
+                            .unbind('load')
+                            .prop('src', initialIframeSrc);
+                    }
+                    if (form) {
+                        form.remove();
+                    }
+                }
+            };
+        }
+    });
+
+    // The iframe transport returns the iframe content document as response.
+    // The following adds converters from iframe to text, json, html, xml
+    // and script.
+    // Please note that the Content-Type for JSON responses has to be text/plain
+    // or text/html, if the browser doesn't include application/json in the
+    // Accept header, else IE will show a download dialog.
+    // The Content-Type for XML responses on the other hand has to be always
+    // application/xml or text/xml, so IE properly parses the XML response.
+    // See also
+    // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation
+    $.ajaxSetup({
+        converters: {
+            'iframe text': function (iframe) {
+                return iframe && $(iframe[0].body).text();
+            },
+            'iframe json': function (iframe) {
+                return iframe && $.parseJSON($(iframe[0].body).text());
+            },
+            'iframe html': function (iframe) {
+                return iframe && $(iframe[0].body).html();
+            },
+            'iframe xml': function (iframe) {
+                var xmlDoc = iframe && iframe[0];
+                return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc :
+                        $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) ||
+                            $(xmlDoc.body).html());
+            },
+            'iframe script': function (iframe) {
+                return iframe && $.globalEval($(iframe[0].body).text());
+            }
+        }
+    });
+
+}));

+ 5 - 5
app/Resources/public/assets/jquery/.bower.json

@@ -12,14 +12,14 @@
     "library"
   ],
   "homepage": "https://github.com/jquery/jquery-dist",
-  "version": "2.2.1",
-  "_release": "2.2.1",
+  "version": "2.2.0",
+  "_release": "2.2.0",
   "_resolution": {
     "type": "version",
-    "tag": "2.2.1",
-    "commit": "788eaba2f83e7b7445c7a83a50c81c0704423874"
+    "tag": "2.2.0",
+    "commit": "6fc01e29bdad0964f62ef56d01297039cdcadbe5"
   },
   "_source": "git://github.com/jquery/jquery-dist.git",
-  "_target": ">=1.6",
+  "_target": "2.2.0",
   "_originalSource": "jquery"
 }

+ 0 - 3
app/Resources/public/assets/jquery/AUTHORS.txt

@@ -273,6 +273,3 @@ Liza Ramo <liza.h.ramo@gmail.com>
 Julian Alexander Murillo <julian.alexander.murillo@gmail.com>
 Joelle Fleurantin <joasqueeniebee@gmail.com>
 Jun Sun <klsforever@gmail.com>
-Devin Wilson <dwilson6.github@gmail.com>
-Todor Prikumov <tono_pr@abv.bg>
-Zack Hall <zackhall@outlook.com>

+ 3 - 63
app/Resources/public/assets/jquery/README.md

@@ -1,65 +1,5 @@
-# jQuery
+# jQuery Dist
 
-> jQuery is a fast, small, and feature-rich JavaScript library.
+This repo only contains package distribution files for jQuery Core.
 
-For information on how to get started and how to use jQuery, please see [jQuery's documentation](http://api.jquery.com/).
-For source files and issues, please visit the [jQuery repo](https://github.com/jquery/jquery).
-
-## Including jQuery
-
-Below are some of the most common ways to include jQuery.
-
-### Browser
-
-#### Script tag
-
-```html
-<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
-```
-
-#### Babel
-
-[Babel](http://babeljs.io/) is a next generation JavaScript compiler. One of the features is the ability to use ES6/ES2015 modules now, even though browsers do not yet support this feature natively.
-
-```js
-import $ from "jquery";
-```
-
-#### Browserify/Webpack
-
-There are several ways to use [Browserify](http://browserify.org/) and [Webpack](https://webpack.github.io/). For more information on using these tools, please refer to the corresponding project's documention. In the script, including jQuery will usually look like this...
-
-```js
-var $ = require("jquery");
-```
-
-#### AMD (Asynchronous Module Definition)
-
-AMD is a module format built for the browser. For more information, we recommend [require.js' documentation](http://requirejs.org/docs/whyamd.html).
-
-```js
-define(["jquery"], function($) {
-
-});
-```
-
-### Node
-
-To include jQuery in [Node](nodejs.org), first install with npm.
-
-```sh
-npm install jquery
-```
-
-For jQuery to work in Node, a window with a document is required. Since no such window exists natively in Node, one can be mocked by tools such as [jsdom](https://github.com/tmpvar/jsdom). This can be useful for testing purposes.
-
-```js
-require("jsdom").env("", function(err, window) {
-	if (err) {
-		console.error(err);
-		return;
-	}
-
-	var $ = require("jquery")(window);
-});
-```
+For source files and issues, visit the [jQuery repo](https://github.com/jquery/jquery).

+ 22 - 22
app/Resources/public/assets/jquery/dist/jquery.js

@@ -1,5 +1,5 @@
 /*!
- * jQuery JavaScript Library v2.2.1
+ * jQuery JavaScript Library v2.2.0
  * http://jquery.com/
  *
  * Includes Sizzle.js
@@ -9,7 +9,7 @@
  * Released under the MIT license
  * http://jquery.org/license
  *
- * Date: 2016-02-22T19:11Z
+ * Date: 2016-01-08T20:02Z
  */
 
 (function( global, factory ) {
@@ -65,7 +65,7 @@ var support = {};
 
 
 var
-	version = "2.2.1",
+	version = "2.2.0",
 
 	// Define a local copy of jQuery
 	jQuery = function( selector, context ) {
@@ -4479,7 +4479,7 @@ function on( elem, types, selector, data, fn, one ) {
 	if ( fn === false ) {
 		fn = returnFalse;
 	} else if ( !fn ) {
-		return elem;
+		return this;
 	}
 
 	if ( one === 1 ) {
@@ -5128,14 +5128,14 @@ var
 	rscriptTypeMasked = /^true\/(.*)/,
 	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
 
-// Manipulating tables requires a tbody
 function manipulationTarget( elem, content ) {
-	return jQuery.nodeName( elem, "table" ) &&
-		jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
+	if ( jQuery.nodeName( elem, "table" ) &&
+		jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {
 
-		elem.getElementsByTagName( "tbody" )[ 0 ] ||
-			elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) :
-		elem;
+		return elem.getElementsByTagName( "tbody" )[ 0 ] || elem;
+	}
+
+	return elem;
 }
 
 // Replace/restore the type attribute of script elements for safe DOM manipulation
@@ -5642,7 +5642,7 @@ var getStyles = function( elem ) {
 		// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
 		var view = elem.ownerDocument.defaultView;
 
-		if ( !view || !view.opener ) {
+		if ( !view.opener ) {
 			view = window;
 		}
 
@@ -5791,18 +5791,15 @@ function curCSS( elem, name, computed ) {
 		style = elem.style;
 
 	computed = computed || getStyles( elem );
-	ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;
-
-	// Support: Opera 12.1x only
-	// Fall back to style even without computed
-	// computed is undefined for elems on document fragments
-	if ( ( ret === "" || ret === undefined ) && !jQuery.contains( elem.ownerDocument, elem ) ) {
-		ret = jQuery.style( elem, name );
-	}
 
 	// Support: IE9
 	// getPropertyValue is only needed for .css('filter') (#12537)
 	if ( computed ) {
+		ret = computed.getPropertyValue( name ) || computed[ name ];
+
+		if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+			ret = jQuery.style( elem, name );
+		}
 
 		// A tribute to the "awesome hack by Dean Edwards"
 		// Android Browser returns percentage for some values,
@@ -7852,7 +7849,7 @@ jQuery.extend( jQuery.event, {
 				// But now, this "simulate" function is used only for events
 				// for which stopPropagation() is noop, so there is no need for that anymore.
 				//
-				// For the 1.x branch though, guard for "click" and "submit"
+				// For the compat branch though, guard for "click" and "submit"
 				// events is still used, but was moved to jQuery.event.stopPropagation function
 				// because `originalEvent` should point to the original event for the constancy
 				// with other events and for more focused logic
@@ -9622,8 +9619,11 @@ jQuery.fn.extend( {
 			}
 
 			// Add offsetParent borders
-			parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
-			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
+			// Subtract offsetParent scroll positions
+			parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ) -
+				offsetParent.scrollTop();
+			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true ) -
+				offsetParent.scrollLeft();
 		}
 
 		// Subtract parent offsets and element margins

File diff ditekan karena terlalu besar
+ 0 - 1
app/Resources/public/assets/jquery/dist/jquery.min.js


File diff ditekan karena terlalu besar
+ 0 - 0
app/Resources/public/assets/jquery/dist/jquery.min.map


+ 0 - 7877
app/Resources/public/assets/jquery/dist/jquery.slim.js

@@ -1,7877 +0,0 @@
-/*!
- * jQuery JavaScript Library v3.0.0-beta1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseJSON,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/Tween,-effects/animatedSelector,-deprecated
- * https://jquery.com/
- *
- * Includes Sizzle.js
- * https://sizzlejs.com/
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license
- * https://jquery.org/license
- *
- * Date: 2016-01-14T23:07Z
- */
-
-(function( global, factory ) {
-
-	if ( typeof module === "object" && typeof module.exports === "object" ) {
-		// For CommonJS and CommonJS-like environments where a proper `window`
-		// is present, execute the factory and get jQuery.
-		// For environments that do not have a `window` with a `document`
-		// (such as Node.js), expose a factory as module.exports.
-		// This accentuates the need for the creation of a real `window`.
-		// e.g. var jQuery = require("jquery")(window);
-		// See ticket #14549 for more info.
-		module.exports = global.document ?
-			factory( global, true ) :
-			function( w ) {
-				if ( !w.document ) {
-					throw new Error( "jQuery requires a window with a document" );
-				}
-				return factory( w );
-			};
-	} else {
-		factory( global );
-	}
-
-// Pass this if window is not defined yet
-}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
-
-// Support: Firefox 18+
-// Can't be in strict mode, several libs including ASP.NET trace
-// the stack via arguments.caller.callee and Firefox dies if
-// you try to trace through "use strict" call chains. (#13335)
-//"use strict";
-var arr = [];
-
-var document = window.document;
-
-var slice = arr.slice;
-
-var concat = arr.concat;
-
-var push = arr.push;
-
-var indexOf = arr.indexOf;
-
-var class2type = {};
-
-var toString = class2type.toString;
-
-var hasOwn = class2type.hasOwnProperty;
-
-var support = {};
-
-
-	function DOMEval( code, doc ) {
-		doc = doc || document;
-
-		var script = doc.createElement( "script" );
-
-		script.text = code;
-		doc.head.appendChild( script ).parentNode.removeChild( script );
-	}
-
-
-var
-	version = "3.0.0-beta1 -ajax,-ajax/jsonp,-ajax/load,-ajax/parseJSON,-ajax/parseXML,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-event/ajax,-effects,-effects/Tween,-effects/animatedSelector,-deprecated",
-
-	// Define a local copy of jQuery
-	jQuery = function( selector, context ) {
-
-		// The jQuery object is actually just the init constructor 'enhanced'
-		// Need init if jQuery is called (just allow error to be thrown if not included)
-		return new jQuery.fn.init( selector, context );
-	},
-
-	// Support: Android<4.1
-	// Make sure we trim BOM and NBSP
-	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
-
-	// Matches dashed string for camelizing
-	rmsPrefix = /^-ms-/,
-	rdashAlpha = /-([a-z])/g,
-
-	// Used by jQuery.camelCase as callback to replace()
-	fcamelCase = function( all, letter ) {
-		return letter.toUpperCase();
-	};
-
-jQuery.fn = jQuery.prototype = {
-
-	// The current version of jQuery being used
-	jquery: version,
-
-	constructor: jQuery,
-
-	// The default length of a jQuery object is 0
-	length: 0,
-
-	toArray: function() {
-		return slice.call( this );
-	},
-
-	// Get the Nth element in the matched element set OR
-	// Get the whole matched element set as a clean array
-	get: function( num ) {
-		return num != null ?
-
-			// Return just the one element from the set
-			( num < 0 ? this[ num + this.length ] : this[ num ] ) :
-
-			// Return all the elements in a clean array
-			slice.call( this );
-	},
-
-	// Take an array of elements and push it onto the stack
-	// (returning the new matched element set)
-	pushStack: function( elems ) {
-
-		// Build a new jQuery matched element set
-		var ret = jQuery.merge( this.constructor(), elems );
-
-		// Add the old object onto the stack (as a reference)
-		ret.prevObject = this;
-
-		// Return the newly-formed element set
-		return ret;
-	},
-
-	// Execute a callback for every element in the matched set.
-	each: function( callback ) {
-		return jQuery.each( this, callback );
-	},
-
-	map: function( callback ) {
-		return this.pushStack( jQuery.map( this, function( elem, i ) {
-			return callback.call( elem, i, elem );
-		} ) );
-	},
-
-	slice: function() {
-		return this.pushStack( slice.apply( this, arguments ) );
-	},
-
-	first: function() {
-		return this.eq( 0 );
-	},
-
-	last: function() {
-		return this.eq( -1 );
-	},
-
-	eq: function( i ) {
-		var len = this.length,
-			j = +i + ( i < 0 ? len : 0 );
-		return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
-	},
-
-	end: function() {
-		return this.prevObject || this.constructor();
-	},
-
-	// For internal use only.
-	// Behaves like an Array's method, not like a jQuery method.
-	push: push,
-	sort: arr.sort,
-	splice: arr.splice
-};
-
-jQuery.extend = jQuery.fn.extend = function() {
-	var options, name, src, copy, copyIsArray, clone,
-		target = arguments[ 0 ] || {},
-		i = 1,
-		length = arguments.length,
-		deep = false;
-
-	// Handle a deep copy situation
-	if ( typeof target === "boolean" ) {
-		deep = target;
-
-		// Skip the boolean and the target
-		target = arguments[ i ] || {};
-		i++;
-	}
-
-	// Handle case when target is a string or something (possible in deep copy)
-	if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
-		target = {};
-	}
-
-	// Extend jQuery itself if only one argument is passed
-	if ( i === length ) {
-		target = this;
-		i--;
-	}
-
-	for ( ; i < length; i++ ) {
-
-		// Only deal with non-null/undefined values
-		if ( ( options = arguments[ i ] ) != null ) {
-
-			// Extend the base object
-			for ( name in options ) {
-				src = target[ name ];
-				copy = options[ name ];
-
-				// Prevent never-ending loop
-				if ( target === copy ) {
-					continue;
-				}
-
-				// Recurse if we're merging plain objects or arrays
-				if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
-					( copyIsArray = jQuery.isArray( copy ) ) ) ) {
-
-					if ( copyIsArray ) {
-						copyIsArray = false;
-						clone = src && jQuery.isArray( src ) ? src : [];
-
-					} else {
-						clone = src && jQuery.isPlainObject( src ) ? src : {};
-					}
-
-					// Never move original objects, clone them
-					target[ name ] = jQuery.extend( deep, clone, copy );
-
-				// Don't bring in undefined values
-				} else if ( copy !== undefined ) {
-					target[ name ] = copy;
-				}
-			}
-		}
-	}
-
-	// Return the modified object
-	return target;
-};
-
-jQuery.extend( {
-
-	// Unique for each copy of jQuery on the page
-	expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
-
-	// Assume jQuery is ready without the ready module
-	isReady: true,
-
-	error: function( msg ) {
-		throw new Error( msg );
-	},
-
-	noop: function() {},
-
-	isFunction: function( obj ) {
-		return jQuery.type( obj ) === "function";
-	},
-
-	isArray: Array.isArray,
-
-	isWindow: function( obj ) {
-		return obj != null && obj === obj.window;
-	},
-
-	isNumeric: function( obj ) {
-
-		// As of jQuery 3.0, isNumeric is limited to
-		// strings and numbers (primitives or objects)
-		// that can be coerced to finite numbers (gh-2662)
-		var type = jQuery.type( obj );
-		return ( type === "number" || type === "string" ) &&
-			( obj - parseFloat( obj ) + 1 ) >= 0;
-	},
-
-	isPlainObject: function( obj ) {
-
-		// Not plain objects:
-		// - Any object or value whose internal [[Class]] property is not "[object Object]"
-		// - DOM nodes
-		// - window
-		if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
-			return false;
-		}
-
-		if ( obj.constructor &&
-				!hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
-			return false;
-		}
-
-		// If the function hasn't returned already, we're confident that
-		// |obj| is a plain object, created by {} or constructed with new Object
-		return true;
-	},
-
-	isEmptyObject: function( obj ) {
-		var name;
-		for ( name in obj ) {
-			return false;
-		}
-		return true;
-	},
-
-	type: function( obj ) {
-		if ( obj == null ) {
-			return obj + "";
-		}
-
-		// Support: Android<4.0 (functionish RegExp)
-		return typeof obj === "object" || typeof obj === "function" ?
-			class2type[ toString.call( obj ) ] || "object" :
-			typeof obj;
-	},
-
-	// Evaluates a script in a global context
-	globalEval: function( code ) {
-		DOMEval( code );
-	},
-
-	// Convert dashed to camelCase; used by the css and data modules
-	// Support: IE9-11+
-	// Microsoft forgot to hump their vendor prefix (#9572)
-	camelCase: function( string ) {
-		return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
-	},
-
-	nodeName: function( elem, name ) {
-		return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
-	},
-
-	each: function( obj, callback ) {
-		var length, i = 0;
-
-		if ( isArrayLike( obj ) ) {
-			length = obj.length;
-			for ( ; i < length; i++ ) {
-				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
-					break;
-				}
-			}
-		} else {
-			for ( i in obj ) {
-				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
-					break;
-				}
-			}
-		}
-
-		return obj;
-	},
-
-	// Support: Android<4.1
-	trim: function( text ) {
-		return text == null ?
-			"" :
-			( text + "" ).replace( rtrim, "" );
-	},
-
-	// results is for internal usage only
-	makeArray: function( arr, results ) {
-		var ret = results || [];
-
-		if ( arr != null ) {
-			if ( isArrayLike( Object( arr ) ) ) {
-				jQuery.merge( ret,
-					typeof arr === "string" ?
-					[ arr ] : arr
-				);
-			} else {
-				push.call( ret, arr );
-			}
-		}
-
-		return ret;
-	},
-
-	inArray: function( elem, arr, i ) {
-		return arr == null ? -1 : indexOf.call( arr, elem, i );
-	},
-
-	// Support: Android<4.1, PhantomJS<2
-	// push.apply(_, arraylike) throws on ancient WebKit
-	merge: function( first, second ) {
-		var len = +second.length,
-			j = 0,
-			i = first.length;
-
-		for ( ; j < len; j++ ) {
-			first[ i++ ] = second[ j ];
-		}
-
-		first.length = i;
-
-		return first;
-	},
-
-	grep: function( elems, callback, invert ) {
-		var callbackInverse,
-			matches = [],
-			i = 0,
-			length = elems.length,
-			callbackExpect = !invert;
-
-		// Go through the array, only saving the items
-		// that pass the validator function
-		for ( ; i < length; i++ ) {
-			callbackInverse = !callback( elems[ i ], i );
-			if ( callbackInverse !== callbackExpect ) {
-				matches.push( elems[ i ] );
-			}
-		}
-
-		return matches;
-	},
-
-	// arg is for internal usage only
-	map: function( elems, callback, arg ) {
-		var length, value,
-			i = 0,
-			ret = [];
-
-		// Go through the array, translating each of the items to their new values
-		if ( isArrayLike( elems ) ) {
-			length = elems.length;
-			for ( ; i < length; i++ ) {
-				value = callback( elems[ i ], i, arg );
-
-				if ( value != null ) {
-					ret.push( value );
-				}
-			}
-
-		// Go through every key on the object,
-		} else {
-			for ( i in elems ) {
-				value = callback( elems[ i ], i, arg );
-
-				if ( value != null ) {
-					ret.push( value );
-				}
-			}
-		}
-
-		// Flatten any nested arrays
-		return concat.apply( [], ret );
-	},
-
-	// A global GUID counter for objects
-	guid: 1,
-
-	// Bind a function to a context, optionally partially applying any
-	// arguments.
-	proxy: function( fn, context ) {
-		var tmp, args, proxy;
-
-		if ( typeof context === "string" ) {
-			tmp = fn[ context ];
-			context = fn;
-			fn = tmp;
-		}
-
-		// Quick check to determine if target is callable, in the spec
-		// this throws a TypeError, but we will just return undefined.
-		if ( !jQuery.isFunction( fn ) ) {
-			return undefined;
-		}
-
-		// Simulated bind
-		args = slice.call( arguments, 2 );
-		proxy = function() {
-			return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
-		};
-
-		// Set the guid of unique handler to the same of original handler, so it can be removed
-		proxy.guid = fn.guid = fn.guid || jQuery.guid++;
-
-		return proxy;
-	},
-
-	now: Date.now,
-
-	// jQuery.support is not used in Core but other projects attach their
-	// properties to it so it needs to exist.
-	support: support
-} );
-
-// JSHint would error on this code due to the Symbol not being defined in ES5.
-// Defining this global in .jshintrc would create a danger of using the global
-// unguarded in another place, it seems safer to just disable JSHint for these
-// three lines.
-/* jshint ignore: start */
-if ( typeof Symbol === "function" ) {
-	jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
-}
-/* jshint ignore: end */
-
-// Populate the class2type map
-jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
-function( i, name ) {
-	class2type[ "[object " + name + "]" ] = name.toLowerCase();
-} );
-
-function isArrayLike( obj ) {
-
-	// Support: iOS 8.2 (not reproducible in simulator)
-	// `in` check used to prevent JIT error (gh-2145)
-	// hasOwn isn't used here due to false negatives
-	// regarding Nodelist length in IE
-	var length = !!obj && "length" in obj && obj.length,
-		type = jQuery.type( obj );
-
-	if ( type === "function" || jQuery.isWindow( obj ) ) {
-		return false;
-	}
-
-	return type === "array" || length === 0 ||
-		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
-}
-var Sizzle =
-/*!
- * Sizzle CSS Selector Engine v2.3.0
- * https://sizzlejs.com/
- *
- * Copyright jQuery Foundation and other contributors
- * Released under the MIT license
- * http://jquery.org/license
- *
- * Date: 2016-01-04
- */
-(function( window ) {
-
-var i,
-	support,
-	Expr,
-	getText,
-	isXML,
-	tokenize,
-	compile,
-	select,
-	outermostContext,
-	sortInput,
-	hasDuplicate,
-
-	// Local document vars
-	setDocument,
-	document,
-	docElem,
-	documentIsHTML,
-	rbuggyQSA,
-	rbuggyMatches,
-	matches,
-	contains,
-
-	// Instance-specific data
-	expando = "sizzle" + 1 * new Date(),
-	preferredDoc = window.document,
-	dirruns = 0,
-	done = 0,
-	classCache = createCache(),
-	tokenCache = createCache(),
-	compilerCache = createCache(),
-	sortOrder = function( a, b ) {
-		if ( a === b ) {
-			hasDuplicate = true;
-		}
-		return 0;
-	},
-
-	// Instance methods
-	hasOwn = ({}).hasOwnProperty,
-	arr = [],
-	pop = arr.pop,
-	push_native = arr.push,
-	push = arr.push,
-	slice = arr.slice,
-	// Use a stripped-down indexOf as it's faster than native
-	// https://jsperf.com/thor-indexof-vs-for/5
-	indexOf = function( list, elem ) {
-		var i = 0,
-			len = list.length;
-		for ( ; i < len; i++ ) {
-			if ( list[i] === elem ) {
-				return i;
-			}
-		}
-		return -1;
-	},
-
-	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
-
-	// Regular expressions
-
-	// http://www.w3.org/TR/css3-selectors/#whitespace
-	whitespace = "[\\x20\\t\\r\\n\\f]",
-
-	// http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
-	identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+",
-
-	// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
-	attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
-		// Operator (capture 2)
-		"*([*^$|!~]?=)" + whitespace +
-		// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
-		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
-		"*\\]",
-
-	pseudos = ":(" + identifier + ")(?:\\((" +
-		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
-		// 1. quoted (capture 3; capture 4 or capture 5)
-		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
-		// 2. simple (capture 6)
-		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
-		// 3. anything else (capture 2)
-		".*" +
-		")\\)|)",
-
-	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
-	rwhitespace = new RegExp( whitespace + "+", "g" ),
-	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
-
-	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
-	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
-
-	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
-
-	rpseudo = new RegExp( pseudos ),
-	ridentifier = new RegExp( "^" + identifier + "$" ),
-
-	matchExpr = {
-		"ID": new RegExp( "^#(" + identifier + ")" ),
-		"CLASS": new RegExp( "^\\.(" + identifier + ")" ),
-		"TAG": new RegExp( "^(" + identifier + "|[*])" ),
-		"ATTR": new RegExp( "^" + attributes ),
-		"PSEUDO": new RegExp( "^" + pseudos ),
-		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
-			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
-			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
-		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
-		// For use in libraries implementing .is()
-		// We use this for POS matching in `select`
-		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
-			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
-	},
-
-	rinputs = /^(?:input|select|textarea|button)$/i,
-	rheader = /^h\d$/i,
-
-	rnative = /^[^{]+\{\s*\[native \w/,
-
-	// Easily-parseable/retrievable ID or TAG or CLASS selectors
-	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
-
-	rsibling = /[+~]/,
-
-	// CSS escapes
-	// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
-	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
-	funescape = function( _, escaped, escapedWhitespace ) {
-		var high = "0x" + escaped - 0x10000;
-		// NaN means non-codepoint
-		// Support: Firefox<24
-		// Workaround erroneous numeric interpretation of +"0x"
-		return high !== high || escapedWhitespace ?
-			escaped :
-			high < 0 ?
-				// BMP codepoint
-				String.fromCharCode( high + 0x10000 ) :
-				// Supplemental Plane codepoint (surrogate pair)
-				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
-	},
-
-	// CSS string/identifier serialization
-	// https://drafts.csswg.org/cssom/#common-serializing-idioms
-	rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,
-	fcssescape = function( ch, asCodePoint ) {
-		if ( asCodePoint ) {
-
-			// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
-			if ( ch === "\0" ) {
-				return "\uFFFD";
-			}
-
-			// Control characters and (dependent upon position) numbers get escaped as code points
-			return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
-		}
-
-		// Other potentially-special ASCII characters get backslash-escaped
-		return "\\" + ch;
-	},
-
-	// Used for iframes
-	// See setDocument()
-	// Removing the function wrapper causes a "Permission Denied"
-	// error in IE
-	unloadHandler = function() {
-		setDocument();
-	},
-
-	disabledAncestor = addCombinator(
-		function( elem ) {
-			return elem.disabled === true;
-		},
-		{ dir: "parentNode", next: "legend" }
-	);
-
-// Optimize for push.apply( _, NodeList )
-try {
-	push.apply(
-		(arr = slice.call( preferredDoc.childNodes )),
-		preferredDoc.childNodes
-	);
-	// Support: Android<4.0
-	// Detect silently failing push.apply
-	arr[ preferredDoc.childNodes.length ].nodeType;
-} catch ( e ) {
-	push = { apply: arr.length ?
-
-		// Leverage slice if possible
-		function( target, els ) {
-			push_native.apply( target, slice.call(els) );
-		} :
-
-		// Support: IE<9
-		// Otherwise append directly
-		function( target, els ) {
-			var j = target.length,
-				i = 0;
-			// Can't trust NodeList.length
-			while ( (target[j++] = els[i++]) ) {}
-			target.length = j - 1;
-		}
-	};
-}
-
-function Sizzle( selector, context, results, seed ) {
-	var m, i, elem, nid, match, groups, newSelector,
-		newContext = context && context.ownerDocument,
-
-		// nodeType defaults to 9, since context defaults to document
-		nodeType = context ? context.nodeType : 9;
-
-	results = results || [];
-
-	// Return early from calls with invalid selector or context
-	if ( typeof selector !== "string" || !selector ||
-		nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
-
-		return results;
-	}
-
-	// Try to shortcut find operations (as opposed to filters) in HTML documents
-	if ( !seed ) {
-
-		if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
-			setDocument( context );
-		}
-		context = context || document;
-
-		if ( documentIsHTML ) {
-
-			// If the selector is sufficiently simple, try using a "get*By*" DOM method
-			// (excepting DocumentFragment context, where the methods don't exist)
-			if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
-
-				// ID selector
-				if ( (m = match[1]) ) {
-
-					// Document context
-					if ( nodeType === 9 ) {
-						if ( (elem = context.getElementById( m )) ) {
-
-							// Support: IE, Opera, Webkit
-							// TODO: identify versions
-							// getElementById can match elements by name instead of ID
-							if ( elem.id === m ) {
-								results.push( elem );
-								return results;
-							}
-						} else {
-							return results;
-						}
-
-					// Element context
-					} else {
-
-						// Support: IE, Opera, Webkit
-						// TODO: identify versions
-						// getElementById can match elements by name instead of ID
-						if ( newContext && (elem = newContext.getElementById( m )) &&
-							contains( context, elem ) &&
-							elem.id === m ) {
-
-							results.push( elem );
-							return results;
-						}
-					}
-
-				// Type selector
-				} else if ( match[2] ) {
-					push.apply( results, context.getElementsByTagName( selector ) );
-					return results;
-
-				// Class selector
-				} else if ( (m = match[3]) && support.getElementsByClassName &&
-					context.getElementsByClassName ) {
-
-					push.apply( results, context.getElementsByClassName( m ) );
-					return results;
-				}
-			}
-
-			// Take advantage of querySelectorAll
-			if ( support.qsa &&
-				!compilerCache[ selector + " " ] &&
-				(!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
-
-				if ( nodeType !== 1 ) {
-					newContext = context;
-					newSelector = selector;
-
-				// qSA looks outside Element context, which is not what we want
-				// Thanks to Andrew Dupont for this workaround technique
-				// Support: IE <=8
-				// Exclude object elements
-				} else if ( context.nodeName.toLowerCase() !== "object" ) {
-
-					// Capture the context ID, setting it first if necessary
-					if ( (nid = context.getAttribute( "id" )) ) {
-						nid = nid.replace( rcssescape, fcssescape );
-					} else {
-						context.setAttribute( "id", (nid = expando) );
-					}
-
-					// Prefix every selector in the list
-					groups = tokenize( selector );
-					i = groups.length;
-					while ( i-- ) {
-						groups[i] = "#" + nid + " " + toSelector( groups[i] );
-					}
-					newSelector = groups.join( "," );
-
-					// Expand context for sibling selectors
-					newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
-						context;
-				}
-
-				if ( newSelector ) {
-					try {
-						push.apply( results,
-							newContext.querySelectorAll( newSelector )
-						);
-						return results;
-					} catch ( qsaError ) {
-					} finally {
-						if ( nid === expando ) {
-							context.removeAttribute( "id" );
-						}
-					}
-				}
-			}
-		}
-	}
-
-	// All others
-	return select( selector.replace( rtrim, "$1" ), context, results, seed );
-}
-
-/**
- * Create key-value caches of limited size
- * @returns {function(string, object)} Returns the Object data after storing it on itself with
- *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
- *	deleting the oldest entry
- */
-function createCache() {
-	var keys = [];
-
-	function cache( key, value ) {
-		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
-		if ( keys.push( key + " " ) > Expr.cacheLength ) {
-			// Only keep the most recent entries
-			delete cache[ keys.shift() ];
-		}
-		return (cache[ key + " " ] = value);
-	}
-	return cache;
-}
-
-/**
- * Mark a function for special use by Sizzle
- * @param {Function} fn The function to mark
- */
-function markFunction( fn ) {
-	fn[ expando ] = true;
-	return fn;
-}
-
-/**
- * Support testing using an element
- * @param {Function} fn Passed the created element and returns a boolean result
- */
-function assert( fn ) {
-	var el = document.createElement("fieldset");
-
-	try {
-		return !!fn( el );
-	} catch (e) {
-		return false;
-	} finally {
-		// Remove from its parent by default
-		if ( el.parentNode ) {
-			el.parentNode.removeChild( el );
-		}
-		// release memory in IE
-		el = null;
-	}
-}
-
-/**
- * Adds the same handler for all of the specified attrs
- * @param {String} attrs Pipe-separated list of attributes
- * @param {Function} handler The method that will be applied
- */
-function addHandle( attrs, handler ) {
-	var arr = attrs.split("|"),
-		i = arr.length;
-
-	while ( i-- ) {
-		Expr.attrHandle[ arr[i] ] = handler;
-	}
-}
-
-/**
- * Checks document order of two siblings
- * @param {Element} a
- * @param {Element} b
- * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
- */
-function siblingCheck( a, b ) {
-	var cur = b && a,
-		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
-			a.sourceIndex - b.sourceIndex;
-
-	// Use IE sourceIndex if available on both nodes
-	if ( diff ) {
-		return diff;
-	}
-
-	// Check if b follows a
-	if ( cur ) {
-		while ( (cur = cur.nextSibling) ) {
-			if ( cur === b ) {
-				return -1;
-			}
-		}
-	}
-
-	return a ? 1 : -1;
-}
-
-/**
- * Returns a function to use in pseudos for input types
- * @param {String} type
- */
-function createInputPseudo( type ) {
-	return function( elem ) {
-		var name = elem.nodeName.toLowerCase();
-		return name === "input" && elem.type === type;
-	};
-}
-
-/**
- * Returns a function to use in pseudos for buttons
- * @param {String} type
- */
-function createButtonPseudo( type ) {
-	return function( elem ) {
-		var name = elem.nodeName.toLowerCase();
-		return (name === "input" || name === "button") && elem.type === type;
-	};
-}
-
-/**
- * Returns a function to use in pseudos for :enabled/:disabled
- * @param {Boolean} disabled true for :disabled; false for :enabled
- */
-function createDisabledPseudo( disabled ) {
-	// Known :disabled false positives:
-	// IE: *[disabled]:not(button, input, select, textarea, optgroup, option, menuitem, fieldset)
-	// not IE: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
-	return function( elem ) {
-
-		// Check form elements and option elements for explicit disabling
-		return "label" in elem && elem.disabled === disabled ||
-			"form" in elem && elem.disabled === disabled ||
-
-			// Check non-disabled form elements for fieldset[disabled] ancestors
-			"form" in elem && elem.disabled === false && (
-				// Support: IE6-11+
-				// Ancestry is covered for us
-				elem.isDisabled === disabled ||
-
-				// Otherwise, assume any non-<option> under fieldset[disabled] is disabled
-				/* jshint -W018 */
-				elem.isDisabled !== !disabled &&
-					("label" in elem || !disabledAncestor( elem )) !== disabled
-			);
-	};
-}
-
-/**
- * Returns a function to use in pseudos for positionals
- * @param {Function} fn
- */
-function createPositionalPseudo( fn ) {
-	return markFunction(function( argument ) {
-		argument = +argument;
-		return markFunction(function( seed, matches ) {
-			var j,
-				matchIndexes = fn( [], seed.length, argument ),
-				i = matchIndexes.length;
-
-			// Match elements found at the specified indexes
-			while ( i-- ) {
-				if ( seed[ (j = matchIndexes[i]) ] ) {
-					seed[j] = !(matches[j] = seed[j]);
-				}
-			}
-		});
-	});
-}
-
-/**
- * Checks a node for validity as a Sizzle context
- * @param {Element|Object=} context
- * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
- */
-function testContext( context ) {
-	return context && typeof context.getElementsByTagName !== "undefined" && context;
-}
-
-// Expose support vars for convenience
-support = Sizzle.support = {};
-
-/**
- * Detects XML nodes
- * @param {Element|Object} elem An element or a document
- * @returns {Boolean} True iff elem is a non-HTML XML node
- */
-isXML = Sizzle.isXML = function( elem ) {
-	// documentElement is verified for cases where it doesn't yet exist
-	// (such as loading iframes in IE - #4833)
-	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
-	return documentElement ? documentElement.nodeName !== "HTML" : false;
-};
-
-/**
- * Sets document-related variables once based on the current document
- * @param {Element|Object} [doc] An element or document object to use to set the document
- * @returns {Object} Returns the current document
- */
-setDocument = Sizzle.setDocument = function( node ) {
-	var hasCompare, subWindow,
-		doc = node ? node.ownerDocument || node : preferredDoc;
-
-	// Return early if doc is invalid or already selected
-	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
-		return document;
-	}
-
-	// Update global variables
-	document = doc;
-	docElem = document.documentElement;
-	documentIsHTML = !isXML( document );
-
-	// Support: IE 9-11, Edge
-	// Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
-	if ( preferredDoc !== document &&
-		(subWindow = document.defaultView) && subWindow.top !== subWindow ) {
-
-		// Support: IE 11, Edge
-		if ( subWindow.addEventListener ) {
-			subWindow.addEventListener( "unload", unloadHandler, false );
-
-		// Support: IE 9 - 10 only
-		} else if ( subWindow.attachEvent ) {
-			subWindow.attachEvent( "onunload", unloadHandler );
-		}
-	}
-
-	/* Attributes
-	---------------------------------------------------------------------- */
-
-	// Support: IE<8
-	// Verify that getAttribute really returns attributes and not properties
-	// (excepting IE8 booleans)
-	support.attributes = assert(function( el ) {
-		el.className = "i";
-		return !el.getAttribute("className");
-	});
-
-	/* getElement(s)By*
-	---------------------------------------------------------------------- */
-
-	// Check if getElementsByTagName("*") returns only elements
-	support.getElementsByTagName = assert(function( el ) {
-		el.appendChild( document.createComment("") );
-		return !el.getElementsByTagName("*").length;
-	});
-
-	// Support: IE<9
-	support.getElementsByClassName = rnative.test( document.getElementsByClassName );
-
-	// Support: IE<10
-	// Check if getElementById returns elements by name
-	// The broken getElementById methods don't pick up programmatically-set names,
-	// so use a roundabout getElementsByName test
-	support.getById = assert(function( el ) {
-		docElem.appendChild( el ).id = expando;
-		return !document.getElementsByName || !document.getElementsByName( expando ).length;
-	});
-
-	// ID find and filter
-	if ( support.getById ) {
-		Expr.find["ID"] = function( id, context ) {
-			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
-				var m = context.getElementById( id );
-				return m ? [ m ] : [];
-			}
-		};
-		Expr.filter["ID"] = function( id ) {
-			var attrId = id.replace( runescape, funescape );
-			return function( elem ) {
-				return elem.getAttribute("id") === attrId;
-			};
-		};
-	} else {
-		// Support: IE6/7
-		// getElementById is not reliable as a find shortcut
-		delete Expr.find["ID"];
-
-		Expr.filter["ID"] =  function( id ) {
-			var attrId = id.replace( runescape, funescape );
-			return function( elem ) {
-				var node = typeof elem.getAttributeNode !== "undefined" &&
-					elem.getAttributeNode("id");
-				return node && node.value === attrId;
-			};
-		};
-	}
-
-	// Tag
-	Expr.find["TAG"] = support.getElementsByTagName ?
-		function( tag, context ) {
-			if ( typeof context.getElementsByTagName !== "undefined" ) {
-				return context.getElementsByTagName( tag );
-
-			// DocumentFragment nodes don't have gEBTN
-			} else if ( support.qsa ) {
-				return context.querySelectorAll( tag );
-			}
-		} :
-
-		function( tag, context ) {
-			var elem,
-				tmp = [],
-				i = 0,
-				// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
-				results = context.getElementsByTagName( tag );
-
-			// Filter out possible comments
-			if ( tag === "*" ) {
-				while ( (elem = results[i++]) ) {
-					if ( elem.nodeType === 1 ) {
-						tmp.push( elem );
-					}
-				}
-
-				return tmp;
-			}
-			return results;
-		};
-
-	// Class
-	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
-		if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
-			return context.getElementsByClassName( className );
-		}
-	};
-
-	/* QSA/matchesSelector
-	---------------------------------------------------------------------- */
-
-	// QSA and matchesSelector support
-
-	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
-	rbuggyMatches = [];
-
-	// qSa(:focus) reports false when true (Chrome 21)
-	// We allow this because of a bug in IE8/9 that throws an error
-	// whenever `document.activeElement` is accessed on an iframe
-	// So, we allow :focus to pass through QSA all the time to avoid the IE error
-	// See https://bugs.jquery.com/ticket/13378
-	rbuggyQSA = [];
-
-	if ( (support.qsa = rnative.test( document.querySelectorAll )) ) {
-		// Build QSA regex
-		// Regex strategy adopted from Diego Perini
-		assert(function( el ) {
-			// Select is set to empty string on purpose
-			// This is to test IE's treatment of not explicitly
-			// setting a boolean content attribute,
-			// since its presence should be enough
-			// https://bugs.jquery.com/ticket/12359
-			docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" +
-				"<select id='" + expando + "-\r\\' msallowcapture=''>" +
-				"<option selected=''></option></select>";
-
-			// Support: IE8, Opera 11-12.16
-			// Nothing should be selected when empty strings follow ^= or $= or *=
-			// The test attribute must be unknown in Opera but "safe" for WinRT
-			// https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
-			if ( el.querySelectorAll("[msallowcapture^='']").length ) {
-				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
-			}
-
-			// Support: IE8
-			// Boolean attributes and "value" are not treated correctly
-			if ( !el.querySelectorAll("[selected]").length ) {
-				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
-			}
-
-			// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
-			if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
-				rbuggyQSA.push("~=");
-			}
-
-			// Webkit/Opera - :checked should return selected option elements
-			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
-			// IE8 throws error here and will not see later tests
-			if ( !el.querySelectorAll(":checked").length ) {
-				rbuggyQSA.push(":checked");
-			}
-
-			// Support: Safari 8+, iOS 8+
-			// https://bugs.webkit.org/show_bug.cgi?id=136851
-			// In-page `selector#id sibling-combinator selector` fails
-			if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
-				rbuggyQSA.push(".#.+[+~]");
-			}
-		});
-
-		assert(function( el ) {
-			el.innerHTML = "<a href='' disabled='disabled'></a>" +
-				"<select disabled='disabled'><option/></select>";
-
-			// Support: Windows 8 Native Apps
-			// The type and name attributes are restricted during .innerHTML assignment
-			var input = document.createElement("input");
-			input.setAttribute( "type", "hidden" );
-			el.appendChild( input ).setAttribute( "name", "D" );
-
-			// Support: IE8
-			// Enforce case-sensitivity of name attribute
-			if ( el.querySelectorAll("[name=d]").length ) {
-				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
-			}
-
-			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
-			// IE8 throws error here and will not see later tests
-			if ( el.querySelectorAll(":enabled").length !== 2 ) {
-				rbuggyQSA.push( ":enabled", ":disabled" );
-			}
-
-			// Support: IE9-11+
-			// IE's :disabled selector does not pick up the children of disabled fieldsets
-			docElem.appendChild( el ).disabled = true;
-			if ( el.querySelectorAll(":disabled").length !== 2 ) {
-				rbuggyQSA.push( ":enabled", ":disabled" );
-			}
-
-			// Opera 10-11 does not throw on post-comma invalid pseudos
-			el.querySelectorAll("*,:x");
-			rbuggyQSA.push(",.*:");
-		});
-	}
-
-	if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
-		docElem.webkitMatchesSelector ||
-		docElem.mozMatchesSelector ||
-		docElem.oMatchesSelector ||
-		docElem.msMatchesSelector) )) ) {
-
-		assert(function( el ) {
-			// Check to see if it's possible to do matchesSelector
-			// on a disconnected node (IE 9)
-			support.disconnectedMatch = matches.call( el, "*" );
-
-			// This should fail with an exception
-			// Gecko does not error, returns false instead
-			matches.call( el, "[s!='']:x" );
-			rbuggyMatches.push( "!=", pseudos );
-		});
-	}
-
-	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
-	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
-
-	/* Contains
-	---------------------------------------------------------------------- */
-	hasCompare = rnative.test( docElem.compareDocumentPosition );
-
-	// Element contains another
-	// Purposefully self-exclusive
-	// As in, an element does not contain itself
-	contains = hasCompare || rnative.test( docElem.contains ) ?
-		function( a, b ) {
-			var adown = a.nodeType === 9 ? a.documentElement : a,
-				bup = b && b.parentNode;
-			return a === bup || !!( bup && bup.nodeType === 1 && (
-				adown.contains ?
-					adown.contains( bup ) :
-					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
-			));
-		} :
-		function( a, b ) {
-			if ( b ) {
-				while ( (b = b.parentNode) ) {
-					if ( b === a ) {
-						return true;
-					}
-				}
-			}
-			return false;
-		};
-
-	/* Sorting
-	---------------------------------------------------------------------- */
-
-	// Document order sorting
-	sortOrder = hasCompare ?
-	function( a, b ) {
-
-		// Flag for duplicate removal
-		if ( a === b ) {
-			hasDuplicate = true;
-			return 0;
-		}
-
-		// Sort on method existence if only one input has compareDocumentPosition
-		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
-		if ( compare ) {
-			return compare;
-		}
-
-		// Calculate position if both inputs belong to the same document
-		compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
-			a.compareDocumentPosition( b ) :
-
-			// Otherwise we know they are disconnected
-			1;
-
-		// Disconnected nodes
-		if ( compare & 1 ||
-			(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
-
-			// Choose the first element that is related to our preferred document
-			if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
-				return -1;
-			}
-			if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
-				return 1;
-			}
-
-			// Maintain original order
-			return sortInput ?
-				( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
-				0;
-		}
-
-		return compare & 4 ? -1 : 1;
-	} :
-	function( a, b ) {
-		// Exit early if the nodes are identical
-		if ( a === b ) {
-			hasDuplicate = true;
-			return 0;
-		}
-
-		var cur,
-			i = 0,
-			aup = a.parentNode,
-			bup = b.parentNode,
-			ap = [ a ],
-			bp = [ b ];
-
-		// Parentless nodes are either documents or disconnected
-		if ( !aup || !bup ) {
-			return a === document ? -1 :
-				b === document ? 1 :
-				aup ? -1 :
-				bup ? 1 :
-				sortInput ?
-				( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
-				0;
-
-		// If the nodes are siblings, we can do a quick check
-		} else if ( aup === bup ) {
-			return siblingCheck( a, b );
-		}
-
-		// Otherwise we need full lists of their ancestors for comparison
-		cur = a;
-		while ( (cur = cur.parentNode) ) {
-			ap.unshift( cur );
-		}
-		cur = b;
-		while ( (cur = cur.parentNode) ) {
-			bp.unshift( cur );
-		}
-
-		// Walk down the tree looking for a discrepancy
-		while ( ap[i] === bp[i] ) {
-			i++;
-		}
-
-		return i ?
-			// Do a sibling check if the nodes have a common ancestor
-			siblingCheck( ap[i], bp[i] ) :
-
-			// Otherwise nodes in our document sort first
-			ap[i] === preferredDoc ? -1 :
-			bp[i] === preferredDoc ? 1 :
-			0;
-	};
-
-	return document;
-};
-
-Sizzle.matches = function( expr, elements ) {
-	return Sizzle( expr, null, null, elements );
-};
-
-Sizzle.matchesSelector = function( elem, expr ) {
-	// Set document vars if needed
-	if ( ( elem.ownerDocument || elem ) !== document ) {
-		setDocument( elem );
-	}
-
-	// Make sure that attribute selectors are quoted
-	expr = expr.replace( rattributeQuotes, "='$1']" );
-
-	if ( support.matchesSelector && documentIsHTML &&
-		!compilerCache[ expr + " " ] &&
-		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
-		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
-
-		try {
-			var ret = matches.call( elem, expr );
-
-			// IE 9's matchesSelector returns false on disconnected nodes
-			if ( ret || support.disconnectedMatch ||
-					// As well, disconnected nodes are said to be in a document
-					// fragment in IE 9
-					elem.document && elem.document.nodeType !== 11 ) {
-				return ret;
-			}
-		} catch (e) {}
-	}
-
-	return Sizzle( expr, document, null, [ elem ] ).length > 0;
-};
-
-Sizzle.contains = function( context, elem ) {
-	// Set document vars if needed
-	if ( ( context.ownerDocument || context ) !== document ) {
-		setDocument( context );
-	}
-	return contains( context, elem );
-};
-
-Sizzle.attr = function( elem, name ) {
-	// Set document vars if needed
-	if ( ( elem.ownerDocument || elem ) !== document ) {
-		setDocument( elem );
-	}
-
-	var fn = Expr.attrHandle[ name.toLowerCase() ],
-		// Don't get fooled by Object.prototype properties (jQuery #13807)
-		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
-			fn( elem, name, !documentIsHTML ) :
-			undefined;
-
-	return val !== undefined ?
-		val :
-		support.attributes || !documentIsHTML ?
-			elem.getAttribute( name ) :
-			(val = elem.getAttributeNode(name)) && val.specified ?
-				val.value :
-				null;
-};
-
-Sizzle.escape = function( sel ) {
-	return (sel + "").replace( rcssescape, fcssescape );
-};
-
-Sizzle.error = function( msg ) {
-	throw new Error( "Syntax error, unrecognized expression: " + msg );
-};
-
-/**
- * Document sorting and removing duplicates
- * @param {ArrayLike} results
- */
-Sizzle.uniqueSort = function( results ) {
-	var elem,
-		duplicates = [],
-		j = 0,
-		i = 0;
-
-	// Unless we *know* we can detect duplicates, assume their presence
-	hasDuplicate = !support.detectDuplicates;
-	sortInput = !support.sortStable && results.slice( 0 );
-	results.sort( sortOrder );
-
-	if ( hasDuplicate ) {
-		while ( (elem = results[i++]) ) {
-			if ( elem === results[ i ] ) {
-				j = duplicates.push( i );
-			}
-		}
-		while ( j-- ) {
-			results.splice( duplicates[ j ], 1 );
-		}
-	}
-
-	// Clear input after sorting to release objects
-	// See https://github.com/jquery/sizzle/pull/225
-	sortInput = null;
-
-	return results;
-};
-
-/**
- * Utility function for retrieving the text value of an array of DOM nodes
- * @param {Array|Element} elem
- */
-getText = Sizzle.getText = function( elem ) {
-	var node,
-		ret = "",
-		i = 0,
-		nodeType = elem.nodeType;
-
-	if ( !nodeType ) {
-		// If no nodeType, this is expected to be an array
-		while ( (node = elem[i++]) ) {
-			// Do not traverse comment nodes
-			ret += getText( node );
-		}
-	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
-		// Use textContent for elements
-		// innerText usage removed for consistency of new lines (jQuery #11153)
-		if ( typeof elem.textContent === "string" ) {
-			return elem.textContent;
-		} else {
-			// Traverse its children
-			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
-				ret += getText( elem );
-			}
-		}
-	} else if ( nodeType === 3 || nodeType === 4 ) {
-		return elem.nodeValue;
-	}
-	// Do not include comment or processing instruction nodes
-
-	return ret;
-};
-
-Expr = Sizzle.selectors = {
-
-	// Can be adjusted by the user
-	cacheLength: 50,
-
-	createPseudo: markFunction,
-
-	match: matchExpr,
-
-	attrHandle: {},
-
-	find: {},
-
-	relative: {
-		">": { dir: "parentNode", first: true },
-		" ": { dir: "parentNode" },
-		"+": { dir: "previousSibling", first: true },
-		"~": { dir: "previousSibling" }
-	},
-
-	preFilter: {
-		"ATTR": function( match ) {
-			match[1] = match[1].replace( runescape, funescape );
-
-			// Move the given value to match[3] whether quoted or unquoted
-			match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
-
-			if ( match[2] === "~=" ) {
-				match[3] = " " + match[3] + " ";
-			}
-
-			return match.slice( 0, 4 );
-		},
-
-		"CHILD": function( match ) {
-			/* matches from matchExpr["CHILD"]
-				1 type (only|nth|...)
-				2 what (child|of-type)
-				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
-				4 xn-component of xn+y argument ([+-]?\d*n|)
-				5 sign of xn-component
-				6 x of xn-component
-				7 sign of y-component
-				8 y of y-component
-			*/
-			match[1] = match[1].toLowerCase();
-
-			if ( match[1].slice( 0, 3 ) === "nth" ) {
-				// nth-* requires argument
-				if ( !match[3] ) {
-					Sizzle.error( match[0] );
-				}
-
-				// numeric x and y parameters for Expr.filter.CHILD
-				// remember that false/true cast respectively to 0/1
-				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
-				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
-
-			// other types prohibit arguments
-			} else if ( match[3] ) {
-				Sizzle.error( match[0] );
-			}
-
-			return match;
-		},
-
-		"PSEUDO": function( match ) {
-			var excess,
-				unquoted = !match[6] && match[2];
-
-			if ( matchExpr["CHILD"].test( match[0] ) ) {
-				return null;
-			}
-
-			// Accept quoted arguments as-is
-			if ( match[3] ) {
-				match[2] = match[4] || match[5] || "";
-
-			// Strip excess characters from unquoted arguments
-			} else if ( unquoted && rpseudo.test( unquoted ) &&
-				// Get excess from tokenize (recursively)
-				(excess = tokenize( unquoted, true )) &&
-				// advance to the next closing parenthesis
-				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
-
-				// excess is a negative index
-				match[0] = match[0].slice( 0, excess );
-				match[2] = unquoted.slice( 0, excess );
-			}
-
-			// Return only captures needed by the pseudo filter method (type and argument)
-			return match.slice( 0, 3 );
-		}
-	},
-
-	filter: {
-
-		"TAG": function( nodeNameSelector ) {
-			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
-			return nodeNameSelector === "*" ?
-				function() { return true; } :
-				function( elem ) {
-					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
-				};
-		},
-
-		"CLASS": function( className ) {
-			var pattern = classCache[ className + " " ];
-
-			return pattern ||
-				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
-				classCache( className, function( elem ) {
-					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
-				});
-		},
-
-		"ATTR": function( name, operator, check ) {
-			return function( elem ) {
-				var result = Sizzle.attr( elem, name );
-
-				if ( result == null ) {
-					return operator === "!=";
-				}
-				if ( !operator ) {
-					return true;
-				}
-
-				result += "";
-
-				return operator === "=" ? result === check :
-					operator === "!=" ? result !== check :
-					operator === "^=" ? check && result.indexOf( check ) === 0 :
-					operator === "*=" ? check && result.indexOf( check ) > -1 :
-					operator === "$=" ? check && result.slice( -check.length ) === check :
-					operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
-					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
-					false;
-			};
-		},
-
-		"CHILD": function( type, what, argument, first, last ) {
-			var simple = type.slice( 0, 3 ) !== "nth",
-				forward = type.slice( -4 ) !== "last",
-				ofType = what === "of-type";
-
-			return first === 1 && last === 0 ?
-
-				// Shortcut for :nth-*(n)
-				function( elem ) {
-					return !!elem.parentNode;
-				} :
-
-				function( elem, context, xml ) {
-					var cache, uniqueCache, outerCache, node, nodeIndex, start,
-						dir = simple !== forward ? "nextSibling" : "previousSibling",
-						parent = elem.parentNode,
-						name = ofType && elem.nodeName.toLowerCase(),
-						useCache = !xml && !ofType,
-						diff = false;
-
-					if ( parent ) {
-
-						// :(first|last|only)-(child|of-type)
-						if ( simple ) {
-							while ( dir ) {
-								node = elem;
-								while ( (node = node[ dir ]) ) {
-									if ( ofType ?
-										node.nodeName.toLowerCase() === name :
-										node.nodeType === 1 ) {
-
-										return false;
-									}
-								}
-								// Reverse direction for :only-* (if we haven't yet done so)
-								start = dir = type === "only" && !start && "nextSibling";
-							}
-							return true;
-						}
-
-						start = [ forward ? parent.firstChild : parent.lastChild ];
-
-						// non-xml :nth-child(...) stores cache data on `parent`
-						if ( forward && useCache ) {
-
-							// Seek `elem` from a previously-cached index
-
-							// ...in a gzip-friendly way
-							node = parent;
-							outerCache = node[ expando ] || (node[ expando ] = {});
-
-							// Support: IE <9 only
-							// Defend against cloned attroperties (jQuery gh-1709)
-							uniqueCache = outerCache[ node.uniqueID ] ||
-								(outerCache[ node.uniqueID ] = {});
-
-							cache = uniqueCache[ type ] || [];
-							nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
-							diff = nodeIndex && cache[ 2 ];
-							node = nodeIndex && parent.childNodes[ nodeIndex ];
-
-							while ( (node = ++nodeIndex && node && node[ dir ] ||
-
-								// Fallback to seeking `elem` from the start
-								(diff = nodeIndex = 0) || start.pop()) ) {
-
-								// When found, cache indexes on `parent` and break
-								if ( node.nodeType === 1 && ++diff && node === elem ) {
-									uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
-									break;
-								}
-							}
-
-						} else {
-							// Use previously-cached element index if available
-							if ( useCache ) {
-								// ...in a gzip-friendly way
-								node = elem;
-								outerCache = node[ expando ] || (node[ expando ] = {});
-
-								// Support: IE <9 only
-								// Defend against cloned attroperties (jQuery gh-1709)
-								uniqueCache = outerCache[ node.uniqueID ] ||
-									(outerCache[ node.uniqueID ] = {});
-
-								cache = uniqueCache[ type ] || [];
-								nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
-								diff = nodeIndex;
-							}
-
-							// xml :nth-child(...)
-							// or :nth-last-child(...) or :nth(-last)?-of-type(...)
-							if ( diff === false ) {
-								// Use the same loop as above to seek `elem` from the start
-								while ( (node = ++nodeIndex && node && node[ dir ] ||
-									(diff = nodeIndex = 0) || start.pop()) ) {
-
-									if ( ( ofType ?
-										node.nodeName.toLowerCase() === name :
-										node.nodeType === 1 ) &&
-										++diff ) {
-
-										// Cache the index of each encountered element
-										if ( useCache ) {
-											outerCache = node[ expando ] || (node[ expando ] = {});
-
-											// Support: IE <9 only
-											// Defend against cloned attroperties (jQuery gh-1709)
-											uniqueCache = outerCache[ node.uniqueID ] ||
-												(outerCache[ node.uniqueID ] = {});
-
-											uniqueCache[ type ] = [ dirruns, diff ];
-										}
-
-										if ( node === elem ) {
-											break;
-										}
-									}
-								}
-							}
-						}
-
-						// Incorporate the offset, then check against cycle size
-						diff -= last;
-						return diff === first || ( diff % first === 0 && diff / first >= 0 );
-					}
-				};
-		},
-
-		"PSEUDO": function( pseudo, argument ) {
-			// pseudo-class names are case-insensitive
-			// http://www.w3.org/TR/selectors/#pseudo-classes
-			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
-			// Remember that setFilters inherits from pseudos
-			var args,
-				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
-					Sizzle.error( "unsupported pseudo: " + pseudo );
-
-			// The user may use createPseudo to indicate that
-			// arguments are needed to create the filter function
-			// just as Sizzle does
-			if ( fn[ expando ] ) {
-				return fn( argument );
-			}
-
-			// But maintain support for old signatures
-			if ( fn.length > 1 ) {
-				args = [ pseudo, pseudo, "", argument ];
-				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
-					markFunction(function( seed, matches ) {
-						var idx,
-							matched = fn( seed, argument ),
-							i = matched.length;
-						while ( i-- ) {
-							idx = indexOf( seed, matched[i] );
-							seed[ idx ] = !( matches[ idx ] = matched[i] );
-						}
-					}) :
-					function( elem ) {
-						return fn( elem, 0, args );
-					};
-			}
-
-			return fn;
-		}
-	},
-
-	pseudos: {
-		// Potentially complex pseudos
-		"not": markFunction(function( selector ) {
-			// Trim the selector passed to compile
-			// to avoid treating leading and trailing
-			// spaces as combinators
-			var input = [],
-				results = [],
-				matcher = compile( selector.replace( rtrim, "$1" ) );
-
-			return matcher[ expando ] ?
-				markFunction(function( seed, matches, context, xml ) {
-					var elem,
-						unmatched = matcher( seed, null, xml, [] ),
-						i = seed.length;
-
-					// Match elements unmatched by `matcher`
-					while ( i-- ) {
-						if ( (elem = unmatched[i]) ) {
-							seed[i] = !(matches[i] = elem);
-						}
-					}
-				}) :
-				function( elem, context, xml ) {
-					input[0] = elem;
-					matcher( input, null, xml, results );
-					// Don't keep the element (issue #299)
-					input[0] = null;
-					return !results.pop();
-				};
-		}),
-
-		"has": markFunction(function( selector ) {
-			return function( elem ) {
-				return Sizzle( selector, elem ).length > 0;
-			};
-		}),
-
-		"contains": markFunction(function( text ) {
-			text = text.replace( runescape, funescape );
-			return function( elem ) {
-				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
-			};
-		}),
-
-		// "Whether an element is represented by a :lang() selector
-		// is based solely on the element's language value
-		// being equal to the identifier C,
-		// or beginning with the identifier C immediately followed by "-".
-		// The matching of C against the element's language value is performed case-insensitively.
-		// The identifier C does not have to be a valid language name."
-		// http://www.w3.org/TR/selectors/#lang-pseudo
-		"lang": markFunction( function( lang ) {
-			// lang value must be a valid identifier
-			if ( !ridentifier.test(lang || "") ) {
-				Sizzle.error( "unsupported lang: " + lang );
-			}
-			lang = lang.replace( runescape, funescape ).toLowerCase();
-			return function( elem ) {
-				var elemLang;
-				do {
-					if ( (elemLang = documentIsHTML ?
-						elem.lang :
-						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
-
-						elemLang = elemLang.toLowerCase();
-						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
-					}
-				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
-				return false;
-			};
-		}),
-
-		// Miscellaneous
-		"target": function( elem ) {
-			var hash = window.location && window.location.hash;
-			return hash && hash.slice( 1 ) === elem.id;
-		},
-
-		"root": function( elem ) {
-			return elem === docElem;
-		},
-
-		"focus": function( elem ) {
-			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
-		},
-
-		// Boolean properties
-		"enabled": createDisabledPseudo( false ),
-		"disabled": createDisabledPseudo( true ),
-
-		"checked": function( elem ) {
-			// In CSS3, :checked should return both checked and selected elements
-			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
-			var nodeName = elem.nodeName.toLowerCase();
-			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
-		},
-
-		"selected": function( elem ) {
-			// Accessing this property makes selected-by-default
-			// options in Safari work properly
-			if ( elem.parentNode ) {
-				elem.parentNode.selectedIndex;
-			}
-
-			return elem.selected === true;
-		},
-
-		// Contents
-		"empty": function( elem ) {
-			// http://www.w3.org/TR/selectors/#empty-pseudo
-			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
-			//   but not by others (comment: 8; processing instruction: 7; etc.)
-			// nodeType < 6 works because attributes (2) do not appear as children
-			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
-				if ( elem.nodeType < 6 ) {
-					return false;
-				}
-			}
-			return true;
-		},
-
-		"parent": function( elem ) {
-			return !Expr.pseudos["empty"]( elem );
-		},
-
-		// Element/input types
-		"header": function( elem ) {
-			return rheader.test( elem.nodeName );
-		},
-
-		"input": function( elem ) {
-			return rinputs.test( elem.nodeName );
-		},
-
-		"button": function( elem ) {
-			var name = elem.nodeName.toLowerCase();
-			return name === "input" && elem.type === "button" || name === "button";
-		},
-
-		"text": function( elem ) {
-			var attr;
-			return elem.nodeName.toLowerCase() === "input" &&
-				elem.type === "text" &&
-
-				// Support: IE<8
-				// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
-				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
-		},
-
-		// Position-in-collection
-		"first": createPositionalPseudo(function() {
-			return [ 0 ];
-		}),
-
-		"last": createPositionalPseudo(function( matchIndexes, length ) {
-			return [ length - 1 ];
-		}),
-
-		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
-			return [ argument < 0 ? argument + length : argument ];
-		}),
-
-		"even": createPositionalPseudo(function( matchIndexes, length ) {
-			var i = 0;
-			for ( ; i < length; i += 2 ) {
-				matchIndexes.push( i );
-			}
-			return matchIndexes;
-		}),
-
-		"odd": createPositionalPseudo(function( matchIndexes, length ) {
-			var i = 1;
-			for ( ; i < length; i += 2 ) {
-				matchIndexes.push( i );
-			}
-			return matchIndexes;
-		}),
-
-		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
-			var i = argument < 0 ? argument + length : argument;
-			for ( ; --i >= 0; ) {
-				matchIndexes.push( i );
-			}
-			return matchIndexes;
-		}),
-
-		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
-			var i = argument < 0 ? argument + length : argument;
-			for ( ; ++i < length; ) {
-				matchIndexes.push( i );
-			}
-			return matchIndexes;
-		})
-	}
-};
-
-Expr.pseudos["nth"] = Expr.pseudos["eq"];
-
-// Add button/input type pseudos
-for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
-	Expr.pseudos[ i ] = createInputPseudo( i );
-}
-for ( i in { submit: true, reset: true } ) {
-	Expr.pseudos[ i ] = createButtonPseudo( i );
-}
-
-// Easy API for creating new setFilters
-function setFilters() {}
-setFilters.prototype = Expr.filters = Expr.pseudos;
-Expr.setFilters = new setFilters();
-
-tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
-	var matched, match, tokens, type,
-		soFar, groups, preFilters,
-		cached = tokenCache[ selector + " " ];
-
-	if ( cached ) {
-		return parseOnly ? 0 : cached.slice( 0 );
-	}
-
-	soFar = selector;
-	groups = [];
-	preFilters = Expr.preFilter;
-
-	while ( soFar ) {
-
-		// Comma and first run
-		if ( !matched || (match = rcomma.exec( soFar )) ) {
-			if ( match ) {
-				// Don't consume trailing commas as valid
-				soFar = soFar.slice( match[0].length ) || soFar;
-			}
-			groups.push( (tokens = []) );
-		}
-
-		matched = false;
-
-		// Combinators
-		if ( (match = rcombinators.exec( soFar )) ) {
-			matched = match.shift();
-			tokens.push({
-				value: matched,
-				// Cast descendant combinators to space
-				type: match[0].replace( rtrim, " " )
-			});
-			soFar = soFar.slice( matched.length );
-		}
-
-		// Filters
-		for ( type in Expr.filter ) {
-			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
-				(match = preFilters[ type ]( match ))) ) {
-				matched = match.shift();
-				tokens.push({
-					value: matched,
-					type: type,
-					matches: match
-				});
-				soFar = soFar.slice( matched.length );
-			}
-		}
-
-		if ( !matched ) {
-			break;
-		}
-	}
-
-	// Return the length of the invalid excess
-	// if we're just parsing
-	// Otherwise, throw an error or return tokens
-	return parseOnly ?
-		soFar.length :
-		soFar ?
-			Sizzle.error( selector ) :
-			// Cache the tokens
-			tokenCache( selector, groups ).slice( 0 );
-};
-
-function toSelector( tokens ) {
-	var i = 0,
-		len = tokens.length,
-		selector = "";
-	for ( ; i < len; i++ ) {
-		selector += tokens[i].value;
-	}
-	return selector;
-}
-
-function addCombinator( matcher, combinator, base ) {
-	var dir = combinator.dir,
-		skip = combinator.next,
-		key = skip || dir,
-		checkNonElements = base && key === "parentNode",
-		doneName = done++;
-
-	return combinator.first ?
-		// Check against closest ancestor/preceding element
-		function( elem, context, xml ) {
-			while ( (elem = elem[ dir ]) ) {
-				if ( elem.nodeType === 1 || checkNonElements ) {
-					return matcher( elem, context, xml );
-				}
-			}
-		} :
-
-		// Check against all ancestor/preceding elements
-		function( elem, context, xml ) {
-			var oldCache, uniqueCache, outerCache,
-				newCache = [ dirruns, doneName ];
-
-			// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
-			if ( xml ) {
-				while ( (elem = elem[ dir ]) ) {
-					if ( elem.nodeType === 1 || checkNonElements ) {
-						if ( matcher( elem, context, xml ) ) {
-							return true;
-						}
-					}
-				}
-			} else {
-				while ( (elem = elem[ dir ]) ) {
-					if ( elem.nodeType === 1 || checkNonElements ) {
-						outerCache = elem[ expando ] || (elem[ expando ] = {});
-
-						// Support: IE <9 only
-						// Defend against cloned attroperties (jQuery gh-1709)
-						uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {});
-
-						if ( skip && skip === elem.nodeName.toLowerCase() ) {
-							elem = elem[ dir ] || elem;
-						} else if ( (oldCache = uniqueCache[ key ]) &&
-							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
-
-							// Assign to newCache so results back-propagate to previous elements
-							return (newCache[ 2 ] = oldCache[ 2 ]);
-						} else {
-							// Reuse newcache so results back-propagate to previous elements
-							uniqueCache[ key ] = newCache;
-
-							// A match means we're done; a fail means we have to keep checking
-							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
-								return true;
-							}
-						}
-					}
-				}
-			}
-		};
-}
-
-function elementMatcher( matchers ) {
-	return matchers.length > 1 ?
-		function( elem, context, xml ) {
-			var i = matchers.length;
-			while ( i-- ) {
-				if ( !matchers[i]( elem, context, xml ) ) {
-					return false;
-				}
-			}
-			return true;
-		} :
-		matchers[0];
-}
-
-function multipleContexts( selector, contexts, results ) {
-	var i = 0,
-		len = contexts.length;
-	for ( ; i < len; i++ ) {
-		Sizzle( selector, contexts[i], results );
-	}
-	return results;
-}
-
-function condense( unmatched, map, filter, context, xml ) {
-	var elem,
-		newUnmatched = [],
-		i = 0,
-		len = unmatched.length,
-		mapped = map != null;
-
-	for ( ; i < len; i++ ) {
-		if ( (elem = unmatched[i]) ) {
-			if ( !filter || filter( elem, context, xml ) ) {
-				newUnmatched.push( elem );
-				if ( mapped ) {
-					map.push( i );
-				}
-			}
-		}
-	}
-
-	return newUnmatched;
-}
-
-function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
-	if ( postFilter && !postFilter[ expando ] ) {
-		postFilter = setMatcher( postFilter );
-	}
-	if ( postFinder && !postFinder[ expando ] ) {
-		postFinder = setMatcher( postFinder, postSelector );
-	}
-	return markFunction(function( seed, results, context, xml ) {
-		var temp, i, elem,
-			preMap = [],
-			postMap = [],
-			preexisting = results.length,
-
-			// Get initial elements from seed or context
-			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
-
-			// Prefilter to get matcher input, preserving a map for seed-results synchronization
-			matcherIn = preFilter && ( seed || !selector ) ?
-				condense( elems, preMap, preFilter, context, xml ) :
-				elems,
-
-			matcherOut = matcher ?
-				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
-				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
-
-					// ...intermediate processing is necessary
-					[] :
-
-					// ...otherwise use results directly
-					results :
-				matcherIn;
-
-		// Find primary matches
-		if ( matcher ) {
-			matcher( matcherIn, matcherOut, context, xml );
-		}
-
-		// Apply postFilter
-		if ( postFilter ) {
-			temp = condense( matcherOut, postMap );
-			postFilter( temp, [], context, xml );
-
-			// Un-match failing elements by moving them back to matcherIn
-			i = temp.length;
-			while ( i-- ) {
-				if ( (elem = temp[i]) ) {
-					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
-				}
-			}
-		}
-
-		if ( seed ) {
-			if ( postFinder || preFilter ) {
-				if ( postFinder ) {
-					// Get the final matcherOut by condensing this intermediate into postFinder contexts
-					temp = [];
-					i = matcherOut.length;
-					while ( i-- ) {
-						if ( (elem = matcherOut[i]) ) {
-							// Restore matcherIn since elem is not yet a final match
-							temp.push( (matcherIn[i] = elem) );
-						}
-					}
-					postFinder( null, (matcherOut = []), temp, xml );
-				}
-
-				// Move matched elements from seed to results to keep them synchronized
-				i = matcherOut.length;
-				while ( i-- ) {
-					if ( (elem = matcherOut[i]) &&
-						(temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
-
-						seed[temp] = !(results[temp] = elem);
-					}
-				}
-			}
-
-		// Add elements to results, through postFinder if defined
-		} else {
-			matcherOut = condense(
-				matcherOut === results ?
-					matcherOut.splice( preexisting, matcherOut.length ) :
-					matcherOut
-			);
-			if ( postFinder ) {
-				postFinder( null, results, matcherOut, xml );
-			} else {
-				push.apply( results, matcherOut );
-			}
-		}
-	});
-}
-
-function matcherFromTokens( tokens ) {
-	var checkContext, matcher, j,
-		len = tokens.length,
-		leadingRelative = Expr.relative[ tokens[0].type ],
-		implicitRelative = leadingRelative || Expr.relative[" "],
-		i = leadingRelative ? 1 : 0,
-
-		// The foundational matcher ensures that elements are reachable from top-level context(s)
-		matchContext = addCombinator( function( elem ) {
-			return elem === checkContext;
-		}, implicitRelative, true ),
-		matchAnyContext = addCombinator( function( elem ) {
-			return indexOf( checkContext, elem ) > -1;
-		}, implicitRelative, true ),
-		matchers = [ function( elem, context, xml ) {
-			var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
-				(checkContext = context).nodeType ?
-					matchContext( elem, context, xml ) :
-					matchAnyContext( elem, context, xml ) );
-			// Avoid hanging onto element (issue #299)
-			checkContext = null;
-			return ret;
-		} ];
-
-	for ( ; i < len; i++ ) {
-		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
-			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
-		} else {
-			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
-
-			// Return special upon seeing a positional matcher
-			if ( matcher[ expando ] ) {
-				// Find the next relative operator (if any) for proper handling
-				j = ++i;
-				for ( ; j < len; j++ ) {
-					if ( Expr.relative[ tokens[j].type ] ) {
-						break;
-					}
-				}
-				return setMatcher(
-					i > 1 && elementMatcher( matchers ),
-					i > 1 && toSelector(
-						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
-						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
-					).replace( rtrim, "$1" ),
-					matcher,
-					i < j && matcherFromTokens( tokens.slice( i, j ) ),
-					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
-					j < len && toSelector( tokens )
-				);
-			}
-			matchers.push( matcher );
-		}
-	}
-
-	return elementMatcher( matchers );
-}
-
-function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
-	var bySet = setMatchers.length > 0,
-		byElement = elementMatchers.length > 0,
-		superMatcher = function( seed, context, xml, results, outermost ) {
-			var elem, j, matcher,
-				matchedCount = 0,
-				i = "0",
-				unmatched = seed && [],
-				setMatched = [],
-				contextBackup = outermostContext,
-				// We must always have either seed elements or outermost context
-				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
-				// Use integer dirruns iff this is the outermost matcher
-				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
-				len = elems.length;
-
-			if ( outermost ) {
-				outermostContext = context === document || context || outermost;
-			}
-
-			// Add elements passing elementMatchers directly to results
-			// Support: IE<9, Safari
-			// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
-			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
-				if ( byElement && elem ) {
-					j = 0;
-					if ( !context && elem.ownerDocument !== document ) {
-						setDocument( elem );
-						xml = !documentIsHTML;
-					}
-					while ( (matcher = elementMatchers[j++]) ) {
-						if ( matcher( elem, context || document, xml) ) {
-							results.push( elem );
-							break;
-						}
-					}
-					if ( outermost ) {
-						dirruns = dirrunsUnique;
-					}
-				}
-
-				// Track unmatched elements for set filters
-				if ( bySet ) {
-					// They will have gone through all possible matchers
-					if ( (elem = !matcher && elem) ) {
-						matchedCount--;
-					}
-
-					// Lengthen the array for every element, matched or not
-					if ( seed ) {
-						unmatched.push( elem );
-					}
-				}
-			}
-
-			// `i` is now the count of elements visited above, and adding it to `matchedCount`
-			// makes the latter nonnegative.
-			matchedCount += i;
-
-			// Apply set filters to unmatched elements
-			// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
-			// equals `i`), unless we didn't visit _any_ elements in the above loop because we have
-			// no element matchers and no seed.
-			// Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
-			// case, which will result in a "00" `matchedCount` that differs from `i` but is also
-			// numerically zero.
-			if ( bySet && i !== matchedCount ) {
-				j = 0;
-				while ( (matcher = setMatchers[j++]) ) {
-					matcher( unmatched, setMatched, context, xml );
-				}
-
-				if ( seed ) {
-					// Reintegrate element matches to eliminate the need for sorting
-					if ( matchedCount > 0 ) {
-						while ( i-- ) {
-							if ( !(unmatched[i] || setMatched[i]) ) {
-								setMatched[i] = pop.call( results );
-							}
-						}
-					}
-
-					// Discard index placeholder values to get only actual matches
-					setMatched = condense( setMatched );
-				}
-
-				// Add matches to results
-				push.apply( results, setMatched );
-
-				// Seedless set matches succeeding multiple successful matchers stipulate sorting
-				if ( outermost && !seed && setMatched.length > 0 &&
-					( matchedCount + setMatchers.length ) > 1 ) {
-
-					Sizzle.uniqueSort( results );
-				}
-			}
-
-			// Override manipulation of globals by nested matchers
-			if ( outermost ) {
-				dirruns = dirrunsUnique;
-				outermostContext = contextBackup;
-			}
-
-			return unmatched;
-		};
-
-	return bySet ?
-		markFunction( superMatcher ) :
-		superMatcher;
-}
-
-compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
-	var i,
-		setMatchers = [],
-		elementMatchers = [],
-		cached = compilerCache[ selector + " " ];
-
-	if ( !cached ) {
-		// Generate a function of recursive functions that can be used to check each element
-		if ( !match ) {
-			match = tokenize( selector );
-		}
-		i = match.length;
-		while ( i-- ) {
-			cached = matcherFromTokens( match[i] );
-			if ( cached[ expando ] ) {
-				setMatchers.push( cached );
-			} else {
-				elementMatchers.push( cached );
-			}
-		}
-
-		// Cache the compiled function
-		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
-
-		// Save selector and tokenization
-		cached.selector = selector;
-	}
-	return cached;
-};
-
-/**
- * A low-level selection function that works with Sizzle's compiled
- *  selector functions
- * @param {String|Function} selector A selector or a pre-compiled
- *  selector function built with Sizzle.compile
- * @param {Element} context
- * @param {Array} [results]
- * @param {Array} [seed] A set of elements to match against
- */
-select = Sizzle.select = function( selector, context, results, seed ) {
-	var i, tokens, token, type, find,
-		compiled = typeof selector === "function" && selector,
-		match = !seed && tokenize( (selector = compiled.selector || selector) );
-
-	results = results || [];
-
-	// Try to minimize operations if there is only one selector in the list and no seed
-	// (the latter of which guarantees us context)
-	if ( match.length === 1 ) {
-
-		// Reduce context if the leading compound selector is an ID
-		tokens = match[0] = match[0].slice( 0 );
-		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
-				support.getById && context.nodeType === 9 && documentIsHTML &&
-				Expr.relative[ tokens[1].type ] ) {
-
-			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
-			if ( !context ) {
-				return results;
-
-			// Precompiled matchers will still verify ancestry, so step up a level
-			} else if ( compiled ) {
-				context = context.parentNode;
-			}
-
-			selector = selector.slice( tokens.shift().value.length );
-		}
-
-		// Fetch a seed set for right-to-left matching
-		i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
-		while ( i-- ) {
-			token = tokens[i];
-
-			// Abort if we hit a combinator
-			if ( Expr.relative[ (type = token.type) ] ) {
-				break;
-			}
-			if ( (find = Expr.find[ type ]) ) {
-				// Search, expanding context for leading sibling combinators
-				if ( (seed = find(
-					token.matches[0].replace( runescape, funescape ),
-					rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
-				)) ) {
-
-					// If seed is empty or no tokens remain, we can return early
-					tokens.splice( i, 1 );
-					selector = seed.length && toSelector( tokens );
-					if ( !selector ) {
-						push.apply( results, seed );
-						return results;
-					}
-
-					break;
-				}
-			}
-		}
-	}
-
-	// Compile and execute a filtering function if one is not provided
-	// Provide `match` to avoid retokenization if we modified the selector above
-	( compiled || compile( selector, match ) )(
-		seed,
-		context,
-		!documentIsHTML,
-		results,
-		!context || rsibling.test( selector ) && testContext( context.parentNode ) || context
-	);
-	return results;
-};
-
-// One-time assignments
-
-// Sort stability
-support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
-
-// Support: Chrome 14-35+
-// Always assume duplicates if they aren't passed to the comparison function
-support.detectDuplicates = !!hasDuplicate;
-
-// Initialize against the default document
-setDocument();
-
-// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
-// Detached nodes confoundingly follow *each other*
-support.sortDetached = assert(function( el ) {
-	// Should return 1, but returns 4 (following)
-	return el.compareDocumentPosition( document.createElement("fieldset") ) & 1;
-});
-
-// Support: IE<8
-// Prevent attribute/property "interpolation"
-// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
-if ( !assert(function( el ) {
-	el.innerHTML = "<a href='#'></a>";
-	return el.firstChild.getAttribute("href") === "#" ;
-}) ) {
-	addHandle( "type|href|height|width", function( elem, name, isXML ) {
-		if ( !isXML ) {
-			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
-		}
-	});
-}
-
-// Support: IE<9
-// Use defaultValue in place of getAttribute("value")
-if ( !support.attributes || !assert(function( el ) {
-	el.innerHTML = "<input/>";
-	el.firstChild.setAttribute( "value", "" );
-	return el.firstChild.getAttribute( "value" ) === "";
-}) ) {
-	addHandle( "value", function( elem, name, isXML ) {
-		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
-			return elem.defaultValue;
-		}
-	});
-}
-
-// Support: IE<9
-// Use getAttributeNode to fetch booleans when getAttribute lies
-if ( !assert(function( el ) {
-	return el.getAttribute("disabled") == null;
-}) ) {
-	addHandle( booleans, function( elem, name, isXML ) {
-		var val;
-		if ( !isXML ) {
-			return elem[ name ] === true ? name.toLowerCase() :
-					(val = elem.getAttributeNode( name )) && val.specified ?
-					val.value :
-				null;
-		}
-	});
-}
-
-return Sizzle;
-
-})( window );
-
-
-
-jQuery.find = Sizzle;
-jQuery.expr = Sizzle.selectors;
-jQuery.expr[ ":" ] = jQuery.expr.pseudos;
-jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
-jQuery.text = Sizzle.getText;
-jQuery.isXMLDoc = Sizzle.isXML;
-jQuery.contains = Sizzle.contains;
-
-
-
-var dir = function( elem, dir, until ) {
-	var matched = [],
-		truncate = until !== undefined;
-
-	while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
-		if ( elem.nodeType === 1 ) {
-			if ( truncate && jQuery( elem ).is( until ) ) {
-				break;
-			}
-			matched.push( elem );
-		}
-	}
-	return matched;
-};
-
-
-var siblings = function( n, elem ) {
-	var matched = [];
-
-	for ( ; n; n = n.nextSibling ) {
-		if ( n.nodeType === 1 && n !== elem ) {
-			matched.push( n );
-		}
-	}
-
-	return matched;
-};
-
-
-var rneedsContext = jQuery.expr.match.needsContext;
-
-var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
-
-
-
-var risSimple = /^.[^:#\[\.,]*$/;
-
-// Implement the identical functionality for filter and not
-function winnow( elements, qualifier, not ) {
-	if ( jQuery.isFunction( qualifier ) ) {
-		return jQuery.grep( elements, function( elem, i ) {
-			/* jshint -W018 */
-			return !!qualifier.call( elem, i, elem ) !== not;
-		} );
-
-	}
-
-	if ( qualifier.nodeType ) {
-		return jQuery.grep( elements, function( elem ) {
-			return ( elem === qualifier ) !== not;
-		} );
-
-	}
-
-	if ( typeof qualifier === "string" ) {
-		if ( risSimple.test( qualifier ) ) {
-			return jQuery.filter( qualifier, elements, not );
-		}
-
-		qualifier = jQuery.filter( qualifier, elements );
-	}
-
-	return jQuery.grep( elements, function( elem ) {
-		return ( indexOf.call( qualifier, elem ) > -1 ) !== not && elem.nodeType === 1;
-	} );
-}
-
-jQuery.filter = function( expr, elems, not ) {
-	var elem = elems[ 0 ];
-
-	if ( not ) {
-		expr = ":not(" + expr + ")";
-	}
-
-	return elems.length === 1 && elem.nodeType === 1 ?
-		jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
-		jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
-			return elem.nodeType === 1;
-		} ) );
-};
-
-jQuery.fn.extend( {
-	find: function( selector ) {
-		var i,
-			len = this.length,
-			ret = [],
-			self = this;
-
-		if ( typeof selector !== "string" ) {
-			return this.pushStack( jQuery( selector ).filter( function() {
-				for ( i = 0; i < len; i++ ) {
-					if ( jQuery.contains( self[ i ], this ) ) {
-						return true;
-					}
-				}
-			} ) );
-		}
-
-		for ( i = 0; i < len; i++ ) {
-			jQuery.find( selector, self[ i ], ret );
-		}
-
-		return this.pushStack( len > 1 ? jQuery.uniqueSort( ret ) : ret );
-	},
-	filter: function( selector ) {
-		return this.pushStack( winnow( this, selector || [], false ) );
-	},
-	not: function( selector ) {
-		return this.pushStack( winnow( this, selector || [], true ) );
-	},
-	is: function( selector ) {
-		return !!winnow(
-			this,
-
-			// If this is a positional/relative selector, check membership in the returned set
-			// so $("p:first").is("p:last") won't return true for a doc with two "p".
-			typeof selector === "string" && rneedsContext.test( selector ) ?
-				jQuery( selector ) :
-				selector || [],
-			false
-		).length;
-	}
-} );
-
-
-// Initialize a jQuery object
-
-
-// A central reference to the root jQuery(document)
-var rootjQuery,
-
-	// A simple way to check for HTML strings
-	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
-	// Strict HTML recognition (#11290: must start with <)
-	// Shortcut simple #id case for speed
-	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
-
-	init = jQuery.fn.init = function( selector, context, root ) {
-		var match, elem;
-
-		// HANDLE: $(""), $(null), $(undefined), $(false)
-		if ( !selector ) {
-			return this;
-		}
-
-		// Method init() accepts an alternate rootjQuery
-		// so migrate can support jQuery.sub (gh-2101)
-		root = root || rootjQuery;
-
-		// Handle HTML strings
-		if ( typeof selector === "string" ) {
-			if ( selector[ 0 ] === "<" &&
-				selector[ selector.length - 1 ] === ">" &&
-				selector.length >= 3 ) {
-
-				// Assume that strings that start and end with <> are HTML and skip the regex check
-				match = [ null, selector, null ];
-
-			} else {
-				match = rquickExpr.exec( selector );
-			}
-
-			// Match html or make sure no context is specified for #id
-			if ( match && ( match[ 1 ] || !context ) ) {
-
-				// HANDLE: $(html) -> $(array)
-				if ( match[ 1 ] ) {
-					context = context instanceof jQuery ? context[ 0 ] : context;
-
-					// Option to run scripts is true for back-compat
-					// Intentionally let the error be thrown if parseHTML is not present
-					jQuery.merge( this, jQuery.parseHTML(
-						match[ 1 ],
-						context && context.nodeType ? context.ownerDocument || context : document,
-						true
-					) );
-
-					// HANDLE: $(html, props)
-					if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
-						for ( match in context ) {
-
-							// Properties of context are called as methods if possible
-							if ( jQuery.isFunction( this[ match ] ) ) {
-								this[ match ]( context[ match ] );
-
-							// ...and otherwise set as attributes
-							} else {
-								this.attr( match, context[ match ] );
-							}
-						}
-					}
-
-					return this;
-
-				// HANDLE: $(#id)
-				} else {
-					elem = document.getElementById( match[ 2 ] );
-
-					if ( elem ) {
-
-						// Inject the element directly into the jQuery object
-						this[ 0 ] = elem;
-						this.length = 1;
-					}
-					return this;
-				}
-
-			// HANDLE: $(expr, $(...))
-			} else if ( !context || context.jquery ) {
-				return ( context || root ).find( selector );
-
-			// HANDLE: $(expr, context)
-			// (which is just equivalent to: $(context).find(expr)
-			} else {
-				return this.constructor( context ).find( selector );
-			}
-
-		// HANDLE: $(DOMElement)
-		} else if ( selector.nodeType ) {
-			this[ 0 ] = selector;
-			this.length = 1;
-			return this;
-
-		// HANDLE: $(function)
-		// Shortcut for document ready
-		} else if ( jQuery.isFunction( selector ) ) {
-			return root.ready !== undefined ?
-				root.ready( selector ) :
-
-				// Execute immediately if ready is not present
-				selector( jQuery );
-		}
-
-		return jQuery.makeArray( selector, this );
-	};
-
-// Give the init function the jQuery prototype for later instantiation
-init.prototype = jQuery.fn;
-
-// Initialize central reference
-rootjQuery = jQuery( document );
-
-
-var rparentsprev = /^(?:parents|prev(?:Until|All))/,
-
-	// Methods guaranteed to produce a unique set when starting from a unique set
-	guaranteedUnique = {
-		children: true,
-		contents: true,
-		next: true,
-		prev: true
-	};
-
-jQuery.fn.extend( {
-	has: function( target ) {
-		var targets = jQuery( target, this ),
-			l = targets.length;
-
-		return this.filter( function() {
-			var i = 0;
-			for ( ; i < l; i++ ) {
-				if ( jQuery.contains( this, targets[ i ] ) ) {
-					return true;
-				}
-			}
-		} );
-	},
-
-	closest: function( selectors, context ) {
-		var cur,
-			i = 0,
-			l = this.length,
-			matched = [],
-			targets = typeof selectors !== "string" && jQuery( selectors );
-
-		// Positional selectors never match, since there's no _selection_ context
-		if ( !rneedsContext.test( selectors ) ) {
-			for ( ; i < l; i++ ) {
-				for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
-
-					// Always skip document fragments
-					if ( cur.nodeType < 11 && ( targets ?
-						targets.index( cur ) > -1 :
-
-						// Don't pass non-elements to Sizzle
-						cur.nodeType === 1 &&
-							jQuery.find.matchesSelector( cur, selectors ) ) ) {
-
-						matched.push( cur );
-						break;
-					}
-				}
-			}
-		}
-
-		return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
-	},
-
-	// Determine the position of an element within the set
-	index: function( elem ) {
-
-		// No argument, return index in parent
-		if ( !elem ) {
-			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
-		}
-
-		// Index in selector
-		if ( typeof elem === "string" ) {
-			return indexOf.call( jQuery( elem ), this[ 0 ] );
-		}
-
-		// Locate the position of the desired element
-		return indexOf.call( this,
-
-			// If it receives a jQuery object, the first element is used
-			elem.jquery ? elem[ 0 ] : elem
-		);
-	},
-
-	add: function( selector, context ) {
-		return this.pushStack(
-			jQuery.uniqueSort(
-				jQuery.merge( this.get(), jQuery( selector, context ) )
-			)
-		);
-	},
-
-	addBack: function( selector ) {
-		return this.add( selector == null ?
-			this.prevObject : this.prevObject.filter( selector )
-		);
-	}
-} );
-
-function sibling( cur, dir ) {
-	while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
-	return cur;
-}
-
-jQuery.each( {
-	parent: function( elem ) {
-		var parent = elem.parentNode;
-		return parent && parent.nodeType !== 11 ? parent : null;
-	},
-	parents: function( elem ) {
-		return dir( elem, "parentNode" );
-	},
-	parentsUntil: function( elem, i, until ) {
-		return dir( elem, "parentNode", until );
-	},
-	next: function( elem ) {
-		return sibling( elem, "nextSibling" );
-	},
-	prev: function( elem ) {
-		return sibling( elem, "previousSibling" );
-	},
-	nextAll: function( elem ) {
-		return dir( elem, "nextSibling" );
-	},
-	prevAll: function( elem ) {
-		return dir( elem, "previousSibling" );
-	},
-	nextUntil: function( elem, i, until ) {
-		return dir( elem, "nextSibling", until );
-	},
-	prevUntil: function( elem, i, until ) {
-		return dir( elem, "previousSibling", until );
-	},
-	siblings: function( elem ) {
-		return siblings( ( elem.parentNode || {} ).firstChild, elem );
-	},
-	children: function( elem ) {
-		return siblings( elem.firstChild );
-	},
-	contents: function( elem ) {
-		return elem.contentDocument || jQuery.merge( [], elem.childNodes );
-	}
-}, function( name, fn ) {
-	jQuery.fn[ name ] = function( until, selector ) {
-		var matched = jQuery.map( this, fn, until );
-
-		if ( name.slice( -5 ) !== "Until" ) {
-			selector = until;
-		}
-
-		if ( selector && typeof selector === "string" ) {
-			matched = jQuery.filter( selector, matched );
-		}
-
-		if ( this.length > 1 ) {
-
-			// Remove duplicates
-			if ( !guaranteedUnique[ name ] ) {
-				jQuery.uniqueSort( matched );
-			}
-
-			// Reverse order for parents* and prev-derivatives
-			if ( rparentsprev.test( name ) ) {
-				matched.reverse();
-			}
-		}
-
-		return this.pushStack( matched );
-	};
-} );
-var rnotwhite = ( /\S+/g );
-
-
-
-// Convert String-formatted options into Object-formatted ones
-function createOptions( options ) {
-	var object = {};
-	jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
-		object[ flag ] = true;
-	} );
-	return object;
-}
-
-/*
- * Create a callback list using the following parameters:
- *
- *	options: an optional list of space-separated options that will change how
- *			the callback list behaves or a more traditional option object
- *
- * By default a callback list will act like an event callback list and can be
- * "fired" multiple times.
- *
- * Possible options:
- *
- *	once:			will ensure the callback list can only be fired once (like a Deferred)
- *
- *	memory:			will keep track of previous values and will call any callback added
- *					after the list has been fired right away with the latest "memorized"
- *					values (like a Deferred)
- *
- *	unique:			will ensure a callback can only be added once (no duplicate in the list)
- *
- *	stopOnFalse:	interrupt callings when a callback returns false
- *
- */
-jQuery.Callbacks = function( options ) {
-
-	// Convert options from String-formatted to Object-formatted if needed
-	// (we check in cache first)
-	options = typeof options === "string" ?
-		createOptions( options ) :
-		jQuery.extend( {}, options );
-
-	var // Flag to know if list is currently firing
-		firing,
-
-		// Last fire value for non-forgettable lists
-		memory,
-
-		// Flag to know if list was already fired
-		fired,
-
-		// Flag to prevent firing
-		locked,
-
-		// Actual callback list
-		list = [],
-
-		// Queue of execution data for repeatable lists
-		queue = [],
-
-		// Index of currently firing callback (modified by add/remove as needed)
-		firingIndex = -1,
-
-		// Fire callbacks
-		fire = function() {
-
-			// Enforce single-firing
-			locked = options.once;
-
-			// Execute callbacks for all pending executions,
-			// respecting firingIndex overrides and runtime changes
-			fired = firing = true;
-			for ( ; queue.length; firingIndex = -1 ) {
-				memory = queue.shift();
-				while ( ++firingIndex < list.length ) {
-
-					// Run callback and check for early termination
-					if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
-						options.stopOnFalse ) {
-
-						// Jump to end and forget the data so .add doesn't re-fire
-						firingIndex = list.length;
-						memory = false;
-					}
-				}
-			}
-
-			// Forget the data if we're done with it
-			if ( !options.memory ) {
-				memory = false;
-			}
-
-			firing = false;
-
-			// Clean up if we're done firing for good
-			if ( locked ) {
-
-				// Keep an empty list if we have data for future add calls
-				if ( memory ) {
-					list = [];
-
-				// Otherwise, this object is spent
-				} else {
-					list = "";
-				}
-			}
-		},
-
-		// Actual Callbacks object
-		self = {
-
-			// Add a callback or a collection of callbacks to the list
-			add: function() {
-				if ( list ) {
-
-					// If we have memory from a past run, we should fire after adding
-					if ( memory && !firing ) {
-						firingIndex = list.length - 1;
-						queue.push( memory );
-					}
-
-					( function add( args ) {
-						jQuery.each( args, function( _, arg ) {
-							if ( jQuery.isFunction( arg ) ) {
-								if ( !options.unique || !self.has( arg ) ) {
-									list.push( arg );
-								}
-							} else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {
-
-								// Inspect recursively
-								add( arg );
-							}
-						} );
-					} )( arguments );
-
-					if ( memory && !firing ) {
-						fire();
-					}
-				}
-				return this;
-			},
-
-			// Remove a callback from the list
-			remove: function() {
-				jQuery.each( arguments, function( _, arg ) {
-					var index;
-					while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
-						list.splice( index, 1 );
-
-						// Handle firing indexes
-						if ( index <= firingIndex ) {
-							firingIndex--;
-						}
-					}
-				} );
-				return this;
-			},
-
-			// Check if a given callback is in the list.
-			// If no argument is given, return whether or not list has callbacks attached.
-			has: function( fn ) {
-				return fn ?
-					jQuery.inArray( fn, list ) > -1 :
-					list.length > 0;
-			},
-
-			// Remove all callbacks from the list
-			empty: function() {
-				if ( list ) {
-					list = [];
-				}
-				return this;
-			},
-
-			// Disable .fire and .add
-			// Abort any current/pending executions
-			// Clear all callbacks and values
-			disable: function() {
-				locked = queue = [];
-				list = memory = "";
-				return this;
-			},
-			disabled: function() {
-				return !list;
-			},
-
-			// Disable .fire
-			// Also disable .add unless we have memory (since it would have no effect)
-			// Abort any pending executions
-			lock: function() {
-				locked = queue = [];
-				if ( !memory && !firing ) {
-					list = memory = "";
-				}
-				return this;
-			},
-			locked: function() {
-				return !!locked;
-			},
-
-			// Call all callbacks with the given context and arguments
-			fireWith: function( context, args ) {
-				if ( !locked ) {
-					args = args || [];
-					args = [ context, args.slice ? args.slice() : args ];
-					queue.push( args );
-					if ( !firing ) {
-						fire();
-					}
-				}
-				return this;
-			},
-
-			// Call all the callbacks with the given arguments
-			fire: function() {
-				self.fireWith( this, arguments );
-				return this;
-			},
-
-			// To know if the callbacks have already been called at least once
-			fired: function() {
-				return !!fired;
-			}
-		};
-
-	return self;
-};
-
-
-function Identity( v ) {
-	return v;
-}
-function Thrower( ex ) {
-	throw ex;
-}
-
-jQuery.extend( {
-
-	Deferred: function( func ) {
-		var tuples = [
-
-				// action, add listener, callbacks,
-				// ... .then handlers, argument index, [final state]
-				[ "notify", "progress", jQuery.Callbacks( "memory" ),
-					jQuery.Callbacks( "memory" ), 2 ],
-				[ "resolve", "done", jQuery.Callbacks( "once memory" ),
-					jQuery.Callbacks( "once memory" ), 0, "resolved" ],
-				[ "reject", "fail", jQuery.Callbacks( "once memory" ),
-					jQuery.Callbacks( "once memory" ), 1, "rejected" ]
-			],
-			state = "pending",
-			promise = {
-				state: function() {
-					return state;
-				},
-				always: function() {
-					deferred.done( arguments ).fail( arguments );
-					return this;
-				},
-				"catch": function( fn ) {
-					return promise.then( null, fn );
-				},
-
-				// Keep pipe for back-compat
-				pipe: function( /* fnDone, fnFail, fnProgress */ ) {
-					var fns = arguments;
-
-					return jQuery.Deferred( function( newDefer ) {
-						jQuery.each( tuples, function( i, tuple ) {
-
-							// Map tuples (progress, done, fail) to arguments (done, fail, progress)
-							var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
-
-							// deferred.progress(function() { bind to newDefer or newDefer.notify })
-							// deferred.done(function() { bind to newDefer or newDefer.resolve })
-							// deferred.fail(function() { bind to newDefer or newDefer.reject })
-							deferred[ tuple[ 1 ] ]( function() {
-								var returned = fn && fn.apply( this, arguments );
-								if ( returned && jQuery.isFunction( returned.promise ) ) {
-									returned.promise()
-										.progress( newDefer.notify )
-										.done( newDefer.resolve )
-										.fail( newDefer.reject );
-								} else {
-									newDefer[ tuple[ 0 ] + "With" ](
-										this === promise ? newDefer.promise() : this,
-										fn ? [ returned ] : arguments
-									);
-								}
-							} );
-						} );
-						fns = null;
-					} ).promise();
-				},
-				then: function( onFulfilled, onRejected, onProgress ) {
-					var maxDepth = 0;
-					function resolve( depth, deferred, handler, special ) {
-						return function() {
-							var that = this === promise ? undefined : this,
-								args = arguments,
-								mightThrow = function() {
-									var returned, then;
-
-									// Support: Promises/A+ section 2.3.3.3.3
-									// https://promisesaplus.com/#point-59
-									// Ignore double-resolution attempts
-									if ( depth < maxDepth ) {
-										return;
-									}
-
-									returned = handler.apply( that, args );
-
-									// Support: Promises/A+ section 2.3.1
-									// https://promisesaplus.com/#point-48
-									if ( returned === deferred.promise() ) {
-										throw new TypeError( "Thenable self-resolution" );
-									}
-
-									// Support: Promises/A+ sections 2.3.3.1, 3.5
-									// https://promisesaplus.com/#point-54
-									// https://promisesaplus.com/#point-75
-									// Retrieve `then` only once
-									then = returned &&
-
-										// Support: Promises/A+ section 2.3.4
-										// https://promisesaplus.com/#point-64
-										// Only check objects and functions for thenability
-										( typeof returned === "object" ||
-											typeof returned === "function" ) &&
-										returned.then;
-
-									// Handle a returned thenable
-									if ( jQuery.isFunction( then ) ) {
-
-										// Special processors (notify) just wait for resolution
-										if ( special ) {
-											then.call(
-												returned,
-												resolve( maxDepth, deferred, Identity, special ),
-												resolve( maxDepth, deferred, Thrower, special )
-											);
-
-										// Normal processors (resolve) also hook into progress
-										} else {
-
-											// ...and disregard older resolution values
-											maxDepth++;
-
-											then.call(
-												returned,
-												resolve( maxDepth, deferred, Identity, special ),
-												resolve( maxDepth, deferred, Thrower, special ),
-												resolve( maxDepth, deferred, Identity,
-													deferred.notify )
-											);
-										}
-
-									// Handle all other returned values
-									} else {
-
-										// Only substitute handlers pass on context
-										// and multiple values (non-spec behavior)
-										if ( handler !== Identity ) {
-											that = undefined;
-											args = [ returned ];
-										}
-
-										// Process the value(s)
-										// Default process is resolve
-										( special || deferred.resolveWith )(
-											that || deferred.promise(), args );
-									}
-								},
-
-								// Only normal processors (resolve) catch and reject exceptions
-								process = special ?
-									mightThrow :
-									function() {
-										try {
-											mightThrow();
-										} catch ( e ) {
-
-											if ( jQuery.Deferred.exceptionHook ) {
-												jQuery.Deferred.exceptionHook( e,
-													process.stackTrace );
-											}
-
-											// Support: Promises/A+ section 2.3.3.3.4.1
-											// https://promisesaplus.com/#point-61
-											// Ignore post-resolution exceptions
-											if ( depth + 1 >= maxDepth ) {
-
-												// Only substitute handlers pass on context
-												// and multiple values (non-spec behavior)
-												if ( handler !== Thrower ) {
-													that = undefined;
-													args = [ e ];
-												}
-
-												deferred.rejectWith( that || deferred.promise(),
-													args );
-											}
-										}
-									};
-
-							// Support: Promises/A+ section 2.3.3.3.1
-							// https://promisesaplus.com/#point-57
-							// Re-resolve promises immediately to dodge false rejection from
-							// subsequent errors
-							if ( depth ) {
-								process();
-							} else {
-
-								// Call an optional hook to record the stack, in case of exception
-								// since it's otherwise lost when execution goes async
-								if ( jQuery.Deferred.getStackHook ) {
-									process.stackTrace = jQuery.Deferred.getStackHook();
-								}
-								window.setTimeout( process );
-							}
-						};
-					}
-
-					return jQuery.Deferred( function( newDefer ) {
-
-						// progress_handlers.add( ... )
-						tuples[ 0 ][ 3 ].add(
-							resolve(
-								0,
-								newDefer,
-								jQuery.isFunction( onProgress ) ?
-									onProgress :
-									Identity,
-								newDefer.notifyWith
-							)
-						);
-
-						// fulfilled_handlers.add( ... )
-						tuples[ 1 ][ 3 ].add(
-							resolve(
-								0,
-								newDefer,
-								jQuery.isFunction( onFulfilled ) ?
-									onFulfilled :
-									Identity
-							)
-						);
-
-						// rejected_handlers.add( ... )
-						tuples[ 2 ][ 3 ].add(
-							resolve(
-								0,
-								newDefer,
-								jQuery.isFunction( onRejected ) ?
-									onRejected :
-									Thrower
-							)
-						);
-					} ).promise();
-				},
-
-				// Get a promise for this deferred
-				// If obj is provided, the promise aspect is added to the object
-				promise: function( obj ) {
-					return obj != null ? jQuery.extend( obj, promise ) : promise;
-				}
-			},
-			deferred = {};
-
-		// Add list-specific methods
-		jQuery.each( tuples, function( i, tuple ) {
-			var list = tuple[ 2 ],
-				stateString = tuple[ 5 ];
-
-			// promise.progress = list.add
-			// promise.done = list.add
-			// promise.fail = list.add
-			promise[ tuple[ 1 ] ] = list.add;
-
-			// Handle state
-			if ( stateString ) {
-				list.add(
-					function() {
-
-						// state = "resolved" (i.e., fulfilled)
-						// state = "rejected"
-						state = stateString;
-					},
-
-					// rejected_callbacks.disable
-					// fulfilled_callbacks.disable
-					tuples[ 3 - i ][ 2 ].disable,
-
-					// progress_callbacks.lock
-					tuples[ 0 ][ 2 ].lock
-				);
-			}
-
-			// progress_handlers.fire
-			// fulfilled_handlers.fire
-			// rejected_handlers.fire
-			list.add( tuple[ 3 ].fire );
-
-			// deferred.notify = function() { deferred.notifyWith(...) }
-			// deferred.resolve = function() { deferred.resolveWith(...) }
-			// deferred.reject = function() { deferred.rejectWith(...) }
-			deferred[ tuple[ 0 ] ] = function() {
-				deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments );
-				return this;
-			};
-
-			// deferred.notifyWith = list.fireWith
-			// deferred.resolveWith = list.fireWith
-			// deferred.rejectWith = list.fireWith
-			deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
-		} );
-
-		// Make the deferred a promise
-		promise.promise( deferred );
-
-		// Call given func if any
-		if ( func ) {
-			func.call( deferred, deferred );
-		}
-
-		// All done!
-		return deferred;
-	},
-
-	// Deferred helper
-	when: function() {
-		var method, resolveContexts,
-			i = 0,
-			resolveValues = slice.call( arguments ),
-			length = resolveValues.length,
-
-			// the count of uncompleted subordinates
-			remaining = length,
-
-			// the master Deferred.
-			master = jQuery.Deferred(),
-
-			// Update function for both resolving subordinates
-			updateFunc = function( i ) {
-				return function( value ) {
-					resolveContexts[ i ] = this;
-					resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
-					if ( !( --remaining ) ) {
-						master.resolveWith(
-							resolveContexts.length === 1 ? resolveContexts[ 0 ] : resolveContexts,
-							resolveValues
-						);
-					}
-				};
-			};
-
-		// Add listeners to promise-like subordinates; treat others as resolved
-		if ( length > 0 ) {
-			resolveContexts = new Array( length );
-			for ( ; i < length; i++ ) {
-
-				// jQuery.Deferred - treated specially to get resolve-sync behavior
-				if ( resolveValues[ i ] &&
-					jQuery.isFunction( ( method = resolveValues[ i ].promise ) ) ) {
-
-					method.call( resolveValues[ i ] )
-						.done( updateFunc( i ) )
-						.fail( master.reject );
-
-				// Other thenables
-				} else if ( resolveValues[ i ] &&
-					jQuery.isFunction( ( method = resolveValues[ i ].then ) ) ) {
-
-					method.call(
-						resolveValues[ i ],
-						updateFunc( i ),
-						master.reject
-					);
-				} else {
-					updateFunc( i )( resolveValues[ i ] );
-				}
-			}
-
-		// If we're not waiting on anything, resolve the master
-		} else {
-			master.resolveWith();
-		}
-
-		return master.promise();
-	}
-} );
-
-
-// These usually indicate a programmer mistake during development,
-// warn about them ASAP rather than swallowing them by default.
-var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
-
-jQuery.Deferred.exceptionHook = function( error, stack ) {
-
-	// Support: IE9
-	// Console exists when dev tools are open, which can happen at any time
-	if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
-		window.console.warn( "jQuery.Deferred exception: " + error.message, stack );
-	}
-};
-
-
-
-
-// The deferred used on DOM ready
-var readyList;
-
-jQuery.fn.ready = function( fn ) {
-
-	// Add the callback
-	jQuery.ready.promise().done( fn );
-
-	return this;
-};
-
-jQuery.extend( {
-
-	// Is the DOM ready to be used? Set to true once it occurs.
-	isReady: false,
-
-	// A counter to track how many items to wait for before
-	// the ready event fires. See #6781
-	readyWait: 1,
-
-	// Hold (or release) the ready event
-	holdReady: function( hold ) {
-		if ( hold ) {
-			jQuery.readyWait++;
-		} else {
-			jQuery.ready( true );
-		}
-	},
-
-	// Handle when the DOM is ready
-	ready: function( wait ) {
-
-		// Abort if there are pending holds or we're already ready
-		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
-			return;
-		}
-
-		// Remember that the DOM is ready
-		jQuery.isReady = true;
-
-		// If a normal DOM Ready event fired, decrement, and wait if need be
-		if ( wait !== true && --jQuery.readyWait > 0 ) {
-			return;
-		}
-
-		// If there are functions bound, to execute
-		readyList.resolveWith( document, [ jQuery ] );
-	}
-} );
-
-/**
- * The ready event handler and self cleanup method
- */
-function completed() {
-	document.removeEventListener( "DOMContentLoaded", completed );
-	window.removeEventListener( "load", completed );
-	jQuery.ready();
-}
-
-jQuery.ready.promise = function( obj ) {
-	if ( !readyList ) {
-
-		readyList = jQuery.Deferred();
-
-		// Catch cases where $(document).ready() is called
-		// after the browser event has already occurred.
-		// Support: IE9-10 only
-		// Older IE sometimes signals "interactive" too soon
-		if ( document.readyState === "complete" ||
-			( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
-
-			// Handle it asynchronously to allow scripts the opportunity to delay ready
-			window.setTimeout( jQuery.ready );
-
-		} else {
-
-			// Use the handy event callback
-			document.addEventListener( "DOMContentLoaded", completed );
-
-			// A fallback to window.onload, that will always work
-			window.addEventListener( "load", completed );
-		}
-	}
-	return readyList.promise( obj );
-};
-
-// Kick off the DOM ready check even if the user does not
-jQuery.ready.promise();
-
-
-
-
-// Multifunctional method to get and set values of a collection
-// The value/s can optionally be executed if it's a function
-var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
-	var i = 0,
-		len = elems.length,
-		bulk = key == null;
-
-	// Sets many values
-	if ( jQuery.type( key ) === "object" ) {
-		chainable = true;
-		for ( i in key ) {
-			access( elems, fn, i, key[ i ], true, emptyGet, raw );
-		}
-
-	// Sets one value
-	} else if ( value !== undefined ) {
-		chainable = true;
-
-		if ( !jQuery.isFunction( value ) ) {
-			raw = true;
-		}
-
-		if ( bulk ) {
-
-			// Bulk operations run against the entire set
-			if ( raw ) {
-				fn.call( elems, value );
-				fn = null;
-
-			// ...except when executing function values
-			} else {
-				bulk = fn;
-				fn = function( elem, key, value ) {
-					return bulk.call( jQuery( elem ), value );
-				};
-			}
-		}
-
-		if ( fn ) {
-			for ( ; i < len; i++ ) {
-				fn(
-					elems[ i ], key, raw ?
-					value :
-					value.call( elems[ i ], i, fn( elems[ i ], key ) )
-				);
-			}
-		}
-	}
-
-	return chainable ?
-		elems :
-
-		// Gets
-		bulk ?
-			fn.call( elems ) :
-			len ? fn( elems[ 0 ], key ) : emptyGet;
-};
-var acceptData = function( owner ) {
-
-	// Accepts only:
-	//  - Node
-	//    - Node.ELEMENT_NODE
-	//    - Node.DOCUMENT_NODE
-	//  - Object
-	//    - Any
-	/* jshint -W018 */
-	return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
-};
-
-
-
-
-function Data() {
-	this.expando = jQuery.expando + Data.uid++;
-}
-
-Data.uid = 1;
-
-Data.prototype = {
-
-	cache: function( owner ) {
-
-		// Check if the owner object already has a cache
-		var value = owner[ this.expando ];
-
-		// If not, create one
-		if ( !value ) {
-			value = {};
-
-			// We can accept data for non-element nodes in modern browsers,
-			// but we should not, see #8335.
-			// Always return an empty object.
-			if ( acceptData( owner ) ) {
-
-				// If it is a node unlikely to be stringify-ed or looped over
-				// use plain assignment
-				if ( owner.nodeType ) {
-					owner[ this.expando ] = value;
-
-				// Otherwise secure it in a non-enumerable property
-				// configurable must be true to allow the property to be
-				// deleted when data is removed
-				} else {
-					Object.defineProperty( owner, this.expando, {
-						value: value,
-						configurable: true
-					} );
-				}
-			}
-		}
-
-		return value;
-	},
-	set: function( owner, data, value ) {
-		var prop,
-			cache = this.cache( owner );
-
-		// Handle: [ owner, key, value ] args
-		// Always use camelCase key (gh-2257)
-		if ( typeof data === "string" ) {
-			cache[ jQuery.camelCase( data ) ] = value;
-
-		// Handle: [ owner, { properties } ] args
-		} else {
-
-			// Copy the properties one-by-one to the cache object
-			for ( prop in data ) {
-				cache[ jQuery.camelCase( prop ) ] = data[ prop ];
-			}
-		}
-		return cache;
-	},
-	get: function( owner, key ) {
-		return key === undefined ?
-			this.cache( owner ) :
-
-			// Always use camelCase key (gh-2257)
-			owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ];
-	},
-	access: function( owner, key, value ) {
-
-		// In cases where either:
-		//
-		//   1. No key was specified
-		//   2. A string key was specified, but no value provided
-		//
-		// Take the "read" path and allow the get method to determine
-		// which value to return, respectively either:
-		//
-		//   1. The entire cache object
-		//   2. The data stored at the key
-		//
-		if ( key === undefined ||
-				( ( key && typeof key === "string" ) && value === undefined ) ) {
-
-			return this.get( owner, key );
-		}
-
-		// When the key is not a string, or both a key and value
-		// are specified, set or extend (existing objects) with either:
-		//
-		//   1. An object of properties
-		//   2. A key and value
-		//
-		this.set( owner, key, value );
-
-		// Since the "set" path can have two possible entry points
-		// return the expected data based on which path was taken[*]
-		return value !== undefined ? value : key;
-	},
-	remove: function( owner, key ) {
-		var i,
-			cache = owner[ this.expando ];
-
-		if ( cache === undefined ) {
-			return;
-		}
-
-		if ( key !== undefined ) {
-
-			// Support array or space separated string of keys
-			if ( jQuery.isArray( key ) ) {
-
-				// If key is an array of keys...
-				// We always set camelCase keys, so remove that.
-				key = key.map( jQuery.camelCase );
-			} else {
-				key = jQuery.camelCase( key );
-
-				// If a key with the spaces exists, use it.
-				// Otherwise, create an array by matching non-whitespace
-				key = key in cache ?
-					[ key ] :
-					( key.match( rnotwhite ) || [] );
-			}
-
-			i = key.length;
-
-			while ( i-- ) {
-				delete cache[ key[ i ] ];
-			}
-		}
-
-		// Remove the expando if there's no more data
-		if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
-
-			// Support: Chrome <= 35-45+
-			// Webkit & Blink performance suffers when deleting properties
-			// from DOM nodes, so set to undefined instead
-			// https://code.google.com/p/chromium/issues/detail?id=378607
-			if ( owner.nodeType ) {
-				owner[ this.expando ] = undefined;
-			} else {
-				delete owner[ this.expando ];
-			}
-		}
-	},
-	hasData: function( owner ) {
-		var cache = owner[ this.expando ];
-		return cache !== undefined && !jQuery.isEmptyObject( cache );
-	}
-};
-var dataPriv = new Data();
-
-var dataUser = new Data();
-
-
-
-//	Implementation Summary
-//
-//	1. Enforce API surface and semantic compatibility with 1.9.x branch
-//	2. Improve the module's maintainability by reducing the storage
-//		paths to a single mechanism.
-//	3. Use the same single mechanism to support "private" and "user" data.
-//	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
-//	5. Avoid exposing implementation details on user objects (eg. expando properties)
-//	6. Provide a clear path for implementation upgrade to WeakMap in 2014
-
-var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
-	rmultiDash = /[A-Z]/g;
-
-function dataAttr( elem, key, data ) {
-	var name;
-
-	// If nothing was found internally, try to fetch any
-	// data from the HTML5 data-* attribute
-	if ( data === undefined && elem.nodeType === 1 ) {
-		name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
-		data = elem.getAttribute( name );
-
-		if ( typeof data === "string" ) {
-			try {
-				data = data === "true" ? true :
-					data === "false" ? false :
-					data === "null" ? null :
-
-					// Only convert to a number if it doesn't change the string
-					+data + "" === data ? +data :
-					rbrace.test( data ) ? jQuery.parseJSON( data ) :
-					data;
-			} catch ( e ) {}
-
-			// Make sure we set the data so it isn't changed later
-			dataUser.set( elem, key, data );
-		} else {
-			data = undefined;
-		}
-	}
-	return data;
-}
-
-jQuery.extend( {
-	hasData: function( elem ) {
-		return dataUser.hasData( elem ) || dataPriv.hasData( elem );
-	},
-
-	data: function( elem, name, data ) {
-		return dataUser.access( elem, name, data );
-	},
-
-	removeData: function( elem, name ) {
-		dataUser.remove( elem, name );
-	},
-
-	// TODO: Now that all calls to _data and _removeData have been replaced
-	// with direct calls to dataPriv methods, these can be deprecated.
-	_data: function( elem, name, data ) {
-		return dataPriv.access( elem, name, data );
-	},
-
-	_removeData: function( elem, name ) {
-		dataPriv.remove( elem, name );
-	}
-} );
-
-jQuery.fn.extend( {
-	data: function( key, value ) {
-		var i, name, data,
-			elem = this[ 0 ],
-			attrs = elem && elem.attributes;
-
-		// Gets all values
-		if ( key === undefined ) {
-			if ( this.length ) {
-				data = dataUser.get( elem );
-
-				if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
-					i = attrs.length;
-					while ( i-- ) {
-
-						// Support: IE11+
-						// The attrs elements can be null (#14894)
-						if ( attrs[ i ] ) {
-							name = attrs[ i ].name;
-							if ( name.indexOf( "data-" ) === 0 ) {
-								name = jQuery.camelCase( name.slice( 5 ) );
-								dataAttr( elem, name, data[ name ] );
-							}
-						}
-					}
-					dataPriv.set( elem, "hasDataAttrs", true );
-				}
-			}
-
-			return data;
-		}
-
-		// Sets multiple values
-		if ( typeof key === "object" ) {
-			return this.each( function() {
-				dataUser.set( this, key );
-			} );
-		}
-
-		return access( this, function( value ) {
-			var data;
-
-			// The calling jQuery object (element matches) is not empty
-			// (and therefore has an element appears at this[ 0 ]) and the
-			// `value` parameter was not undefined. An empty jQuery object
-			// will result in `undefined` for elem = this[ 0 ] which will
-			// throw an exception if an attempt to read a data cache is made.
-			if ( elem && value === undefined ) {
-
-				// Attempt to get data from the cache
-				// The key will always be camelCased in Data
-				data = dataUser.get( elem, key );
-				if ( data !== undefined ) {
-					return data;
-				}
-
-				// Attempt to "discover" the data in
-				// HTML5 custom data-* attrs
-				data = dataAttr( elem, key );
-				if ( data !== undefined ) {
-					return data;
-				}
-
-				// We tried really hard, but the data doesn't exist.
-				return;
-			}
-
-			// Set the data...
-			this.each( function() {
-
-				// We always store the camelCased key
-				dataUser.set( this, key, value );
-			} );
-		}, null, value, arguments.length > 1, null, true );
-	},
-
-	removeData: function( key ) {
-		return this.each( function() {
-			dataUser.remove( this, key );
-		} );
-	}
-} );
-
-
-jQuery.extend( {
-	queue: function( elem, type, data ) {
-		var queue;
-
-		if ( elem ) {
-			type = ( type || "fx" ) + "queue";
-			queue = dataPriv.get( elem, type );
-
-			// Speed up dequeue by getting out quickly if this is just a lookup
-			if ( data ) {
-				if ( !queue || jQuery.isArray( data ) ) {
-					queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
-				} else {
-					queue.push( data );
-				}
-			}
-			return queue || [];
-		}
-	},
-
-	dequeue: function( elem, type ) {
-		type = type || "fx";
-
-		var queue = jQuery.queue( elem, type ),
-			startLength = queue.length,
-			fn = queue.shift(),
-			hooks = jQuery._queueHooks( elem, type ),
-			next = function() {
-				jQuery.dequeue( elem, type );
-			};
-
-		// If the fx queue is dequeued, always remove the progress sentinel
-		if ( fn === "inprogress" ) {
-			fn = queue.shift();
-			startLength--;
-		}
-
-		if ( fn ) {
-
-			// Add a progress sentinel to prevent the fx queue from being
-			// automatically dequeued
-			if ( type === "fx" ) {
-				queue.unshift( "inprogress" );
-			}
-
-			// Clear up the last queue stop function
-			delete hooks.stop;
-			fn.call( elem, next, hooks );
-		}
-
-		if ( !startLength && hooks ) {
-			hooks.empty.fire();
-		}
-	},
-
-	// Not public - generate a queueHooks object, or return the current one
-	_queueHooks: function( elem, type ) {
-		var key = type + "queueHooks";
-		return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
-			empty: jQuery.Callbacks( "once memory" ).add( function() {
-				dataPriv.remove( elem, [ type + "queue", key ] );
-			} )
-		} );
-	}
-} );
-
-jQuery.fn.extend( {
-	queue: function( type, data ) {
-		var setter = 2;
-
-		if ( typeof type !== "string" ) {
-			data = type;
-			type = "fx";
-			setter--;
-		}
-
-		if ( arguments.length < setter ) {
-			return jQuery.queue( this[ 0 ], type );
-		}
-
-		return data === undefined ?
-			this :
-			this.each( function() {
-				var queue = jQuery.queue( this, type, data );
-
-				// Ensure a hooks for this queue
-				jQuery._queueHooks( this, type );
-
-				if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
-					jQuery.dequeue( this, type );
-				}
-			} );
-	},
-	dequeue: function( type ) {
-		return this.each( function() {
-			jQuery.dequeue( this, type );
-		} );
-	},
-	clearQueue: function( type ) {
-		return this.queue( type || "fx", [] );
-	},
-
-	// Get a promise resolved when queues of a certain type
-	// are emptied (fx is the type by default)
-	promise: function( type, obj ) {
-		var tmp,
-			count = 1,
-			defer = jQuery.Deferred(),
-			elements = this,
-			i = this.length,
-			resolve = function() {
-				if ( !( --count ) ) {
-					defer.resolveWith( elements, [ elements ] );
-				}
-			};
-
-		if ( typeof type !== "string" ) {
-			obj = type;
-			type = undefined;
-		}
-		type = type || "fx";
-
-		while ( i-- ) {
-			tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
-			if ( tmp && tmp.empty ) {
-				count++;
-				tmp.empty.add( resolve );
-			}
-		}
-		resolve();
-		return defer.promise( obj );
-	}
-} );
-var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
-
-var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
-
-
-var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
-
-var isHidden = function( elem, el ) {
-
-		// isHidden might be called from jQuery#filter function;
-		// in that case, element will be second argument
-		elem = el || elem;
-		return jQuery.css( elem, "display" ) === "none" ||
-			!jQuery.contains( elem.ownerDocument, elem );
-	};
-
-var swap = function( elem, options, callback, args ) {
-	var ret, name,
-		old = {};
-
-	// Remember the old values, and insert the new ones
-	for ( name in options ) {
-		old[ name ] = elem.style[ name ];
-		elem.style[ name ] = options[ name ];
-	}
-
-	ret = callback.apply( elem, args || [] );
-
-	// Revert the old values
-	for ( name in options ) {
-		elem.style[ name ] = old[ name ];
-	}
-
-	return ret;
-};
-
-
-
-
-function adjustCSS( elem, prop, valueParts, tween ) {
-	var adjusted,
-		scale = 1,
-		maxIterations = 20,
-		currentValue = tween ?
-			function() { return tween.cur(); } :
-			function() { return jQuery.css( elem, prop, "" ); },
-		initial = currentValue(),
-		unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
-
-		// Starting value computation is required for potential unit mismatches
-		initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
-			rcssNum.exec( jQuery.css( elem, prop ) );
-
-	if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
-
-		// Trust units reported by jQuery.css
-		unit = unit || initialInUnit[ 3 ];
-
-		// Make sure we update the tween properties later on
-		valueParts = valueParts || [];
-
-		// Iteratively approximate from a nonzero starting point
-		initialInUnit = +initial || 1;
-
-		do {
-
-			// If previous iteration zeroed out, double until we get *something*.
-			// Use string for doubling so we don't accidentally see scale as unchanged below
-			scale = scale || ".5";
-
-			// Adjust and apply
-			initialInUnit = initialInUnit / scale;
-			jQuery.style( elem, prop, initialInUnit + unit );
-
-		// Update scale, tolerating zero or NaN from tween.cur()
-		// Break the loop if scale is unchanged or perfect, or if we've just had enough.
-		} while (
-			scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations
-		);
-	}
-
-	if ( valueParts ) {
-		initialInUnit = +initialInUnit || +initial || 0;
-
-		// Apply relative offset (+=/-=) if specified
-		adjusted = valueParts[ 1 ] ?
-			initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
-			+valueParts[ 2 ];
-		if ( tween ) {
-			tween.unit = unit;
-			tween.start = initialInUnit;
-			tween.end = adjusted;
-		}
-	}
-	return adjusted;
-}
-
-
-var defaultDisplayMap = {};
-
-function getDefaultDisplay( elem ) {
-	var temp,
-		doc = elem.ownerDocument,
-		nodeName = elem.nodeName,
-		display = defaultDisplayMap[ nodeName ];
-
-	if ( display ) {
-		return display;
-	}
-
-	temp = doc.body.appendChild( doc.createElement( nodeName ) ),
-	display = jQuery.css( temp, "display" );
-
-	temp.parentNode.removeChild( temp );
-
-	if ( display === "none" ) {
-		display = "block";
-	}
-	defaultDisplayMap[ nodeName ] = display;
-
-	return display;
-}
-
-function showHide( elements, show ) {
-	var display, elem,
-		values = [],
-		index = 0,
-		length = elements.length;
-
-	// Determine new display value for elements that need to change
-	for ( ; index < length; index++ ) {
-		elem = elements[ index ];
-		if ( !elem.style ) {
-			continue;
-		}
-
-		display = elem.style.display;
-		if ( show ) {
-
-			// Since we force visibility upon cascade-hidden elements, an immediate (and slow)
-			// check is required in this first loop unless we have a nonempty display value (either
-			// inline or about-to-be-restored)
-			if ( display === "none" ) {
-				values[ index ] = dataPriv.get( elem, "display" ) || null;
-				if ( !values[ index ] ) {
-					elem.style.display = "";
-				}
-			}
-			if ( elem.style.display === "" && jQuery.css( elem, "display" ) === "none" &&
-
-					// Support: Firefox <=42 - 43
-					// Don't set inline display on disconnected elements with computed display: none
-					jQuery.contains( elem.ownerDocument, elem ) ) {
-
-				values[ index ] = getDefaultDisplay( elem );
-			}
-		} else {
-			if ( display !== "none" ) {
-				values[ index ] = "none";
-
-				// Remember what we're overwriting
-				dataPriv.set( elem, "display", display );
-			}
-		}
-	}
-
-	// Set the display of the elements in a second loop to avoid constant reflow
-	for ( index = 0; index < length; index++ ) {
-		if ( values[ index ] != null ) {
-			elements[ index ].style.display = values[ index ];
-		}
-	}
-
-	return elements;
-}
-
-jQuery.fn.extend( {
-	show: function() {
-		return showHide( this, true );
-	},
-	hide: function() {
-		return showHide( this );
-	},
-	toggle: function( state ) {
-		if ( typeof state === "boolean" ) {
-			return state ? this.show() : this.hide();
-		}
-
-		return this.each( function() {
-			if ( isHidden( this ) ) {
-				jQuery( this ).show();
-			} else {
-				jQuery( this ).hide();
-			}
-		} );
-	}
-} );
-var rcheckableType = ( /^(?:checkbox|radio)$/i );
-
-var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i );
-
-var rscriptType = ( /^$|\/(?:java|ecma)script/i );
-
-
-
-// We have to close these tags to support XHTML (#13200)
-var wrapMap = {
-
-	// Support: IE9
-	option: [ 1, "<select multiple='multiple'>", "</select>" ],
-
-	// XHTML parsers do not magically insert elements in the
-	// same way that tag soup parsers do. So we cannot shorten
-	// this by omitting <tbody> or other required elements.
-	thead: [ 1, "<table>", "</table>" ],
-	col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
-	tr: [ 2, "<table><tbody>", "</tbody></table>" ],
-	td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
-
-	_default: [ 0, "", "" ]
-};
-
-// Support: IE9
-wrapMap.optgroup = wrapMap.option;
-
-wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
-wrapMap.th = wrapMap.td;
-
-
-function getAll( context, tag ) {
-
-	// Support: IE9-11+
-	// Use typeof to avoid zero-argument method invocation on host objects (#15151)
-	var ret = typeof context.getElementsByTagName !== "undefined" ?
-			context.getElementsByTagName( tag || "*" ) :
-			typeof context.querySelectorAll !== "undefined" ?
-				context.querySelectorAll( tag || "*" ) :
-			[];
-
-	return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
-		jQuery.merge( [ context ], ret ) :
-		ret;
-}
-
-
-// Mark scripts as having already been evaluated
-function setGlobalEval( elems, refElements ) {
-	var i = 0,
-		l = elems.length;
-
-	for ( ; i < l; i++ ) {
-		dataPriv.set(
-			elems[ i ],
-			"globalEval",
-			!refElements || dataPriv.get( refElements[ i ], "globalEval" )
-		);
-	}
-}
-
-
-var rhtml = /<|&#?\w+;/;
-
-function buildFragment( elems, context, scripts, selection, ignored ) {
-	var elem, tmp, tag, wrap, contains, j,
-		fragment = context.createDocumentFragment(),
-		nodes = [],
-		i = 0,
-		l = elems.length;
-
-	for ( ; i < l; i++ ) {
-		elem = elems[ i ];
-
-		if ( elem || elem === 0 ) {
-
-			// Add nodes directly
-			if ( jQuery.type( elem ) === "object" ) {
-
-				// Support: Android<4.1, PhantomJS<2
-				// push.apply(_, arraylike) throws on ancient WebKit
-				jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
-
-			// Convert non-html into a text node
-			} else if ( !rhtml.test( elem ) ) {
-				nodes.push( context.createTextNode( elem ) );
-
-			// Convert html into DOM nodes
-			} else {
-				tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
-
-				// Deserialize a standard representation
-				tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
-				wrap = wrapMap[ tag ] || wrapMap._default;
-				tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
-
-				// Descend through wrappers to the right content
-				j = wrap[ 0 ];
-				while ( j-- ) {
-					tmp = tmp.lastChild;
-				}
-
-				// Support: Android<4.1, PhantomJS<2
-				// push.apply(_, arraylike) throws on ancient WebKit
-				jQuery.merge( nodes, tmp.childNodes );
-
-				// Remember the top-level container
-				tmp = fragment.firstChild;
-
-				// Ensure the created nodes are orphaned (#12392)
-				tmp.textContent = "";
-			}
-		}
-	}
-
-	// Remove wrapper from fragment
-	fragment.textContent = "";
-
-	i = 0;
-	while ( ( elem = nodes[ i++ ] ) ) {
-
-		// Skip elements already in the context collection (trac-4087)
-		if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
-			if ( ignored ) {
-				ignored.push( elem );
-			}
-			continue;
-		}
-
-		contains = jQuery.contains( elem.ownerDocument, elem );
-
-		// Append to fragment
-		tmp = getAll( fragment.appendChild( elem ), "script" );
-
-		// Preserve script evaluation history
-		if ( contains ) {
-			setGlobalEval( tmp );
-		}
-
-		// Capture executables
-		if ( scripts ) {
-			j = 0;
-			while ( ( elem = tmp[ j++ ] ) ) {
-				if ( rscriptType.test( elem.type || "" ) ) {
-					scripts.push( elem );
-				}
-			}
-		}
-	}
-
-	return fragment;
-}
-
-
-( function() {
-	var fragment = document.createDocumentFragment(),
-		div = fragment.appendChild( document.createElement( "div" ) ),
-		input = document.createElement( "input" );
-
-	// Support: Android 4.0-4.3
-	// Check state lost if the name is set (#11217)
-	// Support: Windows Web Apps (WWA)
-	// `name` and `type` must use .setAttribute for WWA (#14901)
-	input.setAttribute( "type", "radio" );
-	input.setAttribute( "checked", "checked" );
-	input.setAttribute( "name", "t" );
-
-	div.appendChild( input );
-
-	// Support: Android<4.2
-	// Older WebKit doesn't clone checked state correctly in fragments
-	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
-
-	// Support: IE<=11+
-	// Make sure textarea (and checkbox) defaultValue is properly cloned
-	div.innerHTML = "<textarea>x</textarea>";
-	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
-} )();
-
-
-var
-	rkeyEvent = /^key/,
-	rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
-	rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
-
-function returnTrue() {
-	return true;
-}
-
-function returnFalse() {
-	return false;
-}
-
-// Support: IE9
-// See #13393 for more info
-function safeActiveElement() {
-	try {
-		return document.activeElement;
-	} catch ( err ) { }
-}
-
-function on( elem, types, selector, data, fn, one ) {
-	var origFn, type;
-
-	// Types can be a map of types/handlers
-	if ( typeof types === "object" ) {
-
-		// ( types-Object, selector, data )
-		if ( typeof selector !== "string" ) {
-
-			// ( types-Object, data )
-			data = data || selector;
-			selector = undefined;
-		}
-		for ( type in types ) {
-			on( elem, type, selector, data, types[ type ], one );
-		}
-		return elem;
-	}
-
-	if ( data == null && fn == null ) {
-
-		// ( types, fn )
-		fn = selector;
-		data = selector = undefined;
-	} else if ( fn == null ) {
-		if ( typeof selector === "string" ) {
-
-			// ( types, selector, fn )
-			fn = data;
-			data = undefined;
-		} else {
-
-			// ( types, data, fn )
-			fn = data;
-			data = selector;
-			selector = undefined;
-		}
-	}
-	if ( fn === false ) {
-		fn = returnFalse;
-	}
-
-	if ( one === 1 ) {
-		origFn = fn;
-		fn = function( event ) {
-
-			// Can use an empty set, since event contains the info
-			jQuery().off( event );
-			return origFn.apply( this, arguments );
-		};
-
-		// Use same guid so caller can remove using origFn
-		fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
-	}
-	return elem.each( function() {
-		jQuery.event.add( this, types, fn, data, selector );
-	} );
-}
-
-/*
- * Helper functions for managing events -- not part of the public interface.
- * Props to Dean Edwards' addEvent library for many of the ideas.
- */
-jQuery.event = {
-
-	global: {},
-
-	add: function( elem, types, handler, data, selector ) {
-
-		var handleObjIn, eventHandle, tmp,
-			events, t, handleObj,
-			special, handlers, type, namespaces, origType,
-			elemData = dataPriv.get( elem );
-
-		// Don't attach events to noData or text/comment nodes (but allow plain objects)
-		if ( !elemData ) {
-			return;
-		}
-
-		// Caller can pass in an object of custom data in lieu of the handler
-		if ( handler.handler ) {
-			handleObjIn = handler;
-			handler = handleObjIn.handler;
-			selector = handleObjIn.selector;
-		}
-
-		// Make sure that the handler has a unique ID, used to find/remove it later
-		if ( !handler.guid ) {
-			handler.guid = jQuery.guid++;
-		}
-
-		// Init the element's event structure and main handler, if this is the first
-		if ( !( events = elemData.events ) ) {
-			events = elemData.events = {};
-		}
-		if ( !( eventHandle = elemData.handle ) ) {
-			eventHandle = elemData.handle = function( e ) {
-
-				// Discard the second event of a jQuery.event.trigger() and
-				// when an event is called after a page has unloaded
-				return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
-					jQuery.event.dispatch.apply( elem, arguments ) : undefined;
-			};
-		}
-
-		// Handle multiple events separated by a space
-		types = ( types || "" ).match( rnotwhite ) || [ "" ];
-		t = types.length;
-		while ( t-- ) {
-			tmp = rtypenamespace.exec( types[ t ] ) || [];
-			type = origType = tmp[ 1 ];
-			namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
-
-			// There *must* be a type, no attaching namespace-only handlers
-			if ( !type ) {
-				continue;
-			}
-
-			// If event changes its type, use the special event handlers for the changed type
-			special = jQuery.event.special[ type ] || {};
-
-			// If selector defined, determine special event api type, otherwise given type
-			type = ( selector ? special.delegateType : special.bindType ) || type;
-
-			// Update special based on newly reset type
-			special = jQuery.event.special[ type ] || {};
-
-			// handleObj is passed to all event handlers
-			handleObj = jQuery.extend( {
-				type: type,
-				origType: origType,
-				data: data,
-				handler: handler,
-				guid: handler.guid,
-				selector: selector,
-				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
-				namespace: namespaces.join( "." )
-			}, handleObjIn );
-
-			// Init the event handler queue if we're the first
-			if ( !( handlers = events[ type ] ) ) {
-				handlers = events[ type ] = [];
-				handlers.delegateCount = 0;
-
-				// Only use addEventListener if the special events handler returns false
-				if ( !special.setup ||
-					special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
-
-					if ( elem.addEventListener ) {
-						elem.addEventListener( type, eventHandle );
-					}
-				}
-			}
-
-			if ( special.add ) {
-				special.add.call( elem, handleObj );
-
-				if ( !handleObj.handler.guid ) {
-					handleObj.handler.guid = handler.guid;
-				}
-			}
-
-			// Add to the element's handler list, delegates in front
-			if ( selector ) {
-				handlers.splice( handlers.delegateCount++, 0, handleObj );
-			} else {
-				handlers.push( handleObj );
-			}
-
-			// Keep track of which events have ever been used, for event optimization
-			jQuery.event.global[ type ] = true;
-		}
-
-	},
-
-	// Detach an event or set of events from an element
-	remove: function( elem, types, handler, selector, mappedTypes ) {
-
-		var j, origCount, tmp,
-			events, t, handleObj,
-			special, handlers, type, namespaces, origType,
-			elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
-
-		if ( !elemData || !( events = elemData.events ) ) {
-			return;
-		}
-
-		// Once for each type.namespace in types; type may be omitted
-		types = ( types || "" ).match( rnotwhite ) || [ "" ];
-		t = types.length;
-		while ( t-- ) {
-			tmp = rtypenamespace.exec( types[ t ] ) || [];
-			type = origType = tmp[ 1 ];
-			namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
-
-			// Unbind all events (on this namespace, if provided) for the element
-			if ( !type ) {
-				for ( type in events ) {
-					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
-				}
-				continue;
-			}
-
-			special = jQuery.event.special[ type ] || {};
-			type = ( selector ? special.delegateType : special.bindType ) || type;
-			handlers = events[ type ] || [];
-			tmp = tmp[ 2 ] &&
-				new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
-
-			// Remove matching events
-			origCount = j = handlers.length;
-			while ( j-- ) {
-				handleObj = handlers[ j ];
-
-				if ( ( mappedTypes || origType === handleObj.origType ) &&
-					( !handler || handler.guid === handleObj.guid ) &&
-					( !tmp || tmp.test( handleObj.namespace ) ) &&
-					( !selector || selector === handleObj.selector ||
-						selector === "**" && handleObj.selector ) ) {
-					handlers.splice( j, 1 );
-
-					if ( handleObj.selector ) {
-						handlers.delegateCount--;
-					}
-					if ( special.remove ) {
-						special.remove.call( elem, handleObj );
-					}
-				}
-			}
-
-			// Remove generic event handler if we removed something and no more handlers exist
-			// (avoids potential for endless recursion during removal of special event handlers)
-			if ( origCount && !handlers.length ) {
-				if ( !special.teardown ||
-					special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
-
-					jQuery.removeEvent( elem, type, elemData.handle );
-				}
-
-				delete events[ type ];
-			}
-		}
-
-		// Remove data and the expando if it's no longer used
-		if ( jQuery.isEmptyObject( events ) ) {
-			dataPriv.remove( elem, "handle events" );
-		}
-	},
-
-	dispatch: function( event ) {
-
-		// Make a writable jQuery.Event from the native event object
-		event = jQuery.event.fix( event );
-
-		var i, j, ret, matched, handleObj,
-			handlerQueue = [],
-			args = slice.call( arguments ),
-			handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [],
-			special = jQuery.event.special[ event.type ] || {};
-
-		// Use the fix-ed jQuery.Event rather than the (read-only) native event
-		args[ 0 ] = event;
-		event.delegateTarget = this;
-
-		// Call the preDispatch hook for the mapped type, and let it bail if desired
-		if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
-			return;
-		}
-
-		// Determine handlers
-		handlerQueue = jQuery.event.handlers.call( this, event, handlers );
-
-		// Run delegates first; they may want to stop propagation beneath us
-		i = 0;
-		while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
-			event.currentTarget = matched.elem;
-
-			j = 0;
-			while ( ( handleObj = matched.handlers[ j++ ] ) &&
-				!event.isImmediatePropagationStopped() ) {
-
-				// Triggered event must either 1) have no namespace, or 2) have namespace(s)
-				// a subset or equal to those in the bound event (both can have no namespace).
-				if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) {
-
-					event.handleObj = handleObj;
-					event.data = handleObj.data;
-
-					ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
-						handleObj.handler ).apply( matched.elem, args );
-
-					if ( ret !== undefined ) {
-						if ( ( event.result = ret ) === false ) {
-							event.preventDefault();
-							event.stopPropagation();
-						}
-					}
-				}
-			}
-		}
-
-		// Call the postDispatch hook for the mapped type
-		if ( special.postDispatch ) {
-			special.postDispatch.call( this, event );
-		}
-
-		return event.result;
-	},
-
-	handlers: function( event, handlers ) {
-		var i, matches, sel, handleObj,
-			handlerQueue = [],
-			delegateCount = handlers.delegateCount,
-			cur = event.target;
-
-		// Support (at least): Chrome, IE9
-		// Find delegate handlers
-		// Black-hole SVG <use> instance trees (#13180)
-		//
-		// Support: Firefox<=42+
-		// Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343)
-		if ( delegateCount && cur.nodeType &&
-			( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) {
-
-			for ( ; cur !== this; cur = cur.parentNode || this ) {
-
-				// Don't check non-elements (#13208)
-				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
-				if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) {
-					matches = [];
-					for ( i = 0; i < delegateCount; i++ ) {
-						handleObj = handlers[ i ];
-
-						// Don't conflict with Object.prototype properties (#13203)
-						sel = handleObj.selector + " ";
-
-						if ( matches[ sel ] === undefined ) {
-							matches[ sel ] = handleObj.needsContext ?
-								jQuery( sel, this ).index( cur ) > -1 :
-								jQuery.find( sel, this, null, [ cur ] ).length;
-						}
-						if ( matches[ sel ] ) {
-							matches.push( handleObj );
-						}
-					}
-					if ( matches.length ) {
-						handlerQueue.push( { elem: cur, handlers: matches } );
-					}
-				}
-			}
-		}
-
-		// Add the remaining (directly-bound) handlers
-		if ( delegateCount < handlers.length ) {
-			handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } );
-		}
-
-		return handlerQueue;
-	},
-
-	// Includes some event props shared by KeyEvent and MouseEvent
-	props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " +
-		"metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ),
-
-	fixHooks: {},
-
-	keyHooks: {
-		props: "char charCode key keyCode".split( " " ),
-		filter: function( event, original ) {
-
-			// Add which for key events
-			if ( event.which == null ) {
-				event.which = original.charCode != null ? original.charCode : original.keyCode;
-			}
-
-			return event;
-		}
-	},
-
-	mouseHooks: {
-		props: ( "button buttons clientX clientY offsetX offsetY pageX pageY " +
-			"screenX screenY toElement" ).split( " " ),
-		filter: function( event, original ) {
-			var eventDoc, doc, body,
-				button = original.button;
-
-			// Calculate pageX/Y if missing and clientX/Y available
-			if ( event.pageX == null && original.clientX != null ) {
-				eventDoc = event.target.ownerDocument || document;
-				doc = eventDoc.documentElement;
-				body = eventDoc.body;
-
-				event.pageX = original.clientX +
-					( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
-					( doc && doc.clientLeft || body && body.clientLeft || 0 );
-				event.pageY = original.clientY +
-					( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) -
-					( doc && doc.clientTop  || body && body.clientTop  || 0 );
-			}
-
-			// Add which for click: 1 === left; 2 === middle; 3 === right
-			// Note: button is not normalized, so don't use it
-			if ( !event.which && button !== undefined ) {
-				event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
-			}
-
-			return event;
-		}
-	},
-
-	fix: function( event ) {
-		if ( event[ jQuery.expando ] ) {
-			return event;
-		}
-
-		// Create a writable copy of the event object and normalize some properties
-		var i, prop, copy,
-			type = event.type,
-			originalEvent = event,
-			fixHook = this.fixHooks[ type ];
-
-		if ( !fixHook ) {
-			this.fixHooks[ type ] = fixHook =
-				rmouseEvent.test( type ) ? this.mouseHooks :
-				rkeyEvent.test( type ) ? this.keyHooks :
-				{};
-		}
-		copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
-
-		event = new jQuery.Event( originalEvent );
-
-		i = copy.length;
-		while ( i-- ) {
-			prop = copy[ i ];
-			event[ prop ] = originalEvent[ prop ];
-		}
-
-		// Support: Safari 6-8+
-		// Target should not be a text node (#504, #13143)
-		if ( event.target.nodeType === 3 ) {
-			event.target = event.target.parentNode;
-		}
-
-		return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
-	},
-
-	special: {
-		load: {
-
-			// Prevent triggered image.load events from bubbling to window.load
-			noBubble: true
-		},
-		focus: {
-
-			// Fire native event if possible so blur/focus sequence is correct
-			trigger: function() {
-				if ( this !== safeActiveElement() && this.focus ) {
-					this.focus();
-					return false;
-				}
-			},
-			delegateType: "focusin"
-		},
-		blur: {
-			trigger: function() {
-				if ( this === safeActiveElement() && this.blur ) {
-					this.blur();
-					return false;
-				}
-			},
-			delegateType: "focusout"
-		},
-		click: {
-
-			// For checkbox, fire native event so checked state will be right
-			trigger: function() {
-				if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
-					this.click();
-					return false;
-				}
-			},
-
-			// For cross-browser consistency, don't fire native .click() on links
-			_default: function( event ) {
-				return jQuery.nodeName( event.target, "a" );
-			}
-		},
-
-		beforeunload: {
-			postDispatch: function( event ) {
-
-				// Support: Firefox 20+
-				// Firefox doesn't alert if the returnValue field is not set.
-				if ( event.result !== undefined && event.originalEvent ) {
-					event.originalEvent.returnValue = event.result;
-				}
-			}
-		}
-	}
-};
-
-jQuery.removeEvent = function( elem, type, handle ) {
-
-	// This "if" is needed for plain objects
-	if ( elem.removeEventListener ) {
-		elem.removeEventListener( type, handle );
-	}
-};
-
-jQuery.Event = function( src, props ) {
-
-	// Allow instantiation without the 'new' keyword
-	if ( !( this instanceof jQuery.Event ) ) {
-		return new jQuery.Event( src, props );
-	}
-
-	// Event object
-	if ( src && src.type ) {
-		this.originalEvent = src;
-		this.type = src.type;
-
-		// Events bubbling up the document may have been marked as prevented
-		// by a handler lower down the tree; reflect the correct value.
-		this.isDefaultPrevented = src.defaultPrevented ||
-				src.defaultPrevented === undefined &&
-
-				// Support: Android<4.0
-				src.returnValue === false ?
-			returnTrue :
-			returnFalse;
-
-	// Event type
-	} else {
-		this.type = src;
-	}
-
-	// Put explicitly provided properties onto the event object
-	if ( props ) {
-		jQuery.extend( this, props );
-	}
-
-	// Create a timestamp if incoming event doesn't have one
-	this.timeStamp = src && src.timeStamp || jQuery.now();
-
-	// Mark it as fixed
-	this[ jQuery.expando ] = true;
-};
-
-// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
-// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
-jQuery.Event.prototype = {
-	constructor: jQuery.Event,
-	isDefaultPrevented: returnFalse,
-	isPropagationStopped: returnFalse,
-	isImmediatePropagationStopped: returnFalse,
-
-	preventDefault: function() {
-		var e = this.originalEvent;
-
-		this.isDefaultPrevented = returnTrue;
-
-		if ( e ) {
-			e.preventDefault();
-		}
-	},
-	stopPropagation: function() {
-		var e = this.originalEvent;
-
-		this.isPropagationStopped = returnTrue;
-
-		if ( e ) {
-			e.stopPropagation();
-		}
-	},
-	stopImmediatePropagation: function() {
-		var e = this.originalEvent;
-
-		this.isImmediatePropagationStopped = returnTrue;
-
-		if ( e ) {
-			e.stopImmediatePropagation();
-		}
-
-		this.stopPropagation();
-	}
-};
-
-// Create mouseenter/leave events using mouseover/out and event-time checks
-// so that event delegation works in jQuery.
-// Do the same for pointerenter/pointerleave and pointerover/pointerout
-//
-// Support: Safari 7 only
-// Safari sends mouseenter too often; see:
-// https://code.google.com/p/chromium/issues/detail?id=470258
-// for the description of the bug (it existed in older Chrome versions as well).
-jQuery.each( {
-	mouseenter: "mouseover",
-	mouseleave: "mouseout",
-	pointerenter: "pointerover",
-	pointerleave: "pointerout"
-}, function( orig, fix ) {
-	jQuery.event.special[ orig ] = {
-		delegateType: fix,
-		bindType: fix,
-
-		handle: function( event ) {
-			var ret,
-				target = this,
-				related = event.relatedTarget,
-				handleObj = event.handleObj;
-
-			// For mouseenter/leave call the handler if related is outside the target.
-			// NB: No relatedTarget if the mouse left/entered the browser window
-			if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
-				event.type = handleObj.origType;
-				ret = handleObj.handler.apply( this, arguments );
-				event.type = fix;
-			}
-			return ret;
-		}
-	};
-} );
-
-jQuery.fn.extend( {
-
-	on: function( types, selector, data, fn ) {
-		return on( this, types, selector, data, fn );
-	},
-	one: function( types, selector, data, fn ) {
-		return on( this, types, selector, data, fn, 1 );
-	},
-	off: function( types, selector, fn ) {
-		var handleObj, type;
-		if ( types && types.preventDefault && types.handleObj ) {
-
-			// ( event )  dispatched jQuery.Event
-			handleObj = types.handleObj;
-			jQuery( types.delegateTarget ).off(
-				handleObj.namespace ?
-					handleObj.origType + "." + handleObj.namespace :
-					handleObj.origType,
-				handleObj.selector,
-				handleObj.handler
-			);
-			return this;
-		}
-		if ( typeof types === "object" ) {
-
-			// ( types-object [, selector] )
-			for ( type in types ) {
-				this.off( type, selector, types[ type ] );
-			}
-			return this;
-		}
-		if ( selector === false || typeof selector === "function" ) {
-
-			// ( types [, fn] )
-			fn = selector;
-			selector = undefined;
-		}
-		if ( fn === false ) {
-			fn = returnFalse;
-		}
-		return this.each( function() {
-			jQuery.event.remove( this, types, fn, selector );
-		} );
-	}
-} );
-
-
-var
-	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,
-
-	// Support: IE 10-11, Edge 10240+
-	// In IE/Edge using regex groups here causes severe slowdowns.
-	// See https://connect.microsoft.com/IE/feedback/details/1736512/
-	rnoInnerhtml = /<script|<style|<link/i,
-
-	// checked="checked" or checked
-	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
-	rscriptTypeMasked = /^true\/(.*)/,
-	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
-
-function manipulationTarget( elem, content ) {
-	if ( jQuery.nodeName( elem, "table" ) &&
-		jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {
-
-		return elem.getElementsByTagName( "tbody" )[ 0 ] || elem;
-	}
-
-	return elem;
-}
-
-// Replace/restore the type attribute of script elements for safe DOM manipulation
-function disableScript( elem ) {
-	elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
-	return elem;
-}
-function restoreScript( elem ) {
-	var match = rscriptTypeMasked.exec( elem.type );
-
-	if ( match ) {
-		elem.type = match[ 1 ];
-	} else {
-		elem.removeAttribute( "type" );
-	}
-
-	return elem;
-}
-
-function cloneCopyEvent( src, dest ) {
-	var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
-
-	if ( dest.nodeType !== 1 ) {
-		return;
-	}
-
-	// 1. Copy private data: events, handlers, etc.
-	if ( dataPriv.hasData( src ) ) {
-		pdataOld = dataPriv.access( src );
-		pdataCur = dataPriv.set( dest, pdataOld );
-		events = pdataOld.events;
-
-		if ( events ) {
-			delete pdataCur.handle;
-			pdataCur.events = {};
-
-			for ( type in events ) {
-				for ( i = 0, l = events[ type ].length; i < l; i++ ) {
-					jQuery.event.add( dest, type, events[ type ][ i ] );
-				}
-			}
-		}
-	}
-
-	// 2. Copy user data
-	if ( dataUser.hasData( src ) ) {
-		udataOld = dataUser.access( src );
-		udataCur = jQuery.extend( {}, udataOld );
-
-		dataUser.set( dest, udataCur );
-	}
-}
-
-// Fix IE bugs, see support tests
-function fixInput( src, dest ) {
-	var nodeName = dest.nodeName.toLowerCase();
-
-	// Fails to persist the checked state of a cloned checkbox or radio button.
-	if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
-		dest.checked = src.checked;
-
-	// Fails to return the selected option to the default selected state when cloning options
-	} else if ( nodeName === "input" || nodeName === "textarea" ) {
-		dest.defaultValue = src.defaultValue;
-	}
-}
-
-function domManip( collection, args, callback, ignored ) {
-
-	// Flatten any nested arrays
-	args = concat.apply( [], args );
-
-	var fragment, first, scripts, hasScripts, node, doc,
-		i = 0,
-		l = collection.length,
-		iNoClone = l - 1,
-		value = args[ 0 ],
-		isFunction = jQuery.isFunction( value );
-
-	// We can't cloneNode fragments that contain checked, in WebKit
-	if ( isFunction ||
-			( l > 1 && typeof value === "string" &&
-				!support.checkClone && rchecked.test( value ) ) ) {
-		return collection.each( function( index ) {
-			var self = collection.eq( index );
-			if ( isFunction ) {
-				args[ 0 ] = value.call( this, index, self.html() );
-			}
-			domManip( self, args, callback, ignored );
-		} );
-	}
-
-	if ( l ) {
-		fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
-		first = fragment.firstChild;
-
-		if ( fragment.childNodes.length === 1 ) {
-			fragment = first;
-		}
-
-		// Require either new content or an interest in ignored elements to invoke the callback
-		if ( first || ignored ) {
-			scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
-			hasScripts = scripts.length;
-
-			// Use the original fragment for the last item
-			// instead of the first because it can end up
-			// being emptied incorrectly in certain situations (#8070).
-			for ( ; i < l; i++ ) {
-				node = fragment;
-
-				if ( i !== iNoClone ) {
-					node = jQuery.clone( node, true, true );
-
-					// Keep references to cloned scripts for later restoration
-					if ( hasScripts ) {
-
-						// Support: Android<4.1, PhantomJS<2
-						// push.apply(_, arraylike) throws on ancient WebKit
-						jQuery.merge( scripts, getAll( node, "script" ) );
-					}
-				}
-
-				callback.call( collection[ i ], node, i );
-			}
-
-			if ( hasScripts ) {
-				doc = scripts[ scripts.length - 1 ].ownerDocument;
-
-				// Reenable scripts
-				jQuery.map( scripts, restoreScript );
-
-				// Evaluate executable scripts on first document insertion
-				for ( i = 0; i < hasScripts; i++ ) {
-					node = scripts[ i ];
-					if ( rscriptType.test( node.type || "" ) &&
-						!dataPriv.access( node, "globalEval" ) &&
-						jQuery.contains( doc, node ) ) {
-
-						if ( node.src ) {
-
-							// Optional AJAX dependency, but won't run scripts if not present
-							if ( jQuery._evalUrl ) {
-								jQuery._evalUrl( node.src );
-							}
-						} else {
-							DOMEval( node.textContent.replace( rcleanScript, "" ), doc );
-						}
-					}
-				}
-			}
-		}
-	}
-
-	return collection;
-}
-
-function remove( elem, selector, keepData ) {
-	var node,
-		nodes = selector ? jQuery.filter( selector, elem ) : elem,
-		i = 0;
-
-	for ( ; ( node = nodes[ i ] ) != null; i++ ) {
-		if ( !keepData && node.nodeType === 1 ) {
-			jQuery.cleanData( getAll( node ) );
-		}
-
-		if ( node.parentNode ) {
-			if ( keepData && jQuery.contains( node.ownerDocument, node ) ) {
-				setGlobalEval( getAll( node, "script" ) );
-			}
-			node.parentNode.removeChild( node );
-		}
-	}
-
-	return elem;
-}
-
-jQuery.extend( {
-	htmlPrefilter: function( html ) {
-		return html.replace( rxhtmlTag, "<$1></$2>" );
-	},
-
-	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
-		var i, l, srcElements, destElements,
-			clone = elem.cloneNode( true ),
-			inPage = jQuery.contains( elem.ownerDocument, elem );
-
-		// Fix IE cloning issues
-		if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
-				!jQuery.isXMLDoc( elem ) ) {
-
-			// We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2
-			destElements = getAll( clone );
-			srcElements = getAll( elem );
-
-			for ( i = 0, l = srcElements.length; i < l; i++ ) {
-				fixInput( srcElements[ i ], destElements[ i ] );
-			}
-		}
-
-		// Copy the events from the original to the clone
-		if ( dataAndEvents ) {
-			if ( deepDataAndEvents ) {
-				srcElements = srcElements || getAll( elem );
-				destElements = destElements || getAll( clone );
-
-				for ( i = 0, l = srcElements.length; i < l; i++ ) {
-					cloneCopyEvent( srcElements[ i ], destElements[ i ] );
-				}
-			} else {
-				cloneCopyEvent( elem, clone );
-			}
-		}
-
-		// Preserve script evaluation history
-		destElements = getAll( clone, "script" );
-		if ( destElements.length > 0 ) {
-			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
-		}
-
-		// Return the cloned set
-		return clone;
-	},
-
-	cleanData: function( elems ) {
-		var data, elem, type,
-			special = jQuery.event.special,
-			i = 0;
-
-		for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
-			if ( acceptData( elem ) ) {
-				if ( ( data = elem[ dataPriv.expando ] ) ) {
-					if ( data.events ) {
-						for ( type in data.events ) {
-							if ( special[ type ] ) {
-								jQuery.event.remove( elem, type );
-
-							// This is a shortcut to avoid jQuery.event.remove's overhead
-							} else {
-								jQuery.removeEvent( elem, type, data.handle );
-							}
-						}
-					}
-
-					// Support: Chrome <= 35-45+
-					// Assign undefined instead of using delete, see Data#remove
-					elem[ dataPriv.expando ] = undefined;
-				}
-				if ( elem[ dataUser.expando ] ) {
-
-					// Support: Chrome <= 35-45+
-					// Assign undefined instead of using delete, see Data#remove
-					elem[ dataUser.expando ] = undefined;
-				}
-			}
-		}
-	}
-} );
-
-jQuery.fn.extend( {
-	detach: function( selector ) {
-		return remove( this, selector, true );
-	},
-
-	remove: function( selector ) {
-		return remove( this, selector );
-	},
-
-	text: function( value ) {
-		return access( this, function( value ) {
-			return value === undefined ?
-				jQuery.text( this ) :
-				this.empty().each( function() {
-					if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
-						this.textContent = value;
-					}
-				} );
-		}, null, value, arguments.length );
-	},
-
-	append: function() {
-		return domManip( this, arguments, function( elem ) {
-			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
-				var target = manipulationTarget( this, elem );
-				target.appendChild( elem );
-			}
-		} );
-	},
-
-	prepend: function() {
-		return domManip( this, arguments, function( elem ) {
-			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
-				var target = manipulationTarget( this, elem );
-				target.insertBefore( elem, target.firstChild );
-			}
-		} );
-	},
-
-	before: function() {
-		return domManip( this, arguments, function( elem ) {
-			if ( this.parentNode ) {
-				this.parentNode.insertBefore( elem, this );
-			}
-		} );
-	},
-
-	after: function() {
-		return domManip( this, arguments, function( elem ) {
-			if ( this.parentNode ) {
-				this.parentNode.insertBefore( elem, this.nextSibling );
-			}
-		} );
-	},
-
-	empty: function() {
-		var elem,
-			i = 0;
-
-		for ( ; ( elem = this[ i ] ) != null; i++ ) {
-			if ( elem.nodeType === 1 ) {
-
-				// Prevent memory leaks
-				jQuery.cleanData( getAll( elem, false ) );
-
-				// Remove any remaining nodes
-				elem.textContent = "";
-			}
-		}
-
-		return this;
-	},
-
-	clone: function( dataAndEvents, deepDataAndEvents ) {
-		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
-		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
-
-		return this.map( function() {
-			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
-		} );
-	},
-
-	html: function( value ) {
-		return access( this, function( value ) {
-			var elem = this[ 0 ] || {},
-				i = 0,
-				l = this.length;
-
-			if ( value === undefined && elem.nodeType === 1 ) {
-				return elem.innerHTML;
-			}
-
-			// See if we can take a shortcut and just use innerHTML
-			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
-				!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
-
-				value = jQuery.htmlPrefilter( value );
-
-				try {
-					for ( ; i < l; i++ ) {
-						elem = this[ i ] || {};
-
-						// Remove element nodes and prevent memory leaks
-						if ( elem.nodeType === 1 ) {
-							jQuery.cleanData( getAll( elem, false ) );
-							elem.innerHTML = value;
-						}
-					}
-
-					elem = 0;
-
-				// If using innerHTML throws an exception, use the fallback method
-				} catch ( e ) {}
-			}
-
-			if ( elem ) {
-				this.empty().append( value );
-			}
-		}, null, value, arguments.length );
-	},
-
-	replaceWith: function() {
-		var ignored = [];
-
-		// Make the changes, replacing each non-ignored context element with the new content
-		return domManip( this, arguments, function( elem ) {
-			var parent = this.parentNode;
-
-			if ( jQuery.inArray( this, ignored ) < 0 ) {
-				jQuery.cleanData( getAll( this ) );
-				if ( parent ) {
-					parent.replaceChild( elem, this );
-				}
-			}
-
-		// Force callback invocation
-		}, ignored );
-	}
-} );
-
-jQuery.each( {
-	appendTo: "append",
-	prependTo: "prepend",
-	insertBefore: "before",
-	insertAfter: "after",
-	replaceAll: "replaceWith"
-}, function( name, original ) {
-	jQuery.fn[ name ] = function( selector ) {
-		var elems,
-			ret = [],
-			insert = jQuery( selector ),
-			last = insert.length - 1,
-			i = 0;
-
-		for ( ; i <= last; i++ ) {
-			elems = i === last ? this : this.clone( true );
-			jQuery( insert[ i ] )[ original ]( elems );
-
-			// Support: Android<4.1, PhantomJS<2
-			// .get() because push.apply(_, arraylike) throws on ancient WebKit
-			push.apply( ret, elems.get() );
-		}
-
-		return this.pushStack( ret );
-	};
-} );
-var rmargin = ( /^margin/ );
-
-var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
-
-var getStyles = function( elem ) {
-
-		// Support: IE<=11+, Firefox<=30+ (#15098, #14150)
-		// IE throws on elements created in popups
-		// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
-		var view = elem.ownerDocument.defaultView;
-
-		if ( !view.opener ) {
-			view = window;
-		}
-
-		return view.getComputedStyle( elem );
-	};
-
-var documentElement = document.documentElement;
-
-
-
-( function() {
-	var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal,
-		container = document.createElement( "div" ),
-		div = document.createElement( "div" );
-
-	// Finish early in limited (non-browser) environments
-	if ( !div.style ) {
-		return;
-	}
-
-	// Support: IE9-11+
-	// Style of cloned element affects source element cloned (#8908)
-	div.style.backgroundClip = "content-box";
-	div.cloneNode( true ).style.backgroundClip = "";
-	support.clearCloneStyle = div.style.backgroundClip === "content-box";
-
-	container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" +
-		"padding:0;margin-top:1px;position:absolute";
-	container.appendChild( div );
-
-	// Executing both pixelPosition & boxSizingReliable tests require only one layout
-	// so they're executed at the same time to save the second computation.
-	function computeStyleTests() {
-		div.style.cssText =
-			"box-sizing:border-box;" +
-			"position:relative;display:block;" +
-			"margin:auto;border:1px;padding:1px;" +
-			"top:1%;width:50%";
-		div.innerHTML = "";
-		documentElement.appendChild( container );
-
-		var divStyle = window.getComputedStyle( div );
-		pixelPositionVal = divStyle.top !== "1%";
-		reliableMarginLeftVal = divStyle.marginLeft === "2px";
-		boxSizingReliableVal = divStyle.width === "4px";
-
-		// Support: Android 4.0 - 4.3 only
-		// Some styles come back with percentage values, even though they shouldn't
-		div.style.marginRight = "50%";
-		pixelMarginRightVal = divStyle.marginRight === "4px";
-
-		documentElement.removeChild( container );
-	}
-
-	jQuery.extend( support, {
-		pixelPosition: function() {
-
-			// This test is executed only once but we still do memoizing
-			// since we can use the boxSizingReliable pre-computing.
-			// No need to check if the test was already performed, though.
-			computeStyleTests();
-			return pixelPositionVal;
-		},
-		boxSizingReliable: function() {
-			if ( boxSizingReliableVal == null ) {
-				computeStyleTests();
-			}
-			return boxSizingReliableVal;
-		},
-		pixelMarginRight: function() {
-
-			// Support: Android 4.0-4.3
-			// We're checking for boxSizingReliableVal here instead of pixelMarginRightVal
-			// since that compresses better and they're computed together anyway.
-			if ( boxSizingReliableVal == null ) {
-				computeStyleTests();
-			}
-			return pixelMarginRightVal;
-		},
-		reliableMarginLeft: function() {
-
-			// Support: IE <=8 only, Android 4.0 - 4.3 only, Firefox <=3 - 37
-			if ( boxSizingReliableVal == null ) {
-				computeStyleTests();
-			}
-			return reliableMarginLeftVal;
-		}
-	} );
-} )();
-
-
-function curCSS( elem, name, computed ) {
-	var width, minWidth, maxWidth, ret,
-		style = elem.style;
-
-	computed = computed || getStyles( elem );
-
-	// Support: IE9
-	// getPropertyValue is only needed for .css('filter') (#12537)
-	if ( computed ) {
-		ret = computed.getPropertyValue( name ) || computed[ name ];
-
-		if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
-			ret = jQuery.style( elem, name );
-		}
-
-		// A tribute to the "awesome hack by Dean Edwards"
-		// Android Browser returns percentage for some values,
-		// but width seems to be reliably pixels.
-		// This is against the CSSOM draft spec:
-		// http://dev.w3.org/csswg/cssom/#resolved-values
-		if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) {
-
-			// Remember the original values
-			width = style.width;
-			minWidth = style.minWidth;
-			maxWidth = style.maxWidth;
-
-			// Put in the new values to get a computed value out
-			style.minWidth = style.maxWidth = style.width = ret;
-			ret = computed.width;
-
-			// Revert the changed values
-			style.width = width;
-			style.minWidth = minWidth;
-			style.maxWidth = maxWidth;
-		}
-	}
-
-	return ret !== undefined ?
-
-		// Support: IE9-11+
-		// IE returns zIndex value as an integer.
-		ret + "" :
-		ret;
-}
-
-
-function addGetHookIf( conditionFn, hookFn ) {
-
-	// Define the hook, we'll check on the first run if it's really needed.
-	return {
-		get: function() {
-			if ( conditionFn() ) {
-
-				// Hook not needed (or it's not possible to use it due
-				// to missing dependency), remove it.
-				delete this.get;
-				return;
-			}
-
-			// Hook needed; redefine it so that the support test is not executed again.
-			return ( this.get = hookFn ).apply( this, arguments );
-		}
-	};
-}
-
-
-var
-
-	// Swappable if display is none or starts with table
-	// except "table", "table-cell", or "table-caption"
-	// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
-	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
-	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
-	cssNormalTransform = {
-		letterSpacing: "0",
-		fontWeight: "400"
-	},
-
-	cssPrefixes = [ "Webkit", "Moz", "ms" ],
-	emptyStyle = document.createElement( "div" ).style;
-
-// Return a css property mapped to a potentially vendor prefixed property
-function vendorPropName( name ) {
-
-	// Shortcut for names that are not vendor prefixed
-	if ( name in emptyStyle ) {
-		return name;
-	}
-
-	// Check for vendor prefixed names
-	var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
-		i = cssPrefixes.length;
-
-	while ( i-- ) {
-		name = cssPrefixes[ i ] + capName;
-		if ( name in emptyStyle ) {
-			return name;
-		}
-	}
-}
-
-function setPositiveNumber( elem, value, subtract ) {
-
-	// Any relative (+/-) values have already been
-	// normalized at this point
-	var matches = rcssNum.exec( value );
-	return matches ?
-
-		// Guard against undefined "subtract", e.g., when used as in cssHooks
-		Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
-		value;
-}
-
-function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
-	var i = extra === ( isBorderBox ? "border" : "content" ) ?
-
-		// If we already have the right measurement, avoid augmentation
-		4 :
-
-		// Otherwise initialize for horizontal or vertical properties
-		name === "width" ? 1 : 0,
-
-		val = 0;
-
-	for ( ; i < 4; i += 2 ) {
-
-		// Both box models exclude margin, so add it if we want it
-		if ( extra === "margin" ) {
-			val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
-		}
-
-		if ( isBorderBox ) {
-
-			// border-box includes padding, so remove it if we want content
-			if ( extra === "content" ) {
-				val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
-			}
-
-			// At this point, extra isn't border nor margin, so remove border
-			if ( extra !== "margin" ) {
-				val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
-			}
-		} else {
-
-			// At this point, extra isn't content, so add padding
-			val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
-
-			// At this point, extra isn't content nor padding, so add border
-			if ( extra !== "padding" ) {
-				val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
-			}
-		}
-	}
-
-	return val;
-}
-
-function getWidthOrHeight( elem, name, extra ) {
-
-	// Start with offset property, which is equivalent to the border-box value
-	var val,
-		valueIsBorderBox = true,
-		styles = getStyles( elem ),
-		isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
-
-	// Support: IE <= 11 only
-	// Running getBoundingClientRect on a disconnected node
-	// in IE throws an error.
-	if ( elem.getClientRects().length ) {
-		val = elem.getBoundingClientRect()[ name ];
-	}
-
-	// Support: IE11 only
-	// In IE 11 fullscreen elements inside of an iframe have
-	// 100x too small dimensions (gh-1764).
-	if ( document.msFullscreenElement && window.top !== window ) {
-		val *= 100;
-	}
-
-	// Some non-html elements return undefined for offsetWidth, so check for null/undefined
-	// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
-	// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
-	if ( val <= 0 || val == null ) {
-
-		// Fall back to computed then uncomputed css if necessary
-		val = curCSS( elem, name, styles );
-		if ( val < 0 || val == null ) {
-			val = elem.style[ name ];
-		}
-
-		// Computed unit is not pixels. Stop here and return.
-		if ( rnumnonpx.test( val ) ) {
-			return val;
-		}
-
-		// Check for style in case a browser which returns unreliable values
-		// for getComputedStyle silently falls back to the reliable elem.style
-		valueIsBorderBox = isBorderBox &&
-			( support.boxSizingReliable() || val === elem.style[ name ] );
-
-		// Normalize "", auto, and prepare for extra
-		val = parseFloat( val ) || 0;
-	}
-
-	// Use the active box-sizing model to add/subtract irrelevant styles
-	return ( val +
-		augmentWidthOrHeight(
-			elem,
-			name,
-			extra || ( isBorderBox ? "border" : "content" ),
-			valueIsBorderBox,
-			styles
-		)
-	) + "px";
-}
-
-jQuery.extend( {
-
-	// Add in style property hooks for overriding the default
-	// behavior of getting and setting a style property
-	cssHooks: {
-		opacity: {
-			get: function( elem, computed ) {
-				if ( computed ) {
-
-					// We should always get a number back from opacity
-					var ret = curCSS( elem, "opacity" );
-					return ret === "" ? "1" : ret;
-				}
-			}
-		}
-	},
-
-	// Don't automatically add "px" to these possibly-unitless properties
-	cssNumber: {
-		"animationIterationCount": true,
-		"columnCount": true,
-		"fillOpacity": true,
-		"flexGrow": true,
-		"flexShrink": true,
-		"fontWeight": true,
-		"lineHeight": true,
-		"opacity": true,
-		"order": true,
-		"orphans": true,
-		"widows": true,
-		"zIndex": true,
-		"zoom": true
-	},
-
-	// Add in properties whose names you wish to fix before
-	// setting or getting the value
-	cssProps: {
-		"float": "cssFloat"
-	},
-
-	// Get and set the style property on a DOM Node
-	style: function( elem, name, value, extra ) {
-
-		// Don't set styles on text and comment nodes
-		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
-			return;
-		}
-
-		// Make sure that we're working with the right name
-		var ret, type, hooks,
-			origName = jQuery.camelCase( name ),
-			style = elem.style;
-
-		name = jQuery.cssProps[ origName ] ||
-			( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
-
-		// Gets hook for the prefixed version, then unprefixed version
-		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
-
-		// Check if we're setting a value
-		if ( value !== undefined ) {
-			type = typeof value;
-
-			// Convert "+=" or "-=" to relative numbers (#7345)
-			if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
-				value = adjustCSS( elem, name, ret );
-
-				// Fixes bug #9237
-				type = "number";
-			}
-
-			// Make sure that null and NaN values aren't set (#7116)
-			if ( value == null || value !== value ) {
-				return;
-			}
-
-			// If a number was passed in, add the unit (except for certain CSS properties)
-			if ( type === "number" ) {
-				value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
-			}
-
-			// Support: IE9-11+
-			// background-* props affect original clone's values
-			if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
-				style[ name ] = "inherit";
-			}
-
-			// If a hook was provided, use that value, otherwise just set the specified value
-			if ( !hooks || !( "set" in hooks ) ||
-				( value = hooks.set( elem, value, extra ) ) !== undefined ) {
-
-				style[ name ] = value;
-			}
-
-		} else {
-
-			// If a hook was provided get the non-computed value from there
-			if ( hooks && "get" in hooks &&
-				( ret = hooks.get( elem, false, extra ) ) !== undefined ) {
-
-				return ret;
-			}
-
-			// Otherwise just get the value from the style object
-			return style[ name ];
-		}
-	},
-
-	css: function( elem, name, extra, styles ) {
-		var val, num, hooks,
-			origName = jQuery.camelCase( name );
-
-		// Make sure that we're working with the right name
-		name = jQuery.cssProps[ origName ] ||
-			( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
-
-		// Try prefixed name followed by the unprefixed name
-		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
-
-		// If a hook was provided get the computed value from there
-		if ( hooks && "get" in hooks ) {
-			val = hooks.get( elem, true, extra );
-		}
-
-		// Otherwise, if a way to get the computed value exists, use that
-		if ( val === undefined ) {
-			val = curCSS( elem, name, styles );
-		}
-
-		// Convert "normal" to computed value
-		if ( val === "normal" && name in cssNormalTransform ) {
-			val = cssNormalTransform[ name ];
-		}
-
-		// Make numeric if forced or a qualifier was provided and val looks numeric
-		if ( extra === "" || extra ) {
-			num = parseFloat( val );
-			return extra === true || isFinite( num ) ? num || 0 : val;
-		}
-		return val;
-	}
-} );
-
-jQuery.each( [ "height", "width" ], function( i, name ) {
-	jQuery.cssHooks[ name ] = {
-		get: function( elem, computed, extra ) {
-			if ( computed ) {
-
-				// Certain elements can have dimension info if we invisibly show them
-				// but it must have a current display style that would benefit
-				return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
-
-					// Support: Safari 8+
-					// Table columns in Safari have non-zero offsetWidth & zero
-					// getBoundingClientRect().width unless display is changed.
-					// Support: IE <= 11 only
-					// Running getBoundingClientRect on a disconnected node
-					// in IE throws an error.
-					( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
-						swap( elem, cssShow, function() {
-							return getWidthOrHeight( elem, name, extra );
-						} ) :
-						getWidthOrHeight( elem, name, extra );
-			}
-		},
-
-		set: function( elem, value, extra ) {
-			var matches,
-				styles = extra && getStyles( elem ),
-				subtract = extra && augmentWidthOrHeight(
-					elem,
-					name,
-					extra,
-					jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
-					styles
-				);
-
-			// Convert to pixels if value adjustment is needed
-			if ( subtract && ( matches = rcssNum.exec( value ) ) &&
-				( matches[ 3 ] || "px" ) !== "px" ) {
-
-				elem.style[ name ] = value;
-				value = jQuery.css( elem, name );
-			}
-
-			return setPositiveNumber( elem, value, subtract );
-		}
-	};
-} );
-
-jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
-	function( elem, computed ) {
-		if ( computed ) {
-			return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
-				elem.getBoundingClientRect().left -
-					swap( elem, { marginLeft: 0 }, function() {
-						return elem.getBoundingClientRect().left;
-					} )
-				) + "px";
-		}
-	}
-);
-
-// These hooks are used by animate to expand properties
-jQuery.each( {
-	margin: "",
-	padding: "",
-	border: "Width"
-}, function( prefix, suffix ) {
-	jQuery.cssHooks[ prefix + suffix ] = {
-		expand: function( value ) {
-			var i = 0,
-				expanded = {},
-
-				// Assumes a single number if not a string
-				parts = typeof value === "string" ? value.split( " " ) : [ value ];
-
-			for ( ; i < 4; i++ ) {
-				expanded[ prefix + cssExpand[ i ] + suffix ] =
-					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
-			}
-
-			return expanded;
-		}
-	};
-
-	if ( !rmargin.test( prefix ) ) {
-		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
-	}
-} );
-
-jQuery.fn.extend( {
-	css: function( name, value ) {
-		return access( this, function( elem, name, value ) {
-			var styles, len,
-				map = {},
-				i = 0;
-
-			if ( jQuery.isArray( name ) ) {
-				styles = getStyles( elem );
-				len = name.length;
-
-				for ( ; i < len; i++ ) {
-					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
-				}
-
-				return map;
-			}
-
-			return value !== undefined ?
-				jQuery.style( elem, name, value ) :
-				jQuery.css( elem, name );
-		}, name, value, arguments.length > 1 );
-	}
-} );
-
-
-// Based off of the plugin by Clint Helfers, with permission.
-// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
-jQuery.fn.delay = function( time, type ) {
-	time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
-	type = type || "fx";
-
-	return this.queue( type, function( next, hooks ) {
-		var timeout = window.setTimeout( next, time );
-		hooks.stop = function() {
-			window.clearTimeout( timeout );
-		};
-	} );
-};
-
-
-( function() {
-	var input = document.createElement( "input" ),
-		select = document.createElement( "select" ),
-		opt = select.appendChild( document.createElement( "option" ) );
-
-	input.type = "checkbox";
-
-	// Support: Android<4.4
-	// Default value for a checkbox should be "on"
-	support.checkOn = input.value !== "";
-
-	// Support: IE<=11+
-	// Must access selectedIndex to make default options select
-	support.optSelected = opt.selected;
-
-	// Support: IE<=11+
-	// An input loses its value after becoming a radio
-	input = document.createElement( "input" );
-	input.value = "t";
-	input.type = "radio";
-	support.radioValue = input.value === "t";
-} )();
-
-
-var boolHook,
-	attrHandle = jQuery.expr.attrHandle,
-
-	// Exclusively lowercase A-Z in attribute names (gh-2730)
-	// https://dom.spec.whatwg.org/#converted-to-ascii-lowercase
-	raz = /[A-Z]+/g,
-	lowercase = function( ch ) {
-		return ch.toLowerCase();
-	};
-
-jQuery.fn.extend( {
-	attr: function( name, value ) {
-		return access( this, jQuery.attr, name, value, arguments.length > 1 );
-	},
-
-	removeAttr: function( name ) {
-		return this.each( function() {
-			jQuery.removeAttr( this, name );
-		} );
-	}
-} );
-
-jQuery.extend( {
-	attr: function( elem, name, value ) {
-		var ret, hooks,
-			nType = elem.nodeType;
-
-		// Don't get/set attributes on text, comment and attribute nodes
-		if ( nType === 3 || nType === 8 || nType === 2 ) {
-			return;
-		}
-
-		// Fallback to prop when attributes are not supported
-		if ( typeof elem.getAttribute === "undefined" ) {
-			return jQuery.prop( elem, name, value );
-		}
-
-		// All attributes are lowercase
-		// Grab necessary hook if one is defined
-		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
-			name = name.replace( raz, lowercase );
-			hooks = jQuery.attrHooks[ name ] ||
-				( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
-		}
-
-		if ( value !== undefined ) {
-			if ( value === null ) {
-				jQuery.removeAttr( elem, name );
-				return;
-			}
-
-			if ( hooks && "set" in hooks &&
-				( ret = hooks.set( elem, value, name ) ) !== undefined ) {
-				return ret;
-			}
-
-			elem.setAttribute( name, value + "" );
-			return value;
-		}
-
-		if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
-			return ret;
-		}
-
-		ret = jQuery.find.attr( elem, name );
-
-		// Non-existent attributes return null, we normalize to undefined
-		return ret == null ? undefined : ret;
-	},
-
-	attrHooks: {
-		type: {
-			set: function( elem, value ) {
-				if ( !support.radioValue && value === "radio" &&
-					jQuery.nodeName( elem, "input" ) ) {
-					var val = elem.value;
-					elem.setAttribute( "type", value );
-					if ( val ) {
-						elem.value = val;
-					}
-					return value;
-				}
-			}
-		}
-	},
-
-	removeAttr: function( elem, value ) {
-		var name,
-			i = 0,
-			attrNames = value && value.match( rnotwhite );
-
-		if ( attrNames && elem.nodeType === 1 ) {
-			while ( ( name = attrNames[ i++ ] ) ) {
-				elem.removeAttribute( name );
-			}
-		}
-	}
-} );
-
-// Hooks for boolean attributes
-boolHook = {
-	set: function( elem, value, name ) {
-		if ( value === false ) {
-
-			// Remove boolean attributes when set to false
-			jQuery.removeAttr( elem, name );
-		} else {
-			elem.setAttribute( name, name );
-		}
-		return name;
-	}
-};
-
-jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
-	var getter = attrHandle[ name ] || jQuery.find.attr;
-
-	attrHandle[ name ] = function( elem, name, isXML ) {
-		var ret, handle;
-		if ( !isXML ) {
-
-			// Avoid an infinite loop by temporarily removing this function from the getter
-			handle = attrHandle[ name ];
-			attrHandle[ name ] = ret;
-			ret = getter( elem, name, isXML ) != null ?
-				name.toLowerCase() :
-				null;
-			attrHandle[ name ] = handle;
-		}
-		return ret;
-	};
-} );
-
-
-
-
-var rfocusable = /^(?:input|select|textarea|button)$/i,
-	rclickable = /^(?:a|area)$/i;
-
-jQuery.fn.extend( {
-	prop: function( name, value ) {
-		return access( this, jQuery.prop, name, value, arguments.length > 1 );
-	},
-
-	removeProp: function( name ) {
-		return this.each( function() {
-			delete this[ jQuery.propFix[ name ] || name ];
-		} );
-	}
-} );
-
-jQuery.extend( {
-	prop: function( elem, name, value ) {
-		var ret, hooks,
-			nType = elem.nodeType;
-
-		// Don't get/set properties on text, comment and attribute nodes
-		if ( nType === 3 || nType === 8 || nType === 2 ) {
-			return;
-		}
-
-		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
-
-			// Fix name and attach hooks
-			name = jQuery.propFix[ name ] || name;
-			hooks = jQuery.propHooks[ name ];
-		}
-
-		if ( value !== undefined ) {
-			if ( hooks && "set" in hooks &&
-				( ret = hooks.set( elem, value, name ) ) !== undefined ) {
-				return ret;
-			}
-
-			return ( elem[ name ] = value );
-		}
-
-		if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
-			return ret;
-		}
-
-		return elem[ name ];
-	},
-
-	propHooks: {
-		tabIndex: {
-			get: function( elem ) {
-
-				// elem.tabIndex doesn't always return the
-				// correct value when it hasn't been explicitly set
-				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
-				// Use proper attribute retrieval(#12072)
-				var tabindex = jQuery.find.attr( elem, "tabindex" );
-
-				return tabindex ?
-					parseInt( tabindex, 10 ) :
-					rfocusable.test( elem.nodeName ) ||
-						rclickable.test( elem.nodeName ) && elem.href ?
-							0 :
-							-1;
-			}
-		}
-	},
-
-	propFix: {
-		"for": "htmlFor",
-		"class": "className"
-	}
-} );
-
-if ( !support.optSelected ) {
-	jQuery.propHooks.selected = {
-		get: function( elem ) {
-			var parent = elem.parentNode;
-			if ( parent && parent.parentNode ) {
-				parent.parentNode.selectedIndex;
-			}
-			return null;
-		}
-	};
-}
-
-jQuery.each( [
-	"tabIndex",
-	"readOnly",
-	"maxLength",
-	"cellSpacing",
-	"cellPadding",
-	"rowSpan",
-	"colSpan",
-	"useMap",
-	"frameBorder",
-	"contentEditable"
-], function() {
-	jQuery.propFix[ this.toLowerCase() ] = this;
-} );
-
-
-
-
-var rclass = /[\t\r\n\f]/g;
-
-function getClass( elem ) {
-	return elem.getAttribute && elem.getAttribute( "class" ) || "";
-}
-
-jQuery.fn.extend( {
-	addClass: function( value ) {
-		var classes, elem, cur, curValue, clazz, j, finalValue,
-			i = 0;
-
-		if ( jQuery.isFunction( value ) ) {
-			return this.each( function( j ) {
-				jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
-			} );
-		}
-
-		if ( typeof value === "string" && value ) {
-			classes = value.match( rnotwhite ) || [];
-
-			while ( ( elem = this[ i++ ] ) ) {
-				curValue = getClass( elem );
-				cur = elem.nodeType === 1 &&
-					( " " + curValue + " " ).replace( rclass, " " );
-
-				if ( cur ) {
-					j = 0;
-					while ( ( clazz = classes[ j++ ] ) ) {
-						if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
-							cur += clazz + " ";
-						}
-					}
-
-					// Only assign if different to avoid unneeded rendering.
-					finalValue = jQuery.trim( cur );
-					if ( curValue !== finalValue ) {
-						elem.setAttribute( "class", finalValue );
-					}
-				}
-			}
-		}
-
-		return this;
-	},
-
-	removeClass: function( value ) {
-		var classes, elem, cur, curValue, clazz, j, finalValue,
-			i = 0;
-
-		if ( jQuery.isFunction( value ) ) {
-			return this.each( function( j ) {
-				jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
-			} );
-		}
-
-		if ( !arguments.length ) {
-			return this.attr( "class", "" );
-		}
-
-		if ( typeof value === "string" && value ) {
-			classes = value.match( rnotwhite ) || [];
-
-			while ( ( elem = this[ i++ ] ) ) {
-				curValue = getClass( elem );
-
-				// This expression is here for better compressibility (see addClass)
-				cur = elem.nodeType === 1 &&
-					( " " + curValue + " " ).replace( rclass, " " );
-
-				if ( cur ) {
-					j = 0;
-					while ( ( clazz = classes[ j++ ] ) ) {
-
-						// Remove *all* instances
-						while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
-							cur = cur.replace( " " + clazz + " ", " " );
-						}
-					}
-
-					// Only assign if different to avoid unneeded rendering.
-					finalValue = jQuery.trim( cur );
-					if ( curValue !== finalValue ) {
-						elem.setAttribute( "class", finalValue );
-					}
-				}
-			}
-		}
-
-		return this;
-	},
-
-	toggleClass: function( value, stateVal ) {
-		var type = typeof value;
-
-		if ( typeof stateVal === "boolean" && type === "string" ) {
-			return stateVal ? this.addClass( value ) : this.removeClass( value );
-		}
-
-		if ( jQuery.isFunction( value ) ) {
-			return this.each( function( i ) {
-				jQuery( this ).toggleClass(
-					value.call( this, i, getClass( this ), stateVal ),
-					stateVal
-				);
-			} );
-		}
-
-		return this.each( function() {
-			var className, i, self, classNames;
-
-			if ( type === "string" ) {
-
-				// Toggle individual class names
-				i = 0;
-				self = jQuery( this );
-				classNames = value.match( rnotwhite ) || [];
-
-				while ( ( className = classNames[ i++ ] ) ) {
-
-					// Check each className given, space separated list
-					if ( self.hasClass( className ) ) {
-						self.removeClass( className );
-					} else {
-						self.addClass( className );
-					}
-				}
-
-			// Toggle whole class name
-			} else if ( value === undefined || type === "boolean" ) {
-				className = getClass( this );
-				if ( className ) {
-
-					// Store className if set
-					dataPriv.set( this, "__className__", className );
-				}
-
-				// If the element has a class name or if we're passed `false`,
-				// then remove the whole classname (if there was one, the above saved it).
-				// Otherwise bring back whatever was previously saved (if anything),
-				// falling back to the empty string if nothing was stored.
-				if ( this.setAttribute ) {
-					this.setAttribute( "class",
-						className || value === false ?
-						"" :
-						dataPriv.get( this, "__className__" ) || ""
-					);
-				}
-			}
-		} );
-	},
-
-	hasClass: function( selector ) {
-		var className, elem,
-			i = 0;
-
-		className = " " + selector + " ";
-		while ( ( elem = this[ i++ ] ) ) {
-			if ( elem.nodeType === 1 &&
-				( " " + getClass( elem ) + " " ).replace( rclass, " " )
-					.indexOf( className ) > -1
-			) {
-				return true;
-			}
-		}
-
-		return false;
-	}
-} );
-
-
-
-
-var rreturn = /\r/g;
-
-jQuery.fn.extend( {
-	val: function( value ) {
-		var hooks, ret, isFunction,
-			elem = this[ 0 ];
-
-		if ( !arguments.length ) {
-			if ( elem ) {
-				hooks = jQuery.valHooks[ elem.type ] ||
-					jQuery.valHooks[ elem.nodeName.toLowerCase() ];
-
-				if ( hooks &&
-					"get" in hooks &&
-					( ret = hooks.get( elem, "value" ) ) !== undefined
-				) {
-					return ret;
-				}
-
-				ret = elem.value;
-
-				return typeof ret === "string" ?
-
-					// Handle most common string cases
-					ret.replace( rreturn, "" ) :
-
-					// Handle cases where value is null/undef or number
-					ret == null ? "" : ret;
-			}
-
-			return;
-		}
-
-		isFunction = jQuery.isFunction( value );
-
-		return this.each( function( i ) {
-			var val;
-
-			if ( this.nodeType !== 1 ) {
-				return;
-			}
-
-			if ( isFunction ) {
-				val = value.call( this, i, jQuery( this ).val() );
-			} else {
-				val = value;
-			}
-
-			// Treat null/undefined as ""; convert numbers to string
-			if ( val == null ) {
-				val = "";
-
-			} else if ( typeof val === "number" ) {
-				val += "";
-
-			} else if ( jQuery.isArray( val ) ) {
-				val = jQuery.map( val, function( value ) {
-					return value == null ? "" : value + "";
-				} );
-			}
-
-			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
-
-			// If set returns undefined, fall back to normal setting
-			if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) {
-				this.value = val;
-			}
-		} );
-	}
-} );
-
-jQuery.extend( {
-	valHooks: {
-		option: {
-			get: function( elem ) {
-
-				// Support: IE<11
-				// option.value not trimmed (#14858)
-				return jQuery.trim( elem.value );
-			}
-		},
-		select: {
-			get: function( elem ) {
-				var value, option,
-					options = elem.options,
-					index = elem.selectedIndex,
-					one = elem.type === "select-one",
-					values = one ? null : [],
-					max = one ? index + 1 : options.length,
-					i = index < 0 ?
-						max :
-						one ? index : 0;
-
-				// Loop through all the selected options
-				for ( ; i < max; i++ ) {
-					option = options[ i ];
-
-					// IE8-9 doesn't update selected after form reset (#2551)
-					if ( ( option.selected || i === index ) &&
-
-							// Don't return options that are disabled or in a disabled optgroup
-							!option.disabled &&
-							( !option.parentNode.disabled ||
-								!jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
-
-						// Get the specific value for the option
-						value = jQuery( option ).val();
-
-						// We don't need an array for one selects
-						if ( one ) {
-							return value;
-						}
-
-						// Multi-Selects return an array
-						values.push( value );
-					}
-				}
-
-				return values;
-			},
-
-			set: function( elem, value ) {
-				var optionSet, option,
-					options = elem.options,
-					values = jQuery.makeArray( value ),
-					i = options.length;
-
-				while ( i-- ) {
-					option = options[ i ];
-					if ( option.selected =
-							jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
-					) {
-						optionSet = true;
-					}
-				}
-
-				// Force browsers to behave consistently when non-matching value is set
-				if ( !optionSet ) {
-					elem.selectedIndex = -1;
-				}
-				return values;
-			}
-		}
-	}
-} );
-
-// Radios and checkboxes getter/setter
-jQuery.each( [ "radio", "checkbox" ], function() {
-	jQuery.valHooks[ this ] = {
-		set: function( elem, value ) {
-			if ( jQuery.isArray( value ) ) {
-				return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
-			}
-		}
-	};
-	if ( !support.checkOn ) {
-		jQuery.valHooks[ this ].get = function( elem ) {
-			return elem.getAttribute( "value" ) === null ? "on" : elem.value;
-		};
-	}
-} );
-
-
-
-
-// Return jQuery for attributes-only inclusion
-
-
-var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/;
-
-jQuery.extend( jQuery.event, {
-
-	trigger: function( event, data, elem, onlyHandlers ) {
-
-		var i, cur, tmp, bubbleType, ontype, handle, special,
-			eventPath = [ elem || document ],
-			type = hasOwn.call( event, "type" ) ? event.type : event,
-			namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
-
-		cur = tmp = elem = elem || document;
-
-		// Don't do events on text and comment nodes
-		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
-			return;
-		}
-
-		// focus/blur morphs to focusin/out; ensure we're not firing them right now
-		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
-			return;
-		}
-
-		if ( type.indexOf( "." ) > -1 ) {
-
-			// Namespaced trigger; create a regexp to match event type in handle()
-			namespaces = type.split( "." );
-			type = namespaces.shift();
-			namespaces.sort();
-		}
-		ontype = type.indexOf( ":" ) < 0 && "on" + type;
-
-		// Caller can pass in a jQuery.Event object, Object, or just an event type string
-		event = event[ jQuery.expando ] ?
-			event :
-			new jQuery.Event( type, typeof event === "object" && event );
-
-		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
-		event.isTrigger = onlyHandlers ? 2 : 3;
-		event.namespace = namespaces.join( "." );
-		event.rnamespace = event.namespace ?
-			new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
-			null;
-
-		// Clean up the event in case it is being reused
-		event.result = undefined;
-		if ( !event.target ) {
-			event.target = elem;
-		}
-
-		// Clone any incoming data and prepend the event, creating the handler arg list
-		data = data == null ?
-			[ event ] :
-			jQuery.makeArray( data, [ event ] );
-
-		// Allow special events to draw outside the lines
-		special = jQuery.event.special[ type ] || {};
-		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
-			return;
-		}
-
-		// Determine event propagation path in advance, per W3C events spec (#9951)
-		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
-		if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
-
-			bubbleType = special.delegateType || type;
-			if ( !rfocusMorph.test( bubbleType + type ) ) {
-				cur = cur.parentNode;
-			}
-			for ( ; cur; cur = cur.parentNode ) {
-				eventPath.push( cur );
-				tmp = cur;
-			}
-
-			// Only add window if we got to document (e.g., not plain obj or detached DOM)
-			if ( tmp === ( elem.ownerDocument || document ) ) {
-				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
-			}
-		}
-
-		// Fire handlers on the event path
-		i = 0;
-		while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
-
-			event.type = i > 1 ?
-				bubbleType :
-				special.bindType || type;
-
-			// jQuery handler
-			handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
-				dataPriv.get( cur, "handle" );
-			if ( handle ) {
-				handle.apply( cur, data );
-			}
-
-			// Native handler
-			handle = ontype && cur[ ontype ];
-			if ( handle && handle.apply && acceptData( cur ) ) {
-				event.result = handle.apply( cur, data );
-				if ( event.result === false ) {
-					event.preventDefault();
-				}
-			}
-		}
-		event.type = type;
-
-		// If nobody prevented the default action, do it now
-		if ( !onlyHandlers && !event.isDefaultPrevented() ) {
-
-			if ( ( !special._default ||
-				special._default.apply( eventPath.pop(), data ) === false ) &&
-				acceptData( elem ) ) {
-
-				// Call a native DOM method on the target with the same name as the event.
-				// Don't do default actions on window, that's where global variables be (#6170)
-				if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
-
-					// Don't re-trigger an onFOO event when we call its FOO() method
-					tmp = elem[ ontype ];
-
-					if ( tmp ) {
-						elem[ ontype ] = null;
-					}
-
-					// Prevent re-triggering of the same event, since we already bubbled it above
-					jQuery.event.triggered = type;
-					elem[ type ]();
-					jQuery.event.triggered = undefined;
-
-					if ( tmp ) {
-						elem[ ontype ] = tmp;
-					}
-				}
-			}
-		}
-
-		return event.result;
-	},
-
-	// Piggyback on a donor event to simulate a different one
-	simulate: function( type, elem, event ) {
-		var e = jQuery.extend(
-			new jQuery.Event(),
-			event,
-			{
-				type: type,
-				isSimulated: true
-
-				// Previously, `originalEvent: {}` was set here, so stopPropagation call
-				// would not be triggered on donor event, since in our own
-				// jQuery.event.stopPropagation function we had a check for existence of
-				// originalEvent.stopPropagation method, so, consequently it would be a noop.
-				//
-				// But now, this "simulate" function is used only for events
-				// for which stopPropagation() is noop, so there is no need for that anymore.
-				//
-				// For the compat branch though, guard for "click" and "submit"
-				// events is still used, but was moved to jQuery.event.stopPropagation function
-				// because `originalEvent` should point to the original event for the constancy
-				// with other events and for more focused logic
-			}
-		);
-
-		jQuery.event.trigger( e, null, elem );
-
-		if ( e.isDefaultPrevented() ) {
-			event.preventDefault();
-		}
-	}
-
-} );
-
-jQuery.fn.extend( {
-
-	trigger: function( type, data ) {
-		return this.each( function() {
-			jQuery.event.trigger( type, data, this );
-		} );
-	},
-	triggerHandler: function( type, data ) {
-		var elem = this[ 0 ];
-		if ( elem ) {
-			return jQuery.event.trigger( type, data, elem, true );
-		}
-	}
-} );
-
-
-jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
-	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
-	"change select submit keydown keypress keyup contextmenu" ).split( " " ),
-	function( i, name ) {
-
-	// Handle event binding
-	jQuery.fn[ name ] = function( data, fn ) {
-		return arguments.length > 0 ?
-			this.on( name, null, data, fn ) :
-			this.trigger( name );
-	};
-} );
-
-jQuery.fn.extend( {
-	hover: function( fnOver, fnOut ) {
-		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
-	}
-} );
-
-
-
-
-support.focusin = "onfocusin" in window;
-
-
-// Support: Firefox
-// Firefox doesn't have focus(in | out) events
-// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
-//
-// Support: Chrome, Safari
-// focus(in | out) events fire after focus & blur events,
-// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
-// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857
-if ( !support.focusin ) {
-	jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {
-
-		// Attach a single capturing handler on the document while someone wants focusin/focusout
-		var handler = function( event ) {
-			jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
-		};
-
-		jQuery.event.special[ fix ] = {
-			setup: function() {
-				var doc = this.ownerDocument || this,
-					attaches = dataPriv.access( doc, fix );
-
-				if ( !attaches ) {
-					doc.addEventListener( orig, handler, true );
-				}
-				dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
-			},
-			teardown: function() {
-				var doc = this.ownerDocument || this,
-					attaches = dataPriv.access( doc, fix ) - 1;
-
-				if ( !attaches ) {
-					doc.removeEventListener( orig, handler, true );
-					dataPriv.remove( doc, fix );
-
-				} else {
-					dataPriv.access( doc, fix, attaches );
-				}
-			}
-		};
-	} );
-}
-
-
-jQuery.fn.extend( {
-	wrapAll: function( html ) {
-		var wrap;
-
-		if ( this[ 0 ] ) {
-			if ( jQuery.isFunction( html ) ) {
-				html = html.call( this[ 0 ] );
-			}
-
-			// The elements to wrap the target around
-			wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
-
-			if ( this[ 0 ].parentNode ) {
-				wrap.insertBefore( this[ 0 ] );
-			}
-
-			wrap.map( function() {
-				var elem = this;
-
-				while ( elem.firstElementChild ) {
-					elem = elem.firstElementChild;
-				}
-
-				return elem;
-			} ).append( this );
-		}
-
-		return this;
-	},
-
-	wrapInner: function( html ) {
-		if ( jQuery.isFunction( html ) ) {
-			return this.each( function( i ) {
-				jQuery( this ).wrapInner( html.call( this, i ) );
-			} );
-		}
-
-		return this.each( function() {
-			var self = jQuery( this ),
-				contents = self.contents();
-
-			if ( contents.length ) {
-				contents.wrapAll( html );
-
-			} else {
-				self.append( html );
-			}
-		} );
-	},
-
-	wrap: function( html ) {
-		var isFunction = jQuery.isFunction( html );
-
-		return this.each( function( i ) {
-			jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html );
-		} );
-	},
-
-	unwrap: function( selector ) {
-		this.parent( selector ).not( "body" ).each( function() {
-			jQuery( this ).replaceWith( this.childNodes );
-		} );
-		return this;
-	}
-} );
-
-
-jQuery.expr.filters.hidden = function( elem ) {
-	return !jQuery.expr.filters.visible( elem );
-};
-jQuery.expr.filters.visible = function( elem ) {
-	return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
-};
-
-
-
-
-var
-	rbracket = /\[\]$/,
-	rCRLF = /\r?\n/g,
-	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
-	rsubmittable = /^(?:input|select|textarea|keygen)/i;
-
-function buildParams( prefix, obj, traditional, add ) {
-	var name;
-
-	if ( jQuery.isArray( obj ) ) {
-
-		// Serialize array item.
-		jQuery.each( obj, function( i, v ) {
-			if ( traditional || rbracket.test( prefix ) ) {
-
-				// Treat each array item as a scalar.
-				add( prefix, v );
-
-			} else {
-
-				// Item is non-scalar (array or object), encode its numeric index.
-				buildParams(
-					prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
-					v,
-					traditional,
-					add
-				);
-			}
-		} );
-
-	} else if ( !traditional && jQuery.type( obj ) === "object" ) {
-
-		// Serialize object item.
-		for ( name in obj ) {
-			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
-		}
-
-	} else {
-
-		// Serialize scalar item.
-		add( prefix, obj );
-	}
-}
-
-// Serialize an array of form elements or a set of
-// key/values into a query string
-jQuery.param = function( a, traditional ) {
-	var prefix,
-		s = [],
-		add = function( key, value ) {
-
-			// If value is a function, invoke it and return its value
-			value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
-			s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
-		};
-
-	// Set traditional to true for jQuery <= 1.3.2 behavior.
-	if ( traditional === undefined ) {
-		traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
-	}
-
-	// If an array was passed in, assume that it is an array of form elements.
-	if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
-
-		// Serialize the form elements
-		jQuery.each( a, function() {
-			add( this.name, this.value );
-		} );
-
-	} else {
-
-		// If traditional, encode the "old" way (the way 1.3.2 or older
-		// did it), otherwise encode params recursively.
-		for ( prefix in a ) {
-			buildParams( prefix, a[ prefix ], traditional, add );
-		}
-	}
-
-	// Return the resulting serialization
-	return s.join( "&" );
-};
-
-jQuery.fn.extend( {
-	serialize: function() {
-		return jQuery.param( this.serializeArray() );
-	},
-	serializeArray: function() {
-		return this.map( function() {
-
-			// Can add propHook for "elements" to filter or add form elements
-			var elements = jQuery.prop( this, "elements" );
-			return elements ? jQuery.makeArray( elements ) : this;
-		} )
-		.filter( function() {
-			var type = this.type;
-
-			// Use .is( ":disabled" ) so that fieldset[disabled] works
-			return this.name && !jQuery( this ).is( ":disabled" ) &&
-				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
-				( this.checked || !rcheckableType.test( type ) );
-		} )
-		.map( function( i, elem ) {
-			var val = jQuery( this ).val();
-
-			return val == null ?
-				null :
-				jQuery.isArray( val ) ?
-					jQuery.map( val, function( val ) {
-						return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
-					} ) :
-					{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
-		} ).get();
-	}
-} );
-
-
-// Support: Safari 8+
-// In Safari 8 documents created via document.implementation.createHTMLDocument
-// collapse sibling forms: the second one becomes a child of the first one.
-// Because of that, this security measure has to be disabled in Safari 8.
-// https://bugs.webkit.org/show_bug.cgi?id=137337
-support.createHTMLDocument = ( function() {
-	var body = document.implementation.createHTMLDocument( "" ).body;
-	body.innerHTML = "<form></form><form></form>";
-	return body.childNodes.length === 2;
-} )();
-
-
-// Argument "data" should be string of html
-// context (optional): If specified, the fragment will be created in this context,
-// defaults to document
-// keepScripts (optional): If true, will include scripts passed in the html string
-jQuery.parseHTML = function( data, context, keepScripts ) {
-	if ( typeof data !== "string" ) {
-		return [];
-	}
-	if ( typeof context === "boolean" ) {
-		keepScripts = context;
-		context = false;
-	}
-
-	// Stop scripts or inline event handlers from being executed immediately
-	// by using document.implementation
-	context = context || ( support.createHTMLDocument ?
-		document.implementation.createHTMLDocument( "" ) :
-		document );
-
-	var parsed = rsingleTag.exec( data ),
-		scripts = !keepScripts && [];
-
-	// Single tag
-	if ( parsed ) {
-		return [ context.createElement( parsed[ 1 ] ) ];
-	}
-
-	parsed = buildFragment( [ data ], context, scripts );
-
-	if ( scripts && scripts.length ) {
-		jQuery( scripts ).remove();
-	}
-
-	return jQuery.merge( [], parsed.childNodes );
-};
-
-
-/**
- * Gets a window from an element
- */
-function getWindow( elem ) {
-	return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
-}
-
-jQuery.offset = {
-	setOffset: function( elem, options, i ) {
-		var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
-			position = jQuery.css( elem, "position" ),
-			curElem = jQuery( elem ),
-			props = {};
-
-		// Set position first, in-case top/left are set even on static elem
-		if ( position === "static" ) {
-			elem.style.position = "relative";
-		}
-
-		curOffset = curElem.offset();
-		curCSSTop = jQuery.css( elem, "top" );
-		curCSSLeft = jQuery.css( elem, "left" );
-		calculatePosition = ( position === "absolute" || position === "fixed" ) &&
-			( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
-
-		// Need to be able to calculate position if either
-		// top or left is auto and position is either absolute or fixed
-		if ( calculatePosition ) {
-			curPosition = curElem.position();
-			curTop = curPosition.top;
-			curLeft = curPosition.left;
-
-		} else {
-			curTop = parseFloat( curCSSTop ) || 0;
-			curLeft = parseFloat( curCSSLeft ) || 0;
-		}
-
-		if ( jQuery.isFunction( options ) ) {
-
-			// Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
-			options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
-		}
-
-		if ( options.top != null ) {
-			props.top = ( options.top - curOffset.top ) + curTop;
-		}
-		if ( options.left != null ) {
-			props.left = ( options.left - curOffset.left ) + curLeft;
-		}
-
-		if ( "using" in options ) {
-			options.using.call( elem, props );
-
-		} else {
-			curElem.css( props );
-		}
-	}
-};
-
-jQuery.fn.extend( {
-	offset: function( options ) {
-
-		// Preserve chaining for setter
-		if ( arguments.length ) {
-			return options === undefined ?
-				this :
-				this.each( function( i ) {
-					jQuery.offset.setOffset( this, options, i );
-				} );
-		}
-
-		var docElem, win, rect, doc,
-			elem = this[ 0 ];
-
-		if ( !elem ) {
-			return;
-		}
-
-		// Support: IE<=11+
-		// Running getBoundingClientRect on a
-		// disconnected node in IE throws an error
-		if ( !elem.getClientRects().length ) {
-			return { top: 0, left: 0 };
-		}
-
-		rect = elem.getBoundingClientRect();
-
-		// Make sure element is not hidden (display: none)
-		if ( rect.width || rect.height ) {
-			doc = elem.ownerDocument;
-			win = getWindow( doc );
-			docElem = doc.documentElement;
-
-			return {
-				top: rect.top + win.pageYOffset - docElem.clientTop,
-				left: rect.left + win.pageXOffset - docElem.clientLeft
-			};
-		}
-
-		// Return zeros for disconnected and hidden elements (gh-2310)
-		return rect;
-	},
-
-	position: function() {
-		if ( !this[ 0 ] ) {
-			return;
-		}
-
-		var offsetParent, offset,
-			elem = this[ 0 ],
-			parentOffset = { top: 0, left: 0 };
-
-		// Fixed elements are offset from window (parentOffset = {top:0, left: 0},
-		// because it is its only offset parent
-		if ( jQuery.css( elem, "position" ) === "fixed" ) {
-
-			// Assume getBoundingClientRect is there when computed position is fixed
-			offset = elem.getBoundingClientRect();
-
-		} else {
-
-			// Get *real* offsetParent
-			offsetParent = this.offsetParent();
-
-			// Get correct offsets
-			offset = this.offset();
-			if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
-				parentOffset = offsetParent.offset();
-			}
-
-			// Add offsetParent borders
-			// Subtract offsetParent scroll positions
-			parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ) -
-				offsetParent.scrollTop();
-			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true ) -
-				offsetParent.scrollLeft();
-		}
-
-		// Subtract parent offsets and element margins
-		return {
-			top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
-			left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
-		};
-	},
-
-	// This method will return documentElement in the following cases:
-	// 1) For the element inside the iframe without offsetParent, this method will return
-	//    documentElement of the parent window
-	// 2) For the hidden or detached element
-	// 3) For body or html element, i.e. in case of the html node - it will return itself
-	//
-	// but those exceptions were never presented as a real life use-cases
-	// and might be considered as more preferable results.
-	//
-	// This logic, however, is not guaranteed and can change at any point in the future
-	offsetParent: function() {
-		return this.map( function() {
-			var offsetParent = this.offsetParent;
-
-			while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
-				offsetParent = offsetParent.offsetParent;
-			}
-
-			return offsetParent || documentElement;
-		} );
-	}
-} );
-
-// Create scrollLeft and scrollTop methods
-jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
-	var top = "pageYOffset" === prop;
-
-	jQuery.fn[ method ] = function( val ) {
-		return access( this, function( elem, method, val ) {
-			var win = getWindow( elem );
-
-			if ( val === undefined ) {
-				return win ? win[ prop ] : elem[ method ];
-			}
-
-			if ( win ) {
-				win.scrollTo(
-					!top ? val : win.pageXOffset,
-					top ? val : win.pageYOffset
-				);
-
-			} else {
-				elem[ method ] = val;
-			}
-		}, method, val, arguments.length );
-	};
-} );
-
-// Support: Safari<7-8+, Chrome<37-44+
-// Add the top/left cssHooks using jQuery.fn.position
-// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
-// Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280
-// getComputedStyle returns percent when specified for top/left/bottom/right;
-// rather than make the css module depend on the offset module, just check for it here
-jQuery.each( [ "top", "left" ], function( i, prop ) {
-	jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
-		function( elem, computed ) {
-			if ( computed ) {
-				computed = curCSS( elem, prop );
-
-				// If curCSS returns percentage, fallback to offset
-				return rnumnonpx.test( computed ) ?
-					jQuery( elem ).position()[ prop ] + "px" :
-					computed;
-			}
-		}
-	);
-} );
-
-
-// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
-jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
-	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name },
-		function( defaultExtra, funcName ) {
-
-		// Margin is only for outerHeight, outerWidth
-		jQuery.fn[ funcName ] = function( margin, value ) {
-			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
-				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
-
-			return access( this, function( elem, type, value ) {
-				var doc;
-
-				if ( jQuery.isWindow( elem ) ) {
-
-					// $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
-					return funcName.indexOf( "outer" ) === 0 ?
-						elem[ "inner" + name ] :
-						elem.document.documentElement[ "client" + name ];
-				}
-
-				// Get document width or height
-				if ( elem.nodeType === 9 ) {
-					doc = elem.documentElement;
-
-					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
-					// whichever is greatest
-					return Math.max(
-						elem.body[ "scroll" + name ], doc[ "scroll" + name ],
-						elem.body[ "offset" + name ], doc[ "offset" + name ],
-						doc[ "client" + name ]
-					);
-				}
-
-				return value === undefined ?
-
-					// Get width or height on the element, requesting but not forcing parseFloat
-					jQuery.css( elem, type, extra ) :
-
-					// Set width or height on the element
-					jQuery.style( elem, type, value, extra );
-			}, type, chainable ? margin : undefined, chainable );
-		};
-	} );
-} );
-
-
-// Register as a named AMD module, since jQuery can be concatenated with other
-// files that may use define, but not via a proper concatenation script that
-// understands anonymous AMD modules. A named AMD is safest and most robust
-// way to register. Lowercase jquery is used because AMD module names are
-// derived from file names, and jQuery is normally delivered in a lowercase
-// file name. Do this after creating the global so that if an AMD module wants
-// to call noConflict to hide this version of jQuery, it will work.
-
-// Note that for maximum portability, libraries that are not jQuery should
-// declare themselves as anonymous modules, and avoid setting a global if an
-// AMD loader is present. jQuery is a special case. For more information, see
-// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
-
-if ( typeof define === "function" && define.amd ) {
-	define( "jquery", [], function() {
-		return jQuery;
-	} );
-}
-
-
-
-var
-
-	// Map over jQuery in case of overwrite
-	_jQuery = window.jQuery,
-
-	// Map over the $ in case of overwrite
-	_$ = window.$;
-
-jQuery.noConflict = function( deep ) {
-	if ( window.$ === jQuery ) {
-		window.$ = _$;
-	}
-
-	if ( deep && window.jQuery === jQuery ) {
-		window.jQuery = _jQuery;
-	}
-
-	return jQuery;
-};
-
-// Expose jQuery and $ identifiers, even in AMD
-// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
-// and CommonJS for browser emulators (#13566)
-if ( !noGlobal ) {
-	window.jQuery = window.$ = jQuery;
-}
-
-return jQuery;
-}));

File diff ditekan karena terlalu besar
+ 0 - 1
app/Resources/public/assets/jquery/dist/jquery.slim.min.js


File diff ditekan karena terlalu besar
+ 0 - 0
app/Resources/public/assets/jquery/dist/jquery.slim.min.map


+ 0 - 14
app/Resources/public/assets/jquery/src/core/DOMEval.js

@@ -1,14 +0,0 @@
-define( [
-	"../var/document"
-], function( document ) {
-	function DOMEval( code, doc ) {
-		doc = doc || document;
-
-		var script = doc.createElement( "script" );
-
-		script.text = code;
-		doc.head.appendChild( script ).parentNode.removeChild( script );
-	}
-
-	return DOMEval;
-} );

+ 5 - 8
app/Resources/public/assets/jquery/src/css/curCSS.js

@@ -12,18 +12,15 @@ function curCSS( elem, name, computed ) {
 		style = elem.style;
 
 	computed = computed || getStyles( elem );
-	ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;
-
-	// Support: Opera 12.1x only
-	// Fall back to style even without computed
-	// computed is undefined for elems on document fragments
-	if ( ( ret === "" || ret === undefined ) && !jQuery.contains( elem.ownerDocument, elem ) ) {
-		ret = jQuery.style( elem, name );
-	}
 
 	// Support: IE9
 	// getPropertyValue is only needed for .css('filter') (#12537)
 	if ( computed ) {
+		ret = computed.getPropertyValue( name ) || computed[ name ];
+
+		if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+			ret = jQuery.style( elem, name );
+		}
 
 		// A tribute to the "awesome hack by Dean Edwards"
 		// Android Browser returns percentage for some values,

+ 1 - 1
app/Resources/public/assets/jquery/src/css/var/getStyles.js

@@ -6,7 +6,7 @@ define( function() {
 		// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
 		var view = elem.ownerDocument.defaultView;
 
-		if ( !view || !view.opener ) {
+		if ( !view.opener ) {
 			view = window;
 		}
 

+ 0 - 19
app/Resources/public/assets/jquery/src/deferred/exceptionHook.js

@@ -1,19 +0,0 @@
-define( [
-	"../core",
-	"../deferred"
-], function( jQuery ) {
-
-// These usually indicate a programmer mistake during development,
-// warn about them ASAP rather than swallowing them by default.
-var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
-
-jQuery.Deferred.exceptionHook = function( error, stack ) {
-
-	// Support: IE9
-	// Console exists when dev tools are open, which can happen at any time
-	if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
-		window.console.warn( "jQuery.Deferred exception: " + error.message, stack );
-	}
-};
-
-} );

+ 1 - 1
app/Resources/public/assets/jquery/src/event.js

@@ -71,7 +71,7 @@ function on( elem, types, selector, data, fn, one ) {
 	if ( fn === false ) {
 		fn = returnFalse;
 	} else if ( !fn ) {
-		return elem;
+		return this;
 	}
 
 	if ( one === 1 ) {

+ 1 - 1
app/Resources/public/assets/jquery/src/event/trigger.js

@@ -164,7 +164,7 @@ jQuery.extend( jQuery.event, {
 				// But now, this "simulate" function is used only for events
 				// for which stopPropagation() is noop, so there is no need for that anymore.
 				//
-				// For the 1.x branch though, guard for "click" and "submit"
+				// For the compat branch though, guard for "click" and "submit"
 				// events is still used, but was moved to jQuery.event.stopPropagation function
 				// because `originalEvent` should point to the original event for the constancy
 				// with other events and for more focused logic

+ 6 - 6
app/Resources/public/assets/jquery/src/manipulation.js

@@ -38,14 +38,14 @@ var
 	rscriptTypeMasked = /^true\/(.*)/,
 	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
 
-// Manipulating tables requires a tbody
 function manipulationTarget( elem, content ) {
-	return jQuery.nodeName( elem, "table" ) &&
-		jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
+	if ( jQuery.nodeName( elem, "table" ) &&
+		jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {
 
-		elem.getElementsByTagName( "tbody" )[ 0 ] ||
-			elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) :
-		elem;
+		return elem.getElementsByTagName( "tbody" )[ 0 ] || elem;
+	}
+
+	return elem;
 }
 
 // Replace/restore the type attribute of script elements for safe DOM manipulation

+ 5 - 2
app/Resources/public/assets/jquery/src/offset.js

@@ -134,8 +134,11 @@ jQuery.fn.extend( {
 			}
 
 			// Add offsetParent borders
-			parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
-			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
+			// Subtract offsetParent scroll positions
+			parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ) -
+				offsetParent.scrollTop();
+			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true ) -
+				offsetParent.scrollLeft();
 		}
 
 		// Subtract parent offsets and element margins

+ 1 - 1
app/Resources/public/assets/jquery/src/selector-sizzle.js

@@ -1,6 +1,6 @@
 define( [
 	"./core",
-	"../external/sizzle/dist/sizzle"
+	"sizzle"
 ], function( jQuery, Sizzle ) {
 
 jQuery.find = Sizzle;

+ 0 - 73
main/inc/lib/javascript/jquery-upload/README.txt

@@ -1,73 +0,0 @@
-jQuery File Upload Plugin
-=========================
-
-Demo
-----
-http://aquantum-demo.appspot.com/file-upload
-
-Setup instructions
-------------------
-https://github.com/blueimp/jQuery-File-Upload/wiki/Setup
-
-Features
---------
-  - Multiple file upload:
-    Allows to select multiple files at once and upload them simultaneously.
-  - Drag & Drop support:
-    Allows to upload files by dragging them from your desktop or filemanager and dropping them on your browser window.
-  - Upload progress bar:
-    Shows a progress bar indicating the upload progress for individual files and for all uploads combined.
-  - Cancelable uploads:
-    Individual file uploads can be canceled to stop the upload progress.
-  - Resumable uploads:
-    Aborted uploads can be resumed with browsers supporting the Blob API.
-  - Chunked uploads:
-    Large files can be uploaded in smaller chunks with browsers supporting the Blob API.
-  - Preview images:
-    A preview of image files can be displayed before uploading with browsers supporting the required HTML5 APIs.
-  - No browser plugins (e.g. Adobe Flash) required:
-    The implementation is based on open standards like HTML5 and JavaScript and requires no additional browser plugins.
-  - Graceful fallback for legacy browsers:
-    Uploads files via XMLHttpRequests if supported and uses iframes as fallback for legacy browsers.
-  - HTML file upload form fallback:
-    Shows a standard HTML file upload form if JavaScript is disabled.
-  - Cross-site file uploads:
-    Supports uploading files to a different domain with Cross-site XMLHttpRequests.
-  - Multiple plugin instances:
-    Allows to use multiple plugin instances on the same webpage.
-  - Customizable and extensible:
-    Provides an API to set individual options and define callBack methods for various upload events.
-  - Multipart and file contents stream uploads:
-    Files can be uploaded as standard "multipart/form-data" or file contents stream (HTTP PUT file upload).
-  - Compatible with any server-side application platform:
-    Works with Google App Engine (Python, Java), Ruby on Rails, PHP and any other platform that supports HTTP file uploads.
-
-Requirements
-------------
-  - jQuery v. 1.4+
-  - jQuery UI v. 1.8+ (optional)
-
-Browser Support (tested versions)
----------------------------------
-  - Google Chrome - 7.0, 8.0, 9.0, 10.0, 11.0
-  - Apple Safari - 4.0, 5.0 ¹
-  - Mozilla Firefox - 3.5, 3.6, 4.0
-  - Opera - 10.6, 11.0, 11.1 ²
-  - Microsoft Internet Explorer 6.0, 7.0, 8.0, 9.0 ³
-
-¹ Drag & Drop is not supported on the Windows version of Safari.
-² Opera has no suppport for Drag & Drop or upload progress, but support for multiple file selection since version 11.1.
-³ MSIE has no support for Drag & Drop, multiple file selection or upload progress.
-
-License
--------
-Released under the MIT license:
-http://creativecommons.org/licenses/MIT/
-
-Source Code & Download
-----------------------
-https://github.com/blueimp/jQuery-File-Upload
-
-Documentation
--------------
-https://github.com/blueimp/jQuery-File-Upload/wiki

+ 0 - 82
main/inc/lib/javascript/jquery-upload/jquery.fileupload-ui.css

@@ -1,82 +0,0 @@
-.file_upload {
-    position: relative;
-    overflow: hidden;
-    direction: ltr;
-    cursor: pointer;
-    text-align: center;
-    color: #333;
-    font-weight: bold;
-    -moz-border-radius: 10px;
-    -webkit-border-radius: 10px;
-    border-radius: 10px;
-    width: auto;
-    margin-left: auto;
-    margin-right: auto;
-    height: auto;
-    line-height: 50px;
-    background-color: #D4E6F0;
-    border: 2px dashed #bbbbbb;
-    font-size:120%;
-    margin-bottom: 0;
-}
-.form-ajax{
-  border: 2px solid #bbbbbb;
-  padding: .5em;
-  -moz-border-radius: 10px;
-  -webkit-border-radius: 10px;
-  border-radius: 10px;
-}
-.file_upload_small {
-    line-height: 50px;
-    font-size: auto;
-    background: #aed1e5;
-    border: 2px dashed #bbbbbb;
-}
-
-.file_upload_large {
-    /*width: 100%;
-    height: 150px;
-    line-height: 150px; 
-    background: palegreen;
-    border: 1px solid limegreen;*/
-}
-
-.file_upload_highlight {
-    background: #9acce9;
-    border: 2px dashed #88bbd8;
-}
-
-.file_upload input {
-    position: absolute;
-    top: 0;
-    right: 0;
-    margin: 0;
-    border: 300px solid transparent;
-    opacity: 0;
-    -ms-filter: 'alpha(opacity=0)';
-    filter: alpha(opacity=0);
-    -o-transform: translate(-300px, -300px) scale(10);
-    -moz-transform: translate(-800px, 0) scale(10);
-    cursor: pointer;
-}
-
-.file_upload iframe, .file_upload button {
-    display: none;
-}
-
-.file_upload_preview img {
-    width: 80px;
-}
-
-.file_upload_progress .ui-progressbar-value {
-    background: url(pbar-ani.gif);
-}
-
-.file_upload_progress div {
-    width: 150px;
-    height: 15px;
-}
-
-.file_upload_cancel button {
-    cursor: pointer;
-}

+ 0 - 533
main/inc/lib/javascript/jquery-upload/jquery.fileupload-ui.js

@@ -1,533 +0,0 @@
-/*
- * jQuery File Upload User Interface Plugin 4.4.1
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2010, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * http://creativecommons.org/licenses/MIT/
- */
-
-/*jslint browser: true, unparam: true */
-/*global jQuery, FileReader, URL, webkitURL */
-
-(function ($) {
-    'use strict';
-
-    var undef = 'undefined',
-        func = 'function',
-        UploadHandler,
-        methods,
-
-        MultiLoader = function (callBack) {
-            var loaded = 0,
-                list = [];
-            this.complete = function () {
-                loaded += 1;
-                if (loaded === list.length + 1) {
-                    // list.length * onComplete + 1 * onLoadAll
-                    callBack(list);
-                    loaded = 0;
-                    list = [];
-                }
-            };
-            this.push = function (item) {
-                list.push(item);
-            };
-            this.getList = function () {
-                return list;
-            };
-        };
-        
-    UploadHandler = function (container, options) {
-        var uploadHandler = this,
-            dragOverTimeout,
-            isDropZoneEnlarged,
-            multiLoader = new MultiLoader(function (list) {
-                uploadHandler.hideProgressBarAll(function () {
-                    uploadHandler.resetProgressBarAll();
-                    if (typeof uploadHandler.onCompleteAll === func) {
-                        uploadHandler.onCompleteAll(list);
-                    }
-                });
-            }),
-            getUploadTable = function (handler) {
-                return typeof handler.uploadTable === func ?
-                    handler.uploadTable(handler) : handler.uploadTable;
-            },
-            getDownloadTable = function (handler) {
-                return typeof handler.downloadTable === func ?
-                    handler.downloadTable(handler) : handler.downloadTable;
-            };
-        
-        this.requestHeaders = {'Accept': 'application/json, text/javascript, */*; q=0.01'};
-        this.dropZone = container;
-        this.imageTypes = /^image\/(gif|jpeg|png)$/;
-        this.previewMaxWidth = this.previewMaxHeight = 80;
-        this.previewLoadDelay = 100;
-        this.previewAsCanvas = true;
-        this.previewSelector = '.file_upload_preview';
-        this.progressSelector = '.file_upload_progress div';
-        this.cancelSelector = '.file_upload_cancel button';
-        this.cssClassSmall = 'file_upload_small';
-        this.cssClassLarge = 'file_upload_large';
-        this.cssClassHighlight = 'file_upload_highlight';
-        this.dropEffect = 'highlight';
-        this.uploadTable = this.downloadTable = null;
-        this.buildUploadRow = this.buildDownloadRow = null;
-        this.progressAllNode = null;
-
-        this.loadImage = function (file, callBack, maxWidth, maxHeight, imageTypes, noCanvas) {
-            var img,
-                scaleImage,
-                urlAPI,
-                fileReader;
-            if (imageTypes && !imageTypes.test(file.type)) {
-                return null;
-            }
-            scaleImage = function (img) {
-                var canvas = document.createElement('canvas'),
-                    scale = Math.min(
-                        (maxWidth || img.width) / img.width,
-                        (maxHeight || img.height) / img.height
-                    );
-                if (scale > 1) {
-                    scale = 1;
-                }
-                img.width = parseInt(img.width * scale, 10);
-                img.height = parseInt(img.height * scale, 10);
-                if (noCanvas || typeof canvas.getContext !== func) {
-                    return img;
-                }
-                canvas.width = img.width;
-                canvas.height = img.height;
-                canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
-                return canvas;
-            };
-            img = document.createElement('img');
-            urlAPI = typeof URL !== undef ? URL : typeof webkitURL !== undef ? webkitURL : null;
-            if (urlAPI && typeof urlAPI.createObjectURL === func) {
-                img.onload = function () {
-                    urlAPI.revokeObjectURL(this.src);
-                    callBack(scaleImage(img));
-                };
-                img.src = urlAPI.createObjectURL(file);
-            } else if (typeof FileReader !== undef &&
-                    typeof FileReader.prototype.readAsDataURL === func) {
-                img.onload = function () {
-                    callBack(scaleImage(img));
-                };
-                fileReader = new FileReader();
-                fileReader.onload = function (e) {
-                    img.src = e.target.result;
-                };
-                fileReader.readAsDataURL(file);
-            } else {
-                callBack(null);
-            }
-        };
-
-        this.addNode = function (parentNode, node, callBack) {
-            if (parentNode && parentNode.length && node && node.length) {
-                node.css('display', 'none').appendTo(parentNode).fadeIn(function () {
-                    if (typeof callBack === func) {
-                        try {
-                            callBack();
-                        } catch (e) {
-                            // Fix endless exception loop:
-                            node.stop();
-                            throw e;
-                        }
-                    }
-                });
-            } else if (typeof callBack === func) {
-                callBack();
-            }
-        };
-
-        this.removeNode = function (node, callBack) {
-            if (node && node.length) {
-                node.fadeOut(function () {
-                    node.remove();
-                    if (typeof callBack === func) {
-                        try {
-                            callBack();
-                        } catch (e) {
-                            // Fix endless exception loop:
-                            node.stop();
-                            throw e;
-                        }
-                    }
-                });
-            } else if (typeof callBack === func) {
-                callBack();
-            }
-        };
-        
-        this.replaceNode = function (oldNode, newNode, callBack) {
-            if (!(newNode && newNode.length)) {
-                return uploadHandler.removeNode(oldNode, callBack);
-            }
-            if (oldNode && oldNode.length) {
-                oldNode.fadeOut(function () {
-                    newNode.css('display', 'none');
-                    oldNode.replaceWith(newNode);
-                    newNode.fadeIn(function () {
-                        if (typeof callBack === func) {
-                            try {
-                                callBack();
-                            } catch (e) {
-                                // Fix endless exception loop:
-                                oldNode.stop();
-                                newNode.stop();
-                                throw e;
-                            }
-                        }
-                    });
-                });
-            } else if (typeof callBack === func) {
-                callBack();
-            }
-        };
-
-        this.resetProgressBarAll = function () {
-            if (uploadHandler.progressbarAll) {
-                uploadHandler.progressbarAll.progressbar(
-                    'value',
-                    0
-                );
-            }
-        };
-
-        this.hideProgressBarAll = function (callBack) {
-            if (uploadHandler.progressbarAll && !$(getUploadTable(uploadHandler))
-                    .find(uploadHandler.progressSelector + ':visible:first').length) {
-                uploadHandler.progressbarAll.fadeOut(callBack);
-            } else if (typeof callBack === func) {
-                callBack();
-            }
-        };
-
-        this.onAbort = function (event, files, index, xhr, handler) {
-            handler.removeNode(handler.uploadRow, handler.hideProgressBarAll);
-        };
-        
-        this.cancelUpload = function (event, files, index, xhr, handler) {
-            var readyState = xhr.readyState;
-            xhr.abort();
-            // If readyState is below 2, abort() has no effect:
-            if (typeof readyState !== 'number' || readyState < 2) {
-                handler.onAbort(event, files, index, xhr, handler);
-            }
-        };
-        
-        this.initProgressBar = function (node, value) {
-            if (!node || !node.length) {
-                return null;
-            }
-            if (typeof node.progressbar === func) {
-                return node.progressbar({
-                    value: value
-                });
-            } else {
-                node.addClass('progressbar')
-                    .append($('<div/>').css('width', value + '%'))
-                    .progressbar = function (key, value) {
-                        return this.each(function () {
-                            if (key === 'destroy') {
-                                $(this).removeClass('progressbar').empty();
-                            } else {
-                                $(this).children().css('width', value + '%');
-                            }
-                        });
-                    };
-                return node;
-            }
-        };
-        
-        this.destroyProgressBar = function (node) {
-            if (!node || !node.length) {
-                return null;
-            }
-            return node.progressbar('destroy');
-        };
-        
-        this.initUploadProgress = function (xhr, handler) {
-            if (!xhr.upload && handler.progressbar) {
-                handler.progressbar.progressbar(
-                    'value',
-                    100 // indeterminate progress displayed by a full animated progress bar
-                );
-            }
-        };
-
-        this.initUploadProgressAll = function () {
-            if (uploadHandler.progressbarAll && uploadHandler.progressbarAll.is(':hidden')) {
-                uploadHandler.progressbarAll.fadeIn();
-            }
-        };
-
-        this.onSend = function (event, files, index, xhr, handler) {
-            handler.initUploadProgress(xhr, handler);
-        };
-
-        this.onProgress = function (event, files, index, xhr, handler) {
-            if (handler.progressbar && event.lengthComputable) {
-                handler.progressbar.progressbar(
-                    'value',
-                    parseInt(event.loaded / event.total * 100, 10)
-                );
-            }
-        };
-
-        this.onProgressAll = function (event, list) {
-            if (uploadHandler.progressbarAll && event.lengthComputable) {
-                uploadHandler.progressbarAll.progressbar(
-                    'value',
-                    parseInt(event.loaded / event.total * 100, 10)
-                );
-            }
-        };
-
-        this.onLoadAll = function (list) {
-            multiLoader.complete();
-        };
-
-        this.initProgressBarAll = function () {
-            if (!uploadHandler.progressbarAll) {
-                uploadHandler.progressbarAll = uploadHandler.initProgressBar(
-                    (typeof uploadHandler.progressAllNode === func ?
-                    uploadHandler.progressAllNode(uploadHandler) : uploadHandler.progressAllNode),
-                    0
-                );
-            }
-        };
-        
-        this.destroyProgressBarAll = function () {
-            uploadHandler.destroyProgressBar(uploadHandler.progressbarAll);
-        };
-
-        this.loadPreviewImage = function (files, index, handler) {
-            index = index || 0;
-            handler.uploadRow.find(handler.previewSelector).each(function () {
-                var previewNode = $(this),
-                    file = files[index];
-                setTimeout(function () {
-                    handler.loadImage(
-                        file,
-                        function (img) {
-                            handler.addNode(
-                                previewNode,
-                                $(img)
-                            );
-                        },
-                        handler.previewMaxWidth,
-                        handler.previewMaxHeight,
-                        handler.imageTypes,
-                        !handler.previewAsCanvas
-                    );
-                }, handler.previewLoadDelay);
-                index += 1;
-            });
-        };
-
-        this.initUploadRow = function (event, files, index, xhr, handler) {
-            var uploadRow = handler.uploadRow = (typeof handler.buildUploadRow === func ?
-                handler.buildUploadRow(files, index, handler) : null);
-            if (uploadRow) {
-                handler.progressbar = handler.initProgressBar(
-                    uploadRow.find(handler.progressSelector),
-                    0
-                );
-                uploadRow.find(handler.cancelSelector).click(function (e) {
-                    handler.cancelUpload(e, files, index, xhr, handler);
-                    e.preventDefault();
-                });
-                handler.loadPreviewImage(files, index, handler);
-            }
-        };
-        
-        this.initUpload = function (event, files, index, xhr, handler, callBack) {
-            handler.initUploadRow(event, files, index, xhr, handler);
-            handler.addNode(
-                getUploadTable(handler),
-                handler.uploadRow,
-                function () {
-                    if (typeof handler.beforeSend === func) {
-                        handler.beforeSend(event, files, index, xhr, handler, callBack);
-                    } else {
-                        callBack();
-                    }
-                }
-            );
-            handler.initUploadProgressAll();
-        };
-        
-        this.parseResponse = function (xhr, handler) {
-            if (typeof xhr.responseText !== undef) {
-                return $.parseJSON(xhr.responseText);
-            } else {
-                // Instead of an XHR object, an iframe is used for legacy browsers:
-                return $.parseJSON(xhr.contents().text());
-            }
-        };
-        
-        this.initDownloadRow = function (event, files, index, xhr, handler) {
-            var json, downloadRow;
-            try {
-                json = handler.response = handler.parseResponse(xhr, handler);
-            } catch (e) {
-                if (typeof handler.onError === func) {
-                    handler.originalEvent = event;
-                    handler.onError(e, files, index, xhr, handler);
-                } else {
-                    throw e;
-                }
-            }
-            downloadRow = handler.downloadRow = (typeof handler.buildDownloadRow === func ?
-                handler.buildDownloadRow(json, handler) : null);
-        };
-        
-        this.onLoad = function (event, files, index, xhr, handler) {
-            var uploadTable = getUploadTable(handler),
-                downloadTable = getDownloadTable(handler),
-                callBack = function () {
-                    if (typeof handler.onComplete === func) {
-                        handler.onComplete(event, files, index, xhr, handler);
-                    }
-                    multiLoader.complete();
-                };
-            multiLoader.push(Array.prototype.slice.call(arguments, 1));
-            handler.initDownloadRow(event, files, index, xhr, handler);
-            if (uploadTable && handler.uploadRow && handler.uploadRow.length &&
-                    (!downloadTable || uploadTable.get(0) === downloadTable.get(0))) {
-                handler.replaceNode(handler.uploadRow, handler.downloadRow, callBack);
-            } else {
-                handler.removeNode(handler.uploadRow, function () {
-                    handler.addNode(
-                        downloadTable,
-                        handler.downloadRow,
-                        callBack
-                    );
-                });
-            }
-        };
-
-        this.dropZoneEnlarge = function () {
-            if (!isDropZoneEnlarged) {
-                if (typeof uploadHandler.dropZone.switchClass === func) {
-                    uploadHandler.dropZone.switchClass(
-                        uploadHandler.cssClassSmall,
-                        uploadHandler.cssClassLarge
-                    );
-                } else {
-                    uploadHandler.dropZone.addClass(uploadHandler.cssClassLarge);
-                    uploadHandler.dropZone.removeClass(uploadHandler.cssClassSmall);
-                }
-                isDropZoneEnlarged = true;
-            }
-        };
-        
-        this.dropZoneReduce = function () {
-            if (typeof uploadHandler.dropZone.switchClass === func) {
-                uploadHandler.dropZone.switchClass(
-                    uploadHandler.cssClassLarge,
-                    uploadHandler.cssClassSmall
-                );
-            } else {
-                uploadHandler.dropZone.addClass(uploadHandler.cssClassSmall);
-                uploadHandler.dropZone.removeClass(uploadHandler.cssClassLarge);
-            }
-            isDropZoneEnlarged = false;
-        };
-
-        this.onDocumentDragEnter = function (event) {
-            uploadHandler.dropZoneEnlarge();
-        };
-        
-        this.onDocumentDragOver = function (event) {
-            if (dragOverTimeout) {
-                clearTimeout(dragOverTimeout);
-            }
-            dragOverTimeout = setTimeout(function () {
-                uploadHandler.dropZoneReduce();
-            }, 200);
-        };
-        
-        this.onDragEnter = this.onDragLeave = function (event) {
-            uploadHandler.dropZone.toggleClass(uploadHandler.cssClassHighlight);
-        };
-        
-        this.onDrop = function (event) {
-            if (dragOverTimeout) {
-                clearTimeout(dragOverTimeout);
-            }
-            if (uploadHandler.dropEffect && typeof uploadHandler.dropZone.effect === func) {
-                uploadHandler.dropZone.effect(uploadHandler.dropEffect, function () {
-                    uploadHandler.dropZone.removeClass(uploadHandler.cssClassHighlight);
-                    uploadHandler.dropZoneReduce();
-                });
-            } else {
-                uploadHandler.dropZone.removeClass(uploadHandler.cssClassHighlight);
-                uploadHandler.dropZoneReduce();
-            }
-        };
-
-        this.init = function () {
-            uploadHandler.initProgressBarAll();
-            if (typeof uploadHandler.initExtended === func) {
-                uploadHandler.initExtended();
-            }
-        };
-        
-        this.destroy = function () {
-            if (typeof uploadHandler.destroyExtended === func) {
-                uploadHandler.destroyExtended();
-            }
-            uploadHandler.destroyProgressBarAll();
-        };
-
-        $.extend(this, options);
-    };
-
-    methods = {
-        init : function (options) {
-            return this.each(function () {
-                $(this).fileUpload(new UploadHandler($(this), options));
-            });
-        },
-        
-        option: function (option, value, namespace) {
-            if (!option || (typeof option === 'string' && typeof value === undef)) {
-                return $(this).fileUpload('option', option, value, namespace);
-            }
-            return this.each(function () {
-                $(this).fileUpload('option', option, value, namespace);
-            });
-        },
-            
-        destroy : function (namespace) {
-            return this.each(function () {
-                $(this).fileUpload('destroy', namespace);
-            });
-        },
-        
-        upload: function (files, namespace) {
-            return this.each(function () {
-                $(this).fileUpload('upload', files, namespace);
-            });
-        }
-    };
-    
-    $.fn.fileUploadUI = function (method) {
-        if (methods[method]) {
-            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
-        } else if (typeof method === 'object' || !method) {
-            return methods.init.apply(this, arguments);
-        } else {
-            $.error('Method "' + method + '" does not exist on jQuery.fileUploadUI');
-        }
-    };
-    
-}(jQuery));

+ 0 - 975
main/inc/lib/javascript/jquery-upload/jquery.fileupload.js

@@ -1,975 +0,0 @@
-/*
- * jQuery File Upload Plugin 4.5.1
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2010, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * http://creativecommons.org/licenses/MIT/
- */
-
-/*jslint browser: true, unparam: true */
-/*global XMLHttpRequestUpload, File, FileReader, FormData, ProgressEvent, unescape, jQuery, upload */
-
-(function ($) {
-    'use strict';
-
-    var defaultNamespace = 'file_upload',
-        undef = 'undefined',
-        func = 'function',
-        FileUpload,
-        methods,
-
-        MultiLoader = function (callBack, numOrList) {
-            var loaded = 0,
-                list = [];
-            if (numOrList) {
-                if (numOrList.length) {
-                    list = numOrList;
-                } else {
-                    list[numOrList - 1] = null;
-                }
-            }
-            this.complete = function () {
-                loaded += 1;
-                if (loaded === list.length) {
-                    callBack(list);
-                    loaded = 0;
-                    list = [];
-                }
-            };
-            this.push = function (item) {
-                list.push(item);
-            };
-            this.getList = function () {
-                return list;
-            };
-        },
-        
-        SequenceHandler = function () {
-            var sequence = [];
-            this.push = function (callBack) {
-                sequence.push(callBack);
-                if (sequence.length === 1) {
-                    callBack();
-                }
-            };
-            this.next = function () {
-                sequence.shift();
-                if (sequence.length) {
-                    sequence[0]();
-                }
-            };
-        };
-        
-    FileUpload = function (container) {
-        var fileUpload = this,
-            uploadForm,
-            fileInput,
-            settings = {
-                namespace: defaultNamespace,
-                uploadFormFilter: function (index) {
-                    return true;
-                },
-                fileInputFilter: function (index) {
-                    return true;
-                },
-                cssClass: defaultNamespace,
-                dragDropSupport: true,
-                dropZone: container,
-                url: function (form) {
-                    return form.attr('action');
-                },
-                method: function (form) {
-                    return form.attr('method');
-                },
-                fieldName: function (input) {
-                    return input.attr('name');
-                },
-                formData: function (form) {
-                    return form.serializeArray();
-                },
-                requestHeaders: null,
-                multipart: true,
-                multiFileRequest: false,
-                withCredentials: false,
-                forceIframeUpload: false,
-                sequentialUploads: false,
-                maxChunkSize: null,
-                maxFileReaderSize: 50000000,
-                replaceFileInput: true
-            },
-            documentListeners = {},
-            dropZoneListeners = {},
-            protocolRegExp = /^http(s)?:\/\//,
-            optionsReference,
-            multiLoader = new MultiLoader(function (list) {
-                if (typeof settings.onLoadAll === func) {
-                    settings.onLoadAll(list);
-                }
-            }),
-            sequenceHandler = new SequenceHandler(),
-            
-            completeNext = function () {
-                multiLoader.complete();
-                sequenceHandler.next();
-            },
-
-            isXHRUploadCapable = function () {
-                return typeof XMLHttpRequest !== undef && typeof XMLHttpRequestUpload !== undef &&
-                    typeof File !== undef && (!settings.multipart || typeof FormData !== undef ||
-                    (typeof FileReader !== undef && typeof XMLHttpRequest.prototype.sendAsBinary === func));
-            },
-
-            initEventHandlers = function () {
-                if (settings.dragDropSupport) {
-                    if (typeof settings.onDocumentDragEnter === func) {
-                        documentListeners['dragenter.' + settings.namespace] = function (e) {
-                            settings.onDocumentDragEnter(e);
-                        };
-                    }
-                    if (typeof settings.onDocumentDragLeave === func) {
-                        documentListeners['dragleave.' + settings.namespace] = function (e) {
-                            settings.onDocumentDragLeave(e);
-                        };
-                    }
-                    documentListeners['dragover.'   + settings.namespace] = fileUpload.onDocumentDragOver;
-                    documentListeners['drop.'       + settings.namespace] = fileUpload.onDocumentDrop;
-                    $(document).bind(documentListeners);
-                    if (typeof settings.onDragEnter === func) {
-                        dropZoneListeners['dragenter.' + settings.namespace] = function (e) {
-                            settings.onDragEnter(e);
-                        };
-                    }
-                    if (typeof settings.onDragLeave === func) {
-                        dropZoneListeners['dragleave.' + settings.namespace] = function (e) {
-                            settings.onDragLeave(e);
-                        };
-                    }
-                    dropZoneListeners['dragover.'   + settings.namespace] = fileUpload.onDragOver;
-                    dropZoneListeners['drop.'       + settings.namespace] = fileUpload.onDrop;
-                    settings.dropZone.bind(dropZoneListeners);
-                }
-                fileInput.bind('change.' + settings.namespace, fileUpload.onChange);
-            },
-
-            removeEventHandlers = function () {
-                $.each(documentListeners, function (key, value) {
-                    $(document).unbind(key, value);
-                });
-                $.each(dropZoneListeners, function (key, value) {
-                    settings.dropZone.unbind(key, value);
-                });
-                fileInput.unbind('change.' + settings.namespace);
-            },
-
-            isChunkedUpload = function (settings) {
-                return typeof settings.uploadedBytes !== undef;
-            },
-
-            createProgressEvent = function (lengthComputable, loaded, total) {
-                var event;
-                if (typeof document.createEvent === func && typeof ProgressEvent !== undef) {
-                    event = document.createEvent('ProgressEvent');
-                    event.initProgressEvent(
-                        'progress',
-                        false,
-                        false,
-                        lengthComputable,
-                        loaded,
-                        total
-                    );
-                } else {
-                    event = {
-                        lengthComputable: true,
-                        loaded: loaded,
-                        total: total
-                    };
-                }
-                return event;
-            },
-
-            getProgressTotal = function (files, index, settings) {
-                var i,
-                    total;
-                if (typeof settings.progressTotal === undef) {
-                    if (files[index]) {
-                        total = files[index].size;
-                        settings.progressTotal = total ? total : 1;
-                    } else {
-                        total = 0;
-                        for (i = 0; i < files.length; i += 1) {
-                            total += files[i].size;
-                        }
-                        settings.progressTotal = total;
-                    }
-                }
-                return settings.progressTotal;
-            },
-
-            handleGlobalProgress = function (event, files, index, xhr, settings) {
-                var progressEvent,
-                    loaderList,
-                    globalLoaded = 0,
-                    globalTotal = 0;
-                if (event.lengthComputable && typeof settings.onProgressAll === func) {
-                    settings.progressLoaded = parseInt(
-                        event.loaded / event.total * getProgressTotal(files, index, settings),
-                        10
-                    );
-                    loaderList = multiLoader.getList();
-                    $.each(loaderList, function (index, item) {
-                        // item is an array with [files, index, xhr, settings]
-                        globalLoaded += item[3].progressLoaded || 0;
-                        globalTotal += getProgressTotal(item[0], item[1], item[3]);
-                    });
-                    progressEvent = createProgressEvent(
-                        true,
-                        globalLoaded,
-                        globalTotal
-                    );
-                    settings.onProgressAll(progressEvent, loaderList);
-                }
-            },
-            
-            handleLoadEvent = function (event, files, index, xhr, settings) {
-                var progressEvent;
-                if (isChunkedUpload(settings)) {
-                    settings.uploadedBytes += settings.chunkSize;
-                    progressEvent = createProgressEvent(
-                        true,
-                        settings.uploadedBytes,
-                        files[index].size
-                    );
-                    if (typeof settings.onProgress === func) {
-                        settings.onProgress(progressEvent, files, index, xhr, settings);
-                    }
-                    handleGlobalProgress(progressEvent, files, index, xhr, settings);
-                    if (settings.uploadedBytes < files[index].size) {
-                        if (typeof settings.resumeUpload === func) {
-                            settings.resumeUpload(
-                                event,
-                                files,
-                                index,
-                                xhr,
-                                settings,
-                                function () {
-                                    upload(event, files, index, xhr, settings, true);
-                                }
-                            );
-                        } else {
-                            upload(event, files, index, xhr, settings, true);
-                        }
-                        return;
-                    }
-                }
-                settings.progressLoaded = getProgressTotal(files, index, settings);
-                if (typeof settings.onLoad === func) {
-                    settings.onLoad(event, files, index, xhr, settings);
-                }
-                completeNext();
-            },
-            
-            handleProgressEvent = function (event, files, index, xhr, settings) {
-                var progressEvent = event;
-                if (isChunkedUpload(settings) && event.lengthComputable) {
-                    progressEvent = createProgressEvent(
-                        true,
-                        settings.uploadedBytes + parseInt(event.loaded / event.total * settings.chunkSize, 10),
-                        files[index].size
-                    );
-                }
-                if (typeof settings.onProgress === func) {
-                    settings.onProgress(progressEvent, files, index, xhr, settings);
-                }
-                handleGlobalProgress(progressEvent, files, index, xhr, settings);
-            },
-            
-            initUploadEventHandlers = function (files, index, xhr, settings) {
-                if (xhr.upload) {
-                    xhr.upload.onprogress = function (e) {
-                        handleProgressEvent(e, files, index, xhr, settings);
-                    };
-                }
-                xhr.onload = function (e) {
-                    handleLoadEvent(e, files, index, xhr, settings);
-                };
-                xhr.onabort = function (e) {
-                    settings.progressTotal = settings.progressLoaded;
-                    if (typeof settings.onAbort === func) {
-                        settings.onAbort(e, files, index, xhr, settings);
-                    }
-                    completeNext();
-                };
-                xhr.onerror = function (e) {
-                    settings.progressTotal = settings.progressLoaded;
-                    if (typeof settings.onError === func) {
-                        settings.onError(e, files, index, xhr, settings);
-                    }
-                    completeNext();
-                };
-            },
-
-            getUrl = function (settings) {
-                if (typeof settings.url === func) {
-                    return settings.url(settings.uploadForm || uploadForm);
-                }
-                return settings.url;
-            },
-            
-            getMethod = function (settings) {
-                if (typeof settings.method === func) {
-                    return settings.method(settings.uploadForm || uploadForm);
-                }
-                return settings.method;
-            },
-            
-            getFieldName = function (settings) {
-                if (typeof settings.fieldName === func) {
-                    return settings.fieldName(settings.fileInput || fileInput);
-                }
-                return settings.fieldName;
-            },
-
-            getFormData = function (settings) {
-                var formData;
-                if (typeof settings.formData === func) {
-                    return settings.formData(settings.uploadForm || uploadForm);
-                } else if ($.isArray(settings.formData)) {
-                    return settings.formData;
-                } else if (settings.formData) {
-                    formData = [];
-                    $.each(settings.formData, function (name, value) {
-                        formData.push({name: name, value: value});
-                    });
-                    return formData;
-                }
-                return [];
-            },
-
-            isSameDomain = function (url) {
-                if (protocolRegExp.test(url)) {
-                    var host = location.host,
-                        indexStart = location.protocol.length + 2,
-                        index = url.indexOf(host, indexStart),
-                        pathIndex = index + host.length;
-                    if ((index === indexStart || index === url.indexOf('@', indexStart) + 1) &&
-                            (url.length === pathIndex || $.inArray(url.charAt(pathIndex), ['/', '?', '#']) !== -1)) {
-                        return true;
-                    }
-                    return false;
-                }
-                return true;
-            },
-
-            initUploadRequest = function (files, index, xhr, settings) {
-                var file = files[index],
-                    url = getUrl(settings),
-                    sameDomain = isSameDomain(url);
-                xhr.open(getMethod(settings), url, true);
-                if (sameDomain) {
-                    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
-                    if (!settings.multipart || isChunkedUpload(settings)) {
-                        xhr.setRequestHeader('X-File-Name', file.name);
-                        xhr.setRequestHeader('X-File-Type', file.type);
-                        xhr.setRequestHeader('X-File-Size', file.size);
-                        if (!isChunkedUpload(settings)) {
-                            xhr.setRequestHeader('Content-Type', file.type);
-                        } else if (!settings.multipart) {
-                            xhr.setRequestHeader('Content-Type', 'application/octet-stream');
-                        }
-                    }
-                } else if (settings.withCredentials) {
-                    xhr.withCredentials = true;
-                }
-                if ($.isArray(settings.requestHeaders)) {
-                    $.each(settings.requestHeaders, function (index, header) {
-                        xhr.setRequestHeader(header.name, header.value);
-                    });
-                } else if (settings.requestHeaders) {
-                    $.each(settings.requestHeaders, function (name, value) {
-                        xhr.setRequestHeader(name, value);
-                    });
-                }
-            },
-
-            formDataUpload = function (files, xhr, settings) {
-                var formData = new FormData(),
-                    i;
-                $.each(getFormData(settings), function (index, field) {
-                    formData.append(field.name, field.value);
-                });
-                for (i = 0; i < files.length; i += 1) {
-                    formData.append(getFieldName(settings), files[i]);
-                }
-                xhr.send(formData);
-            },
-
-            loadFileContent = function (file, callBack) {
-                file.reader = new FileReader();
-                file.reader.onload = callBack;
-                file.reader.readAsBinaryString(file);
-            },
-
-            utf8encode = function (str) {
-                return unescape(encodeURIComponent(str));
-            },
-
-            buildMultiPartFormData = function (boundary, files, filesFieldName, fields) {
-                var doubleDash = '--',
-                    crlf     = '\r\n',
-                    formData = '',
-                    buffer = [];
-                $.each(fields, function (index, field) {
-                    formData += doubleDash + boundary + crlf +
-                        'Content-Disposition: form-data; name="' +
-                        utf8encode(field.name) +
-                        '"' + crlf + crlf +
-                        utf8encode(field.value) + crlf;
-                });
-                $.each(files, function (index, file) {
-                    formData += doubleDash + boundary + crlf +
-                        'Content-Disposition: form-data; name="' +
-                        utf8encode(filesFieldName) +
-                        '"; filename="' + utf8encode(file.name) + '"' + crlf +
-                        'Content-Type: ' + utf8encode(file.type) + crlf + crlf;
-                    buffer.push(formData);
-                    buffer.push(file.reader.result);
-                    delete file.reader;
-                    formData = crlf;
-                });
-                formData += doubleDash + boundary + doubleDash + crlf;
-                buffer.push(formData);
-                return buffer.join('');
-            },
-            
-            fileReaderUpload = function (files, xhr, settings) {
-                var boundary = '----MultiPartFormBoundary' + (new Date()).getTime(),
-                    loader,
-                    i;
-                xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
-                loader = new MultiLoader(function () {
-                    xhr.sendAsBinary(buildMultiPartFormData(
-                        boundary,
-                        files,
-                        getFieldName(settings),
-                        getFormData(settings)
-                    ));
-                }, files.length);
-                for (i = 0; i < files.length; i += 1) {
-                    loadFileContent(files[i], loader.complete);
-                }
-            },
-
-            getBlob = function (file, settings) {
-                var blob,
-                    ub = settings.uploadedBytes,
-                    mcs = settings.maxChunkSize;
-                if (file && typeof file.slice === func && (ub || (mcs && mcs < file.size))) {
-                    settings.uploadedBytes = ub = ub || 0;
-                    blob = file.slice(ub, mcs || file.size - ub);
-                    settings.chunkSize = blob.size;
-                    return blob;
-                }
-                return file;
-            },
-
-            upload = function (event, files, index, xhr, settings, nextChunk) {
-                var send;
-                send = function () {
-                    if (!nextChunk) {
-                        if (typeof settings.onSend === func &&
-                                settings.onSend(event, files, index, xhr, settings) === false) {
-                            completeNext();
-                            return;
-                        }
-                    }
-                    var blob = getBlob(files[index], settings),
-                        filesToUpload;
-                    initUploadEventHandlers(files, index, xhr, settings);
-                    initUploadRequest(files, index, xhr, settings);
-                    if (!settings.multipart) {
-                        if (xhr.upload) {
-                            xhr.send(blob);
-                        } else {
-                            $.error('Browser does not support XHR file uploads');
-                        }
-                    } else {
-                        filesToUpload = (typeof index === 'number') ? [blob] : files;
-                        if (typeof FormData !== undef) {
-                            formDataUpload(filesToUpload, xhr, settings);
-                        } else if (typeof FileReader !== undef && typeof xhr.sendAsBinary === func) {
-                            fileReaderUpload(filesToUpload, xhr, settings);
-                        } else {
-                            $.error('Browser does not support multipart/form-data XHR file uploads');
-                        }
-                    }
-                };
-                if (!nextChunk) {
-                    multiLoader.push(Array.prototype.slice.call(arguments, 1));
-                    if (settings.sequentialUploads) {
-                        sequenceHandler.push(send);
-                        return;
-                    }
-                }
-                send();
-            },
-
-            handleUpload = function (event, files, input, form, index) {
-                var xhr = new XMLHttpRequest(),
-                    uploadSettings = $.extend({}, settings);
-                uploadSettings.fileInput = input;
-                uploadSettings.uploadForm = form;
-                if (typeof uploadSettings.initUpload === func) {
-                    uploadSettings.initUpload(
-                        event,
-                        files,
-                        index,
-                        xhr,
-                        uploadSettings,
-                        function () {
-                            upload(event, files, index, xhr, uploadSettings);
-                        }
-                    );
-                } else {
-                    upload(event, files, index, xhr, uploadSettings);
-                }
-            },
-
-            handleLegacyGlobalProgress = function (event, files, index, iframe, settings) {
-                var total = 0,
-                    progressEvent;
-                if (typeof index === undef) {
-                    $.each(files, function (index, file) {
-                        total += file.size ? file.size : 1;
-                    });
-                } else {
-                    total = files[index].size ? files[index].size : 1;
-                }
-                progressEvent = createProgressEvent(true, total, total);
-                settings.progressLoaded = total;
-                handleGlobalProgress(progressEvent, files, index, iframe, settings);
-            },
-
-            legacyUploadFormDataInit = function (input, form, settings) {
-                var formData = getFormData(settings);
-                form.find(':input').not(':disabled')
-                    .attr('disabled', true)
-                    .addClass(settings.namespace + '_disabled');
-                $.each(formData, function (index, field) {
-                    $('<input type="hidden"/>')
-                        .attr('name', field.name)
-                        .val(field.value)
-                        .addClass(settings.namespace + '_form_data')
-                        .appendTo(form);
-                });
-                input
-                    .attr('name', getFieldName(settings))
-                    .appendTo(form);
-            },
-
-            legacyUploadFormDataReset = function (input, form, settings) {
-                input.detach();
-                form.find('.' + settings.namespace + '_disabled')
-                    .removeAttr('disabled')
-                    .removeClass(settings.namespace + '_disabled');
-                form.find('.' + settings.namespace + '_form_data').remove();
-            },
-
-            legacyUpload = function (event, files, input, form, iframe, settings, index) {
-                var send;
-                send = function () {
-                    if (typeof settings.onSend === func && settings.onSend(event, files, index, iframe, settings) === false) {
-                        completeNext();
-                        return;
-                    }
-                    var originalAttributes = {
-                        'action': form.attr('action'),
-                        'method': form.attr('method'),
-                        'target': form.attr('target'),
-                        'enctype': form.attr('enctype')
-                    };
-                    iframe
-                        .unbind('abort')
-                        .bind('abort', function (e) {
-                            iframe.readyState = 0;
-                            // javascript:false as iframe src prevents warning popups on HTTPS in IE6
-                            // concat is used here to prevent the "Script URL" JSLint error:
-                            iframe.unbind('load').attr('src', 'javascript'.concat(':false;'));
-                            handleLegacyGlobalProgress(e, files, index, iframe, settings);
-                            if (typeof settings.onAbort === func) {
-                                settings.onAbort(e, files, index, iframe, settings);
-                            }
-                            completeNext();
-                        })
-                        .unbind('load')
-                        .bind('load', function (e) {
-                            iframe.readyState = 4;
-                            handleLegacyGlobalProgress(e, files, index, iframe, settings);
-                            if (typeof settings.onLoad === func) {
-                                settings.onLoad(e, files, index, iframe, settings);
-                            }
-                            // Fix for IE endless progress bar activity bug
-                            // (happens on form submits to iframe targets):
-                            $('<iframe src="javascript:false;" style="display:none;"></iframe>')
-                                .appendTo(form).remove();
-                            completeNext();
-                        });
-                    form
-                        .attr('action', getUrl(settings))
-                        .attr('method', getMethod(settings))
-                        .attr('target', iframe.attr('name'))
-                        .attr('enctype', 'multipart/form-data');
-                    legacyUploadFormDataInit(input, form, settings);
-                    iframe.readyState = 2;
-                    form.get(0).submit();
-                    legacyUploadFormDataReset(input, form, settings);
-                    $.each(originalAttributes, function (name, value) {
-                        if (value) {
-                            form.attr(name, value);
-                        } else {
-                            form.removeAttr(name);
-                        }
-                    });
-                };
-                multiLoader.push([files, index, iframe, settings]);
-                if (settings.sequentialUploads) {
-                    sequenceHandler.push(send);
-                } else {
-                    send();
-                }
-            },
-
-            normalizeFile = function (index, file) {
-                if (typeof file.name === undef && typeof file.size === undef) {
-                    file.name = file.fileName;
-                    file.size = file.fileSize;
-                }
-            },
-
-            handleLegacyUpload = function (event, input, form, index) {
-                if (!(event && input && form)) {
-                    $.error('Iframe based File Upload requires a file input change event');
-                    return;
-                }
-                // javascript:false as iframe src prevents warning popups on HTTPS in IE6:
-                var iframe = $('<iframe src="javascript:false;" style="display:none;" name="iframe_' +
-                    settings.namespace + '_' + (new Date()).getTime() + '"></iframe>'),
-                    uploadSettings = $.extend({}, settings),
-                    files = event.target && event.target.files;
-                files = files ? Array.prototype.slice.call(files, 0) : [{name: input.val(), type: null, size: null}];
-                $.each(files, normalizeFile);
-                index = files.length === 1 ? 0 : index;
-                uploadSettings.fileInput = input;
-                uploadSettings.uploadForm = form;
-                iframe.readyState = 0;
-                iframe.abort = function () {
-                    iframe.trigger('abort');
-                };
-                iframe.bind('load', function () {
-                    iframe.unbind('load');
-                    if (typeof uploadSettings.initUpload === func) {
-                        uploadSettings.initUpload(
-                            event,
-                            files,
-                            index,
-                            iframe,
-                            uploadSettings,
-                            function () {
-                                legacyUpload(event, files, input, form, iframe, uploadSettings, index);
-                            }
-                        );
-                    } else {
-                        legacyUpload(event, files, input, form, iframe, uploadSettings, index);
-                    }
-                }).appendTo(form);
-            },
-
-            canHandleXHRUploadSize = function (files) {
-                var bytes = 0,
-                    totalBytes = 0,
-                    i;
-                if (settings.multipart && typeof FormData === undef) {
-                    for (i = 0; i < files.length; i += 1) {
-                        bytes = files[i].size;
-                        if (bytes > settings.maxFileReaderSize) {
-                            return false;
-                        }
-                        totalBytes += bytes;
-                    }
-                    if (settings.multiFileRequest && totalBytes > settings.maxFileReaderSize) {
-                        return false;
-                    }
-                }
-                return true;
-            },
-
-            handleFiles = function (event, files, input, form) {
-                if (!canHandleXHRUploadSize(files)) {
-                    handleLegacyUpload(event, input, form);
-                    return;
-                }
-                var i;
-                files = Array.prototype.slice.call(files, 0);
-                $.each(files, normalizeFile);
-                if (settings.multiFileRequest && settings.multipart && files.length) {
-                    handleUpload(event, files, input, form);
-                } else {
-                    for (i = 0; i < files.length; i += 1) {
-                        handleUpload(event, files, input, form, i);
-                    }
-                }
-            },
-            
-            initUploadForm = function () {
-                uploadForm = (container.is('form') ? container : container.find('form'))
-                    .filter(settings.uploadFormFilter);
-            },
-            
-            initFileInput = function () {
-                fileInput = (uploadForm.length ? uploadForm : container).find('input:file')
-                    .filter(settings.fileInputFilter);
-            },
-            
-            replaceFileInput = function (input) {
-                var inputClone = input.clone(true);
-                $('<form/>').append(inputClone).get(0).reset();
-                input.after(inputClone).detach();
-                initFileInput();
-            };
-
-        this.onDocumentDragOver = function (e) {
-            if (typeof settings.onDocumentDragOver === func &&
-                    settings.onDocumentDragOver(e) === false) {
-                return false;
-            }
-            e.preventDefault();
-        };
-        
-        this.onDocumentDrop = function (e) {
-            if (typeof settings.onDocumentDrop === func &&
-                    settings.onDocumentDrop(e) === false) {
-                return false;
-            }
-            e.preventDefault();
-        };
-
-        this.onDragOver = function (e) {
-            if (typeof settings.onDragOver === func &&
-                    settings.onDragOver(e) === false) {
-                return false;
-            }
-            var dataTransfer = e.originalEvent.dataTransfer;
-            if (dataTransfer && dataTransfer.files) {
-                dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy';
-                e.preventDefault();
-            }
-        };
-
-        this.onDrop = function (e) {
-            if (typeof settings.onDrop === func &&
-                    settings.onDrop(e) === false) {
-                return false;
-            }
-            var dataTransfer = e.originalEvent.dataTransfer;
-            if (dataTransfer && dataTransfer.files && isXHRUploadCapable()) {
-                handleFiles(e, dataTransfer.files);
-            }
-            e.preventDefault();
-        };
-        
-        this.onChange = function (e) {
-            if (typeof settings.onChange === func &&
-                    settings.onChange(e) === false) {
-                return false;
-            }
-            var input = $(e.target),
-                form = $(e.target.form);
-            if (form.length === 1) {
-                if (settings.replaceFileInput) {
-                    input.data(defaultNamespace + '_form', form);
-                    replaceFileInput(input);
-                }
-            } else {
-                form = input.data(defaultNamespace + '_form');
-            }
-            if (!settings.forceIframeUpload && e.target.files && isXHRUploadCapable()) {
-                handleFiles(e, e.target.files, input, form);
-            } else {
-                handleLegacyUpload(e, input, form);
-            }
-        };
-
-        this.init = function (options) {
-            if (options) {
-                $.extend(settings, options);
-                optionsReference = options;
-            }
-            initUploadForm();
-            initFileInput();
-            if (container.data(settings.namespace)) {
-                $.error('FileUpload with namespace "' + settings.namespace + '" already assigned to this element');
-                return;
-            }
-            container
-                .data(settings.namespace, fileUpload)
-                .addClass(settings.cssClass);
-            settings.dropZone.not(container).addClass(settings.cssClass);
-            initEventHandlers();
-            if (typeof settings.init === func) {
-                settings.init();
-            }
-        };
-
-        this.options = function (options) {
-            var oldCssClass,
-                oldDropZone,
-                uploadFormFilterUpdate,
-                fileInputFilterUpdate;
-            if (typeof options === undef) {
-                return $.extend({}, settings);
-            }
-            if (optionsReference) {
-                $.extend(optionsReference, options);
-            }
-            removeEventHandlers();
-            $.each(options, function (name, value) {
-                switch (name) {
-                case 'namespace':
-                    $.error('The FileUpload namespace cannot be updated.');
-                    return;
-                case 'uploadFormFilter':
-                    uploadFormFilterUpdate = true;
-                    fileInputFilterUpdate = true;
-                    break;
-                case 'fileInputFilter':
-                    fileInputFilterUpdate = true;
-                    break;
-                case 'cssClass':
-                    oldCssClass = settings.cssClass;
-                    break;
-                case 'dropZone':
-                    oldDropZone = settings.dropZone;
-                    break;
-                }
-                settings[name] = value;
-            });
-            if (uploadFormFilterUpdate) {
-                initUploadForm();
-            }
-            if (fileInputFilterUpdate) {
-                initFileInput();
-            }
-            if (typeof oldCssClass !== undef) {
-                container
-                    .removeClass(oldCssClass)
-                    .addClass(settings.cssClass);
-                (oldDropZone ? oldDropZone : settings.dropZone).not(container)
-                    .removeClass(oldCssClass);
-                settings.dropZone.not(container).addClass(settings.cssClass);
-            } else if (oldDropZone) {
-                oldDropZone.not(container).removeClass(settings.cssClass);
-                settings.dropZone.not(container).addClass(settings.cssClass);
-            }
-            initEventHandlers();
-        };
-        
-        this.option = function (name, value) {
-            var options;
-            if (typeof value === undef) {
-                return settings[name];
-            }
-            options = {};
-            options[name] = value;
-            fileUpload.options(options);
-        };
-        
-        this.destroy = function () {
-            if (typeof settings.destroy === func) {
-                settings.destroy();
-            }
-            removeEventHandlers();
-            container
-                .removeData(settings.namespace)
-                .removeClass(settings.cssClass);
-            settings.dropZone.not(container).removeClass(settings.cssClass);
-        };
-        
-        this.upload = function (files) {
-            if (typeof files.length === undef) {
-                files = [files];
-            }
-            handleFiles(null, files);
-        };
-    };
-
-    methods = {
-        init : function (options) {
-            return this.each(function () {
-                (new FileUpload($(this))).init(options);
-            });
-        },
-        
-        option: function (option, value, namespace) {
-            namespace = namespace ? namespace : defaultNamespace;
-            var fileUpload = $(this).data(namespace);
-            if (fileUpload) {
-                if (!option) {
-                    return fileUpload.options();
-                } else if (typeof option === 'string' && typeof value === undef) {
-                    return fileUpload.option(option);
-                }
-            } else {
-                $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
-            }
-            return this.each(function () {
-                var fu = $(this).data(namespace);
-                if (fu) {
-                    if (typeof option === 'string') {
-                        fu.option(option, value);
-                    } else {
-                        fu.options(option);
-                    }
-                } else {
-                    $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
-                }
-            });
-        },
-                
-        destroy: function (namespace) {
-            namespace = namespace ? namespace : defaultNamespace;
-            return this.each(function () {
-                var fileUpload = $(this).data(namespace);
-                if (fileUpload) {
-                    fileUpload.destroy();
-                } else {
-                    $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
-                }
-            });
-        },
-        
-        upload: function (files, namespace) {
-            namespace = namespace ? namespace : defaultNamespace;
-            return this.each(function () {
-                var fileUpload = $(this).data(namespace);
-                if (fileUpload) {
-                    fileUpload.upload(files);
-                } else {
-                    $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
-                }
-            });
-        }
-    };
-    
-    $.fn.fileUpload = function (method) {
-        if (methods[method]) {
-            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
-        } else if (typeof method === 'object' || !method) {
-            return methods.init.apply(this, arguments);
-        } else {
-            $.error('Method "' + method + '" does not exist on jQuery.fileUpload');
-        }
-    };
-    
-}(jQuery));

+ 2 - 1
src/Chamilo/CoreBundle/Resources/config/bower.json

@@ -25,7 +25,8 @@
     "MathJax": "2.5.*",
     "webcamjs": "1.0.*",
     "cropper": "1.0.*",
-    "jquery.scrollbar": "0.2.*"
+    "jquery.scrollbar": "0.2.*",
+    "jquery-file-upload": "*"
   },
   "resolutions": {
     "jquery": "2.1.4"

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini