├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── canvas.min.js ├── cute.png ├── filesaver.min.js ├── jszip.min.js ├── ybat.css ├── ybat.html └── ybat.js /.eslintignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | canvas.min.js 3 | jszip.min.js 4 | filesaver.min.js 5 | node_modules/ -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "browser": true 5 | }, 6 | "globals": { 7 | "Canvas": true, 8 | "JSZip": true, 9 | "saveAs": true 10 | }, 11 | "extends": "eslint:recommended", 12 | "parserOptions": { 13 | "sourceType": "script" 14 | }, 15 | "rules": { 16 | "max-len": [ 17 | "error", 18 | 120, 19 | 4 20 | ], 21 | "indent": [ 22 | "error", 23 | 4, 24 | { 25 | "SwitchCase": 1 26 | } 27 | ], 28 | "one-var": [ 29 | 2, 30 | "never" 31 | ], 32 | "linebreak-style": [ 33 | "error", 34 | "unix" 35 | ], 36 | "quotes": [ 37 | "error", 38 | "double" 39 | ], 40 | "semi": [ 41 | "error", 42 | "never" 43 | ], 44 | "eqeqeq": 2, 45 | "callback-return": 2, 46 | "handle-callback-err": 2, 47 | "no-mixed-requires": 2, 48 | "no-new-require": 2, 49 | "no-tabs": 2, 50 | "no-continue": 2, 51 | "brace-style": 2, 52 | "camelcase": 2, 53 | "comma-style": 2, 54 | "func-style": 2, 55 | "newline-after-var": 2, 56 | "newline-before-return": 2, 57 | "newline-per-chained-call": 2, 58 | "object-property-newline": 2, 59 | "object-curly-newline": 2, 60 | "object-curly-spacing": 2, 61 | "comma-dangle": 2, 62 | "array-bracket-spacing": 2, 63 | "block-spacing": 2, 64 | "computed-property-spacing": 2, 65 | "func-call-spacing": 2, 66 | "key-spacing": 2, 67 | "keyword-spacing": 2, 68 | "new-parens": 2 69 | } 70 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 2019-12-02, v0.2.5 2 | Renamed project to make it more fit for professional environment. 3 | Replaced sample image. 4 | 5 | # 2019-04-08, v0.2.4 6 | * Fixed VOC format not exporting objects correctly. 7 | * Updated copyrights. 8 | 9 | # 2018-12-31, v0.2.3 10 | * Fixed a potential bbox shift when loading bboxes from file. 11 | 12 | # 2018-12-31, v0.2.2 13 | * Fixed a warning if Recovery is not supported. 14 | * Added missing changelog from last time :) 15 | 16 | # 2018-12-30, v0.2.1 17 | * Added a warning if Recovery is not supported. 18 | 19 | # 2018-06-26, v0.2.0 20 | * **NEW! Basic Pascal VOC and COCO format support. EXPERIMENTAL!** 21 | * Some text clarifications. 22 | * Fixed a bug with uppercase extension names on images not working. 23 | 24 | # 2018-06-24, v0.1.9 25 | * Fixed issue with newlines in classes for different OS. 26 | 27 | # 2018-03-16, v0.1.8 28 | * Some minor refactoring. 29 | * Added ability to fit image into screen. Configurable. 30 | * Fixed bug with images resetting upon not selecting anything from file window. 31 | * Added detailed explanations on the configurable parameters. 32 | 33 | # 2018-03-13, v0.1.7 34 | * Changed middle dot to a cross sign. Also made it configurable. 35 | * Added guidelines for cursor. Configurable. 36 | * Updated README.md. 37 | 38 | # 2018-03-04, v0.1.6 39 | * Fixed bbox dimension display being affected by zoom. 40 | * Added a middle dot in bboxes for easier center approximation. 41 | 42 | # 2018-03-01, v0.1.5 43 | * Fixed canvas not resetting on image mouse select. 44 | * Fixed classes and bboxes not reloading on selecting the same files. 45 | * Added image and current bbox information. 46 | * Updated README.md. 47 | 48 | # 2018-02-19, v0.1.4 49 | * Fixed bbox select form name. Not sure if it caused problems. 50 | * Made sure cursor is shown as busy upon crop. (Doesn't work properly - this needs a proper "wait" spinner). 51 | * Fixed crops not working on Linux. 52 | * Updated README.md. 53 | 54 | # 2018-02-18, v0.1.3 55 | MAJOR: 56 | * Added ability to crop and saves images from bboxes (experimental). 57 | * Added FileSaver to better handle file downloading. 58 | * Fixed issue where MacBook DELETE key wouldn't work. 59 | 60 | MINOR: 61 | * Renamed labels.zip to bboxes.zip. 62 | * Updated screenshot to reflect changes. 63 | * Fixed previous version CHANGELOG typo. 64 | * Added version to footer. 65 | 66 | # 2018-02-17, v0.1.2 67 | 68 | * Fixed CHANGELOG formatting. 69 | * Fixed select box not auto scrolling to selection. 70 | * Made sure coordinate pixels are without decimal point. 71 | * Canvas now resets on image change to original zoom and position (can be turned off). 72 | * Implemented rudimentary image search by name. 73 | * Added ability to upload unzipped and/or multiple bboxes. 74 | * Fixed mouse cursor not resetting on bbox delete. 75 | * Updated README.md and screenshot to reflect changes. 76 | 77 | # 2018-02-15, v0.1.1 78 | 79 | * Fixed, so that canvas left offset doesn't cause misplacement of bboxes. 80 | * Fixed, that errors won't be thrown in console if image doesn't exist for a bbox. 81 | 82 | # 2018-02-14, v0.1.0 83 | 84 | * Initial release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2019 Draining Sun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ybat - YOLO BBox Annotation Tool 2 | Fast and efficient BBox annotation for your images in YOLO, and now, VOC/COCO formats! 3 | 4 | ## INTRO 5 | To see why and for what this was created, please read [Ybat - YOLO BBox Annotation Tool](https://medium.com/@drainingsun/ybat-yolo-bbox-annotation-tool-96fb765d0036) 6 | 7 | ![Sample](cute.png) 8 | 9 | ## USAGE 10 | 1. Download the zip. 11 | 2. Extract it. 12 | 3. Open `ybat.html` in your browser. 13 | 4. Load images and classes and start bboxing! 14 | 15 | ## CONFIGURATION 16 | 1. Open ybat.js. 17 | 2. Edit section named `parameters`. 18 | 19 | ## COMPATIBILITY 20 | All browsers that support ES6 should work. Tested with: 21 | 22 | * Chrome v65 23 | * Firefox v58 24 | * Safari v11 25 | * Opera v51 26 | 27 | No idea about IE/Edge. 28 | 29 | ## FEATURES 30 | * **NEW! Basic Pascal VOC and COCO format support.** 31 | * Works in your browser on any platform. 32 | * Complete YOLO format support. 33 | * No need for image upload - everything is done locally! 34 | * Zooming and panning images with guidelines for precise bboxing. 35 | * Fast navigation for quick bboxing. 36 | * Auto save in memory in case of accidental refreshes and crashes. 37 | * Ability to crop your bboxes and save the resulting images. 38 | * Information on both image and current bbox. 39 | 40 | ## CAVEATS 41 | * Loading many and or big images might take a while. This is because tool needs to figure out image dimensions. 42 | * Cropping many items might crash your browser. This and above will be fixed at some point. 43 | 44 | ## CONTRIBUTING 45 | Go nuts! Just don't forget to follow eslint guidelines. Credit will be given where it's due. -------------------------------------------------------------------------------- /canvas.min.js: -------------------------------------------------------------------------------- 1 | (function(window){!function(e){"use strict";var n,t=0,a=["moz","webkit","o","ms"];for(n=0;nthis.framerateLimit)return;this._lastFrame=now;if(!this._paused)this.fireEvent('update',this._HANDYOBJECT_,delta,Canvas.now());this.renderCanvasElements();this.fireEvent('draw',this.renderer,this._HANDYOBJECT_,delta,Canvas.now())} 9 | drawFrame(fn){fn.bind(this)(this.renderer,this._HANDYOBJECT_)} 10 | start(fn){if(this._running)return;this._running=!0;this._lastFrame=Canvas.now()-16;this.fireEvent('start',this.renderer,this._HANDYOBJECT_,Canvas.now());if(fn)fn.apply(this,[this.renderer,this._HANDYOBJECT_,Canvas.now()]);this._loop();return this} 11 | stop(fn){if(!this._running)return;this._running=!1;this.fireEvent('stop',this.renderer,this._HANDYOBJECT_,Canvas.now());if(fn)fn.apply(this,[this.renderer,this._HANDYOBJECT_,Canvas.now()]);window.cancelAnimationFrame(this._rafid);return this} 12 | pause(fn){if(this._paused)return;this._paused=!0;this.fireEvent('pause',this.renderer,this._HANDYOBJECT_,Canvas.now());if(fn)fn.apply(this,[this.renderer,this._HANDYOBJECT_,Canvas.now()]);return this} 13 | resume(fn){if(!this._paused)return;this._paused=!1;this.fireEvent('resume',this.renderer,this._HANDYOBJECT_,Canvas.now());if(fn)fn.apply(this,[this.renderer,this._HANDYOBJECT_,Canvas.now()]);return this} 14 | setFramerateLimit(limit){this.framerateLimit=limit} 15 | balanceFramerate(bool,min,max){this.balanceFramerate=bool;if(min)this._balanceFramerateMin=min;if(max)this._balanceFramerateMax=max} 16 | toImage(format){format=format||'png';return this.element.toDataURL('image/'+format)} 17 | saveToStorage(key){key=key||'Canvas';window.localStorage.setItem('canvas-'+key,this.toImage())} 18 | removeFromStorage(key){key=key||'Canvas';const base64=window.localStorage.getItem('canvas-'+key);if(!base64)return!1;window.localStorage.removeItem('canvas-'+key);return!0} 19 | restoreFromStorage(key){key=key||'Canvas';const base64=window.localStorage.getItem('canvas-'+key);if(!base64)return!1;const image=new Image();image.src=base64;image.onload=function(){this.drawFrame(function(ctx){ctx.drawImage(image,0,0)})}.bind(this);return!0} 20 | getObject(){return this._HANDYOBJECT_} 21 | saveObject(key){key=key||'HO';window.localStorage.setItem('canvas-'+key,JSON.stringify(this.getObject()))} 22 | loadObject(key){key=key||'HO';const handyObject=JSON.parse(window.localStorage.getItem('canvas-'+key));if(!handyObject||typeof handyObject!=='object')return!1;this._HANDYOBJECT_=handyObject;return this._HANDYOBJECT_} 23 | removeSavedObject(key){key=key||'HO';const handyObject=window.localStorage.getItem('canvas-'+key);if(!handyObject)return!1;window.localStorage.removeItem('canvas-'+key);return!0} 24 | addElement(canvasElement){this.canvasElements.push(canvasElement)} 25 | removeElement(canvasElement){this.canvasElements.splice(this.canvasElements.indexOf(canvasElement),1)} 26 | getElements(){return this.canvasElements} 27 | renderCanvasElements(){const renderer=this.renderer;renderer.clear();if(this.canvasElements.length)renderer.clear();for(let element of this.canvasElements){if(element.animation){const ratio=(Canvas.now()-element.animation.start)/(element.animation.end-element.animation.start);if(ratio>=1){element.animation=!1}else{switch(element.animation.type){case 'move':element.x=Canvas.lerp(element.animation.from.x,element.animation.to.x,ratio);element.y=Canvas.lerp(element.animation.from.y,element.animation.to.y,ratio);break;case 'lineLength':element.lineLength=Canvas.lerp(element.animation.from.lineLength,element.animation.to.lineLength,ratio)}}} 28 | switch(element.constructor.name){case 'CanvasImage':if(!element.element)continue;if(element.sourceX) 29 | renderer.drawImage(element.element,element.sourceX,element.sourceY,element.sourceWidth,element.sourceHeight,element.x,element.y,element.width,element.height);else renderer.drawImage(element.element,element.x,element.y,element.width,element.height);break;case 'CanvasLine':renderer.save();renderer.beginPath();renderer.moveTo(element.from.x,element.from.y);renderer.lineTo(Canvas.lerp(element.from.x,element.to.x,element.lineLength),Canvas.lerp(element.from.y,element.to.y,element.lineLength));renderer.lineWidth=element.lineWidth;renderer.lineCap=element.lineCap;renderer.stroke();renderer.restore()}}}};Canvas.lerp=function(v1,v2,t){return(1-t)*v1+t*v2};Canvas.now=function(){if('performance' in window&&'now' in window.performance){return performance.now()} 30 | return+new Date};Canvas.Vector=class Vector{constructor(){this.pos={};this.pos.x=arguments[0];this.pos.y=arguments[1];this.pos.z=arguments[2];this.rotation={};this.rotation.x=arguments[3];this.rotation.y=arguments[4];this.rotation.z=arguments[5]} 31 | mult(vector){this.pos.x*=vector.pos.x;this.pos.y*=vector.pos.y;this.pos.z*=vector.pos.z;this.pos.x*=vector.rotation.x;this.pos.y*=vector.rotation.y;this.pos.z*=vector.rotation.z} 32 | add(vector){this.pos.x+=vector.pos.x;this.pos.y+=vector.pos.y;this.pos.z+=vector.pos.z;this.pos.x+=vector.rotation.x;this.pos.y+=vector.rotation.y;this.pos.z+=vector.rotation.z}} 33 | Canvas.Element=class Element{constructor(){const args=arguments[0]||{};this.x=typeof args.x!=='undefined'?args.x:null;this.y=typeof args.y!=='undefined'?args.y:null;this.width=typeof args.width!=='undefined'?args.width:null;this.height=typeof args.height!=='undefined'?args.height:null;this.rotation=typeof args.rotation!=='undefined'?args.rotation:null;this.animation=!1} 34 | animate(){const type=arguments[0];const args=arguments[1];switch(type){case 'move':this.animation={type:type,from:{x:this.x,y:this.y},to:{x:args.x,y:args.y}};break;case 'lineLength':this.animation={type:type,from:{lineLength:this.lineLength},to:{lineLength:args.lineLength}};break;default:return!1};this.animation.start=Canvas.now();this.animation.end=this.animation.start+args.duration}};Canvas.Image=class CanvasImage extends Canvas.Element{constructor(){const args=arguments[0]||{};super(args);this.element=(function(){if(args.image) 35 | return args.image;else{const image=new Image();image.src=args.url;return image}})();this.sourceX=args.sourceX||null;this.sourceY=args.sourceY||null;this.sourceWidth=args.sourceWidth||null;this.sourceHeight=args.sourceHeight||null;this.element.onload=function(){if(this.width){this.element.width=this.width}else this.width=this.element.width;if(this.height){this.element.height=this.height}else this.height=this.element.height;this.sourceX=args.sourceX||0;this.sourceY=args.sourceY||0;this.sourceWidth=args.sourceWidth||this.width;this.sourceHeight=args.sourceHeight||this.height;if(typeof arguments[1]=='function')arguments[1]()}.bind(this)}};Canvas.Line=class CanvasLine extends Canvas.Element{constructor(){const args=arguments[0]||{};super(args);this.from={x:args.from.x,y:args.from.y};this.to={x:args.to.x,y:args.to.y};this.lineWidth=args.lineWidth||2;this.lineCap=args.lineCap||'butt';this.lineLength=args.lineLength||0.5}}})(window) -------------------------------------------------------------------------------- /cute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drainingsun/ybat/41a27e20d36fa6f4056cd8b285cd16441a4f8023/cute.png -------------------------------------------------------------------------------- /filesaver.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 2 | var saveAs=saveAs||function(e){"use strict";if(typeof e==="undefined"||typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=t.createElementNS("http://www.w3.org/1999/xhtml","a"),o="download"in r,a=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},i=/constructor/i.test(e.HTMLElement)||e.safari,f=/CriOS\/[\d]+/.test(navigator.userAgent),u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},s="application/octet-stream",d=1e3*40,c=function(e){var t=function(){if(typeof e==="string"){n().revokeObjectURL(e)}else{e.remove()}};setTimeout(t,d)},l=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var o=e["on"+t[r]];if(typeof o==="function"){try{o.call(e,n||e)}catch(a){u(a)}}}},p=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob([String.fromCharCode(65279),e],{type:e.type})}return e},v=function(t,u,d){if(!d){t=p(t)}var v=this,w=t.type,m=w===s,y,h=function(){l(v,"writestart progress write writeend".split(" "))},S=function(){if((f||m&&i)&&e.FileReader){var r=new FileReader;r.onloadend=function(){var t=f?r.result:r.result.replace(/^data:[^;]*;/,"data:attachment/file;");var n=e.open(t,"_blank");if(!n)e.location.href=t;t=undefined;v.readyState=v.DONE;h()};r.readAsDataURL(t);v.readyState=v.INIT;return}if(!y){y=n().createObjectURL(t)}if(m){e.location.href=y}else{var o=e.open(y,"_blank");if(!o){e.location.href=y}}v.readyState=v.DONE;h();c(y)};v.readyState=v.INIT;if(o){y=n().createObjectURL(t);setTimeout(function(){r.href=y;r.download=u;a(r);h();c(y);v.readyState=v.DONE});return}S()},w=v.prototype,m=function(e,t,n){return new v(e,t||e.name||"download",n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(e,t,n){t=t||e.name||"download";if(!n){e=p(e)}return navigator.msSaveOrOpenBlob(e,t)}}w.abort=function(){};w.readyState=w.INIT=0;w.WRITING=1;w.DONE=2;w.error=w.onwritestart=w.onprogress=w.onwrite=w.onabort=w.onerror=w.onwriteend=null;return m}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!==null){define("FileSaver.js",function(){return saveAs})} 3 | -------------------------------------------------------------------------------- /jszip.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | JSZip v3.1.5 - A JavaScript class for generating and reading zip files 4 | 5 | 6 | (c) 2009-2016 Stuart Knightley 7 | Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown. 8 | 9 | JSZip uses the library pako released under the MIT license : 10 | https://github.com/nodeca/pako/blob/master/LICENSE 11 | */ 12 | !function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.JSZip=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g>2,h=(3&b)<<4|c>>4,i=n>1?(15&c)<<2|e>>6:64,j=n>2?63&e:64,k.push(f.charAt(g)+f.charAt(h)+f.charAt(i)+f.charAt(j));return k.join("")},c.decode=function(a){var b,c,d,g,h,i,j,k=0,l=0,m="data:";if(a.substr(0,m.length)===m)throw new Error("Invalid base64 input, it looks like a data url.");a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");var n=3*a.length/4;if(a.charAt(a.length-1)===f.charAt(64)&&n--,a.charAt(a.length-2)===f.charAt(64)&&n--,n%1!==0)throw new Error("Invalid base64 input, bad content length.");var o;for(o=e.uint8array?new Uint8Array(0|n):new Array(0|n);k>4,c=(15&h)<<4|i>>2,d=(3&i)<<6|j,o[l++]=b,64!==i&&(o[l++]=c),64!==j&&(o[l++]=d);return o}},{"./support":30,"./utils":32}],2:[function(a,b,c){"use strict";function d(a,b,c,d,e){this.compressedSize=a,this.uncompressedSize=b,this.crc32=c,this.compression=d,this.compressedContent=e}var e=a("./external"),f=a("./stream/DataWorker"),g=a("./stream/DataLengthProbe"),h=a("./stream/Crc32Probe"),g=a("./stream/DataLengthProbe");d.prototype={getContentWorker:function(){var a=new f(e.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new g("data_length")),b=this;return a.on("end",function(){if(this.streamInfo.data_length!==b.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),a},getCompressedWorker:function(){return new f(e.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},d.createWorkerFrom=function(a,b,c){return a.pipe(new h).pipe(new g("uncompressedSize")).pipe(b.compressWorker(c)).pipe(new g("compressedSize")).withStreamInfo("compression",b)},b.exports=d},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(a,b,c){"use strict";var d=a("./stream/GenericWorker");c.STORE={magic:"\0\0",compressWorker:function(a){return new d("STORE compression")},uncompressWorker:function(){return new d("STORE decompression")}},c.DEFLATE=a("./flate")},{"./flate":7,"./stream/GenericWorker":28}],4:[function(a,b,c){"use strict";function d(){for(var a,b=[],c=0;c<256;c++){a=c;for(var d=0;d<8;d++)a=1&a?3988292384^a>>>1:a>>>1;b[c]=a}return b}function e(a,b,c,d){var e=h,f=d+c;a^=-1;for(var g=d;g>>8^e[255&(a^b[g])];return a^-1}function f(a,b,c,d){var e=h,f=d+c;a^=-1;for(var g=d;g>>8^e[255&(a^b.charCodeAt(g))];return a^-1}var g=a("./utils"),h=d();b.exports=function(a,b){if("undefined"==typeof a||!a.length)return 0;var c="string"!==g.getTypeOf(a);return c?e(0|b,a,a.length,0):f(0|b,a,a.length,0)}},{"./utils":32}],5:[function(a,b,c){"use strict";c.base64=!1,c.binary=!1,c.dir=!1,c.createFolders=!0,c.date=null,c.compression=null,c.compressionOptions=null,c.comment=null,c.unixPermissions=null,c.dosPermissions=null},{}],6:[function(a,b,c){"use strict";var d=null;d="undefined"!=typeof Promise?Promise:a("lie"),b.exports={Promise:d}},{lie:58}],7:[function(a,b,c){"use strict";function d(a,b){h.call(this,"FlateWorker/"+a),this._pako=null,this._pakoAction=a,this._pakoOptions=b,this.meta={}}var e="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,f=a("pako"),g=a("./utils"),h=a("./stream/GenericWorker"),i=e?"uint8array":"array";c.magic="\b\0",g.inherits(d,h),d.prototype.processChunk=function(a){this.meta=a.meta,null===this._pako&&this._createPako(),this._pako.push(g.transformTo(i,a.data),!1)},d.prototype.flush=function(){h.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0)},d.prototype.cleanUp=function(){h.prototype.cleanUp.call(this),this._pako=null},d.prototype._createPako=function(){this._pako=new f[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var a=this;this._pako.onData=function(b){a.push({data:b,meta:a.meta})}},c.compressWorker=function(a){return new d("Deflate",a)},c.uncompressWorker=function(){return new d("Inflate",{})}},{"./stream/GenericWorker":28,"./utils":32,pako:59}],8:[function(a,b,c){"use strict";function d(a,b,c,d){f.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=b,this.zipPlatform=c,this.encodeFileName=d,this.streamFiles=a,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}var e=a("../utils"),f=a("../stream/GenericWorker"),g=a("../utf8"),h=a("../crc32"),i=a("../signature"),j=function(a,b){var c,d="";for(c=0;c>>=8;return d},k=function(a,b){var c=a;return a||(c=b?16893:33204),(65535&c)<<16},l=function(a,b){return 63&(a||0)},m=function(a,b,c,d,f,m){var n,o,p=a.file,q=a.compression,r=m!==g.utf8encode,s=e.transformTo("string",m(p.name)),t=e.transformTo("string",g.utf8encode(p.name)),u=p.comment,v=e.transformTo("string",m(u)),w=e.transformTo("string",g.utf8encode(u)),x=t.length!==p.name.length,y=w.length!==u.length,z="",A="",B="",C=p.dir,D=p.date,E={crc32:0,compressedSize:0,uncompressedSize:0};b&&!c||(E.crc32=a.crc32,E.compressedSize=a.compressedSize,E.uncompressedSize=a.uncompressedSize);var F=0;b&&(F|=8),r||!x&&!y||(F|=2048);var G=0,H=0;C&&(G|=16),"UNIX"===f?(H=798,G|=k(p.unixPermissions,C)):(H=20,G|=l(p.dosPermissions,C)),n=D.getUTCHours(),n<<=6,n|=D.getUTCMinutes(),n<<=5,n|=D.getUTCSeconds()/2,o=D.getUTCFullYear()-1980,o<<=4,o|=D.getUTCMonth()+1,o<<=5,o|=D.getUTCDate(),x&&(A=j(1,1)+j(h(s),4)+t,z+="up"+j(A.length,2)+A),y&&(B=j(1,1)+j(h(v),4)+w,z+="uc"+j(B.length,2)+B);var I="";I+="\n\0",I+=j(F,2),I+=q.magic,I+=j(n,2),I+=j(o,2),I+=j(E.crc32,4),I+=j(E.compressedSize,4),I+=j(E.uncompressedSize,4),I+=j(s.length,2),I+=j(z.length,2);var J=i.LOCAL_FILE_HEADER+I+s+z,K=i.CENTRAL_FILE_HEADER+j(H,2)+I+j(v.length,2)+"\0\0\0\0"+j(G,4)+j(d,4)+s+z+v;return{fileRecord:J,dirRecord:K}},n=function(a,b,c,d,f){var g="",h=e.transformTo("string",f(d));return g=i.CENTRAL_DIRECTORY_END+"\0\0\0\0"+j(a,2)+j(a,2)+j(b,4)+j(c,4)+j(h.length,2)+h},o=function(a){var b="";return b=i.DATA_DESCRIPTOR+j(a.crc32,4)+j(a.compressedSize,4)+j(a.uncompressedSize,4)};e.inherits(d,f),d.prototype.push=function(a){var b=a.meta.percent||0,c=this.entriesCount,d=this._sources.length;this.accumulate?this.contentBuffer.push(a):(this.bytesWritten+=a.data.length,f.prototype.push.call(this,{data:a.data,meta:{currentFile:this.currentFile,percent:c?(b+100*(c-d-1))/c:100}}))},d.prototype.openedSource=function(a){this.currentSourceOffset=this.bytesWritten,this.currentFile=a.file.name;var b=this.streamFiles&&!a.file.dir;if(b){var c=m(a,b,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:c.fileRecord,meta:{percent:0}})}else this.accumulate=!0},d.prototype.closedSource=function(a){this.accumulate=!1;var b=this.streamFiles&&!a.file.dir,c=m(a,b,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(c.dirRecord),b)this.push({data:o(a),meta:{percent:100}});else for(this.push({data:c.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null},d.prototype.flush=function(){for(var a=this.bytesWritten,b=0;b0?a.substring(0,b):""},q=function(a){return"/"!==a.slice(-1)&&(a+="/"),a},r=function(a,b){return b="undefined"!=typeof b?b:i.createFolders,a=q(a),this.files[a]||o.call(this,a,null,{dir:!0,createFolders:b}),this.files[a]},s={load:function(){throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.")},forEach:function(a){var b,c,d;for(b in this.files)this.files.hasOwnProperty(b)&&(d=this.files[b],c=b.slice(this.root.length,b.length),c&&b.slice(0,this.root.length)===this.root&&a(c,d))},filter:function(a){var b=[];return this.forEach(function(c,d){a(c,d)&&b.push(d)}),b},file:function(a,b,c){if(1===arguments.length){if(d(a)){var e=a;return this.filter(function(a,b){return!b.dir&&e.test(a)})}var f=this.files[this.root+a];return f&&!f.dir?f:null}return a=this.root+a,o.call(this,a,b,c),this},folder:function(a){if(!a)return this;if(d(a))return this.filter(function(b,c){return c.dir&&a.test(b)});var b=this.root+a,c=r.call(this,b),e=this.clone();return e.root=c.name,e},remove:function(a){a=this.root+a;var b=this.files[a];if(b||("/"!==a.slice(-1)&&(a+="/"),b=this.files[a]),b&&!b.dir)delete this.files[a];else for(var c=this.filter(function(b,c){return c.name.slice(0,a.length)===a}),d=0;d=0;--f)if(this.data[f]===b&&this.data[f+1]===c&&this.data[f+2]===d&&this.data[f+3]===e)return f-this.zero;return-1},d.prototype.readAndCheckSignature=function(a){var b=a.charCodeAt(0),c=a.charCodeAt(1),d=a.charCodeAt(2),e=a.charCodeAt(3),f=this.readData(4);return b===f[0]&&c===f[1]&&d===f[2]&&e===f[3]},d.prototype.readData=function(a){if(this.checkOffset(a),0===a)return[];var b=this.data.slice(this.zero+this.index,this.zero+this.index+a);return this.index+=a,b},b.exports=d},{"../utils":32,"./DataReader":18}],18:[function(a,b,c){"use strict";function d(a){this.data=a,this.length=a.length,this.index=0,this.zero=0}var e=a("../utils");d.prototype={checkOffset:function(a){this.checkIndex(this.index+a)},checkIndex:function(a){if(this.length=this.index;b--)c=(c<<8)+this.byteAt(b);return this.index+=a,c},readString:function(a){return e.transformTo("string",this.readData(a))},readData:function(a){},lastIndexOfSignature:function(a){},readAndCheckSignature:function(a){},readDate:function(){var a=this.readInt(4);return new Date(Date.UTC((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&31,a>>5&63,(31&a)<<1))}},b.exports=d},{"../utils":32}],19:[function(a,b,c){"use strict";function d(a){e.call(this,a)}var e=a("./Uint8ArrayReader"),f=a("../utils");f.inherits(d,e),d.prototype.readData=function(a){this.checkOffset(a);var b=this.data.slice(this.zero+this.index,this.zero+this.index+a);return this.index+=a,b},b.exports=d},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(a,b,c){"use strict";function d(a){e.call(this,a)}var e=a("./DataReader"),f=a("../utils");f.inherits(d,e),d.prototype.byteAt=function(a){return this.data.charCodeAt(this.zero+a)},d.prototype.lastIndexOfSignature=function(a){return this.data.lastIndexOf(a)-this.zero},d.prototype.readAndCheckSignature=function(a){var b=this.readData(4);return a===b},d.prototype.readData=function(a){this.checkOffset(a);var b=this.data.slice(this.zero+this.index,this.zero+this.index+a);return this.index+=a,b},b.exports=d},{"../utils":32,"./DataReader":18}],21:[function(a,b,c){"use strict";function d(a){e.call(this,a)}var e=a("./ArrayReader"),f=a("../utils");f.inherits(d,e),d.prototype.readData=function(a){if(this.checkOffset(a),0===a)return new Uint8Array(0);var b=this.data.subarray(this.zero+this.index,this.zero+this.index+a);return this.index+=a,b},b.exports=d},{"../utils":32,"./ArrayReader":17}],22:[function(a,b,c){"use strict";var d=a("../utils"),e=a("../support"),f=a("./ArrayReader"),g=a("./StringReader"),h=a("./NodeBufferReader"),i=a("./Uint8ArrayReader");b.exports=function(a){var b=d.getTypeOf(a);return d.checkSupport(b),"string"!==b||e.uint8array?"nodebuffer"===b?new h(a):e.uint8array?new i(d.transformTo("uint8array",a)):new f(d.transformTo("array",a)):new g(a)}},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(a,b,c){"use strict";c.LOCAL_FILE_HEADER="PK",c.CENTRAL_FILE_HEADER="PK",c.CENTRAL_DIRECTORY_END="PK",c.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",c.ZIP64_CENTRAL_DIRECTORY_END="PK",c.DATA_DESCRIPTOR="PK\b"},{}],24:[function(a,b,c){"use strict";function d(a){e.call(this,"ConvertWorker to "+a),this.destType=a}var e=a("./GenericWorker"),f=a("../utils");f.inherits(d,e),d.prototype.processChunk=function(a){this.push({data:f.transformTo(this.destType,a.data),meta:a.meta})},b.exports=d},{"../utils":32,"./GenericWorker":28}],25:[function(a,b,c){"use strict";function d(){e.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0)}var e=a("./GenericWorker"),f=a("../crc32"),g=a("../utils");g.inherits(d,e),d.prototype.processChunk=function(a){this.streamInfo.crc32=f(a.data,this.streamInfo.crc32||0),this.push(a)},b.exports=d},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(a,b,c){"use strict";function d(a){f.call(this,"DataLengthProbe for "+a),this.propName=a,this.withStreamInfo(a,0)}var e=a("../utils"),f=a("./GenericWorker");e.inherits(d,f),d.prototype.processChunk=function(a){if(a){var b=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=b+a.data.length}f.prototype.processChunk.call(this,a)},b.exports=d},{"../utils":32,"./GenericWorker":28}],27:[function(a,b,c){"use strict";function d(a){f.call(this,"DataWorker");var b=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,a.then(function(a){b.dataIsReady=!0,b.data=a,b.max=a&&a.length||0,b.type=e.getTypeOf(a),b.isPaused||b._tickAndRepeat()},function(a){b.error(a)})}var e=a("../utils"),f=a("./GenericWorker"),g=16384;e.inherits(d,f),d.prototype.cleanUp=function(){f.prototype.cleanUp.call(this),this.data=null},d.prototype.resume=function(){return!!f.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,e.delay(this._tickAndRepeat,[],this)),!0)},d.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(e.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},d.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var a=g,b=null,c=Math.min(this.max,this.index+a);if(this.index>=this.max)return this.end();switch(this.type){case"string":b=this.data.substring(this.index,c);break;case"uint8array":b=this.data.subarray(this.index,c);break;case"array":case"nodebuffer":b=this.data.slice(this.index,c)}return this.index=c,this.push({data:b,meta:{percent:this.max?this.index/this.max*100:0}})},b.exports=d},{"../utils":32,"./GenericWorker":28}],28:[function(a,b,c){"use strict";function d(a){this.name=a||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null}d.prototype={push:function(a){this.emit("data",a)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0}catch(a){this.emit("error",a)}return!0},error:function(a){return!this.isFinished&&(this.isPaused?this.generatedError=a:(this.isFinished=!0,this.emit("error",a),this.previous&&this.previous.error(a),this.cleanUp()),!0)},on:function(a,b){return this._listeners[a].push(b),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(a,b){if(this._listeners[a])for(var c=0;c "+a:a}},b.exports=d},{}],29:[function(a,b,c){"use strict";function d(a,b,c){switch(a){case"blob":return h.newBlob(h.transformTo("arraybuffer",b),c);case"base64":return k.encode(b);default:return h.transformTo(a,b)}}function e(a,b){var c,d=0,e=null,f=0;for(c=0;c=252?6:k>=248?5:k>=240?4:k>=224?3:k>=192?2:1;j[254]=j[254]=1;var l=function(a){var b,c,d,e,f,h=a.length,i=0;for(e=0;e>>6,b[f++]=128|63&c):c<65536?(b[f++]=224|c>>>12,b[f++]=128|c>>>6&63,b[f++]=128|63&c):(b[f++]=240|c>>>18,b[f++]=128|c>>>12&63,b[f++]=128|c>>>6&63,b[f++]=128|63&c);return b},m=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return c<0?b:0===c?b:c+j[a[c]]>b?c:b},n=function(a){var b,c,d,e,g=a.length,h=new Array(2*g);for(c=0,b=0;b4)h[c++]=65533,b+=e-1;else{for(d&=2===e?31:3===e?15:7;e>1&&b1?h[c++]=65533:d<65536?h[c++]=d:(d-=65536,h[c++]=55296|d>>10&1023,h[c++]=56320|1023&d)}return h.length!==c&&(h.subarray?h=h.subarray(0,c):h.length=c),f.applyFromCharCode(h)};c.utf8encode=function(a){return g.nodebuffer?h.newBufferFrom(a,"utf-8"):l(a)},c.utf8decode=function(a){return g.nodebuffer?f.transformTo("nodebuffer",a).toString("utf-8"):(a=f.transformTo(g.uint8array?"uint8array":"array",a),n(a))},f.inherits(d,i),d.prototype.processChunk=function(a){var b=f.transformTo(g.uint8array?"uint8array":"array",a.data);if(this.leftOver&&this.leftOver.length){if(g.uint8array){var d=b;b=new Uint8Array(d.length+this.leftOver.length),b.set(this.leftOver,0),b.set(d,this.leftOver.length)}else b=this.leftOver.concat(b);this.leftOver=null}var e=m(b),h=b;e!==b.length&&(g.uint8array?(h=b.subarray(0,e),this.leftOver=b.subarray(e,b.length)):(h=b.slice(0,e),this.leftOver=b.slice(e,b.length))),this.push({data:c.utf8decode(h),meta:a.meta})},d.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:c.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},c.Utf8DecodeWorker=d,f.inherits(e,i),e.prototype.processChunk=function(a){this.push({data:c.utf8encode(a.data),meta:a.meta})},c.Utf8EncodeWorker=e},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(a,b,c){"use strict";function d(a){var b=null;return b=i.uint8array?new Uint8Array(a.length):new Array(a.length),f(a,b)}function e(a){return a}function f(a,b){for(var c=0;c1;)try{return n.stringifyByChunk(a,d,b)}catch(f){b=Math.floor(b/2)}return n.stringifyByChar(a)}function h(a,b){for(var c=0;c1)throw new Error("Multi-volumes zip are not supported")},readLocalFiles:function(){var a,b;for(a=0;a0)this.isSignature(c,g.CENTRAL_FILE_HEADER)||(this.reader.zero=e);else if(e<0)throw new Error("Corrupted zip: missing "+Math.abs(e)+" bytes.")},prepareReader:function(a){this.reader=e(a)},load:function(a){this.prepareReader(a),this.readEndOfCentral(),this.readCentralDir(),this.readLocalFiles()}},b.exports=d},{"./reader/readerFor":22,"./signature":23,"./support":30,"./utf8":31,"./utils":32,"./zipEntry":34}],34:[function(a,b,c){"use strict";function d(a,b){this.options=a,this.loadOptions=b}var e=a("./reader/readerFor"),f=a("./utils"),g=a("./compressedObject"),h=a("./crc32"),i=a("./utf8"),j=a("./compressions"),k=a("./support"),l=0,m=3,n=function(a){for(var b in j)if(j.hasOwnProperty(b)&&j[b].magic===a)return j[b];return null};d.prototype={isEncrypted:function(){return 1===(1&this.bitFlag)},useUTF8:function(){return 2048===(2048&this.bitFlag)},readLocalPart:function(a){var b,c;if(a.skip(22),this.fileNameLength=a.readInt(2),c=a.readInt(2),this.fileName=a.readData(this.fileNameLength),a.skip(c),this.compressedSize===-1||this.uncompressedSize===-1)throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory (compressedSize === -1 || uncompressedSize === -1)");if(b=n(this.compressionMethod),null===b)throw new Error("Corrupted zip : compression "+f.pretty(this.compressionMethod)+" unknown (inner file : "+f.transformTo("string",this.fileName)+")");this.decompressed=new g(this.compressedSize,this.uncompressedSize,this.crc32,b,a.readData(this.compressedSize))},readCentralPart:function(a){this.versionMadeBy=a.readInt(2),a.skip(2),this.bitFlag=a.readInt(2),this.compressionMethod=a.readString(2),this.date=a.readDate(),this.crc32=a.readInt(4),this.compressedSize=a.readInt(4),this.uncompressedSize=a.readInt(4);var b=a.readInt(2);if(this.extraFieldsLength=a.readInt(2),this.fileCommentLength=a.readInt(2),this.diskNumberStart=a.readInt(2),this.internalFileAttributes=a.readInt(2),this.externalFileAttributes=a.readInt(4),this.localHeaderOffset=a.readInt(4),this.isEncrypted())throw new Error("Encrypted zip are not supported");a.skip(b),this.readExtraFields(a),this.parseZIP64ExtraField(a),this.fileComment=a.readData(this.fileCommentLength)},processAttributes:function(){this.unixPermissions=null,this.dosPermissions=null;var a=this.versionMadeBy>>8;this.dir=!!(16&this.externalFileAttributes),a===l&&(this.dosPermissions=63&this.externalFileAttributes),a===m&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileNameStr.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(a){if(this.extraFields[1]){var b=e(this.extraFields[1].value);this.uncompressedSize===f.MAX_VALUE_32BITS&&(this.uncompressedSize=b.readInt(8)),this.compressedSize===f.MAX_VALUE_32BITS&&(this.compressedSize=b.readInt(8)),this.localHeaderOffset===f.MAX_VALUE_32BITS&&(this.localHeaderOffset=b.readInt(8)),this.diskNumberStart===f.MAX_VALUE_32BITS&&(this.diskNumberStart=b.readInt(4))}},readExtraFields:function(a){var b,c,d,e=a.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});a.indexc;)b.push(arguments[c++]);return q[++p]=function(){h("function"==typeof a?a:Function(a),b)},d(p),p},n=function(a){delete q[a]},"process"==a("./_cof")(l)?d=function(a){l.nextTick(g(s,a,1))}:o?(e=new o,f=e.port2,e.port1.onmessage=t,d=g(f.postMessage,f,1)):k.addEventListener&&"function"==typeof postMessage&&!k.importScripts?(d=function(a){k.postMessage(a+"","*")},k.addEventListener("message",t,!1)):d=r in j("script")?function(a){i.appendChild(j("script"))[r]=function(){i.removeChild(this),s.call(a)}}:function(a){setTimeout(g(s,a,1),0)}),b.exports={set:m,clear:n}},{"./_cof":39,"./_ctx":41,"./_dom-create":43,"./_global":46,"./_html":48,"./_invoke":50}],55:[function(a,b,c){var d=a("./_is-object");b.exports=function(a,b){if(!d(a))return a;var c,e;if(b&&"function"==typeof(c=a.toString)&&!d(e=c.call(a)))return e;if("function"==typeof(c=a.valueOf)&&!d(e=c.call(a)))return e;if(!b&&"function"==typeof(c=a.toString)&&!d(e=c.call(a)))return e;throw TypeError("Can't convert object to primitive value")}},{"./_is-object":51}],56:[function(a,b,c){var d=a("./_export"),e=a("./_task");d(d.G+d.B,{setImmediate:e.set,clearImmediate:e.clear})},{"./_export":44,"./_task":54}],57:[function(a,b,c){(function(a){"use strict";function c(){k=!0;for(var a,b,c=l.length;c;){for(b=l,l=[],a=-1;++a0?b.windowBits=-b.windowBits:b.gzip&&b.windowBits>0&&b.windowBits<16&&(b.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new l,this.strm.avail_out=0;var c=h.deflateInit2(this.strm,b.level,b.method,b.windowBits,b.memLevel,b.strategy);if(c!==p)throw new Error(k[c]);if(b.header&&h.deflateSetHeader(this.strm,b.header),b.dictionary){var e;if(e="string"==typeof b.dictionary?j.string2buf(b.dictionary):"[object ArrayBuffer]"===m.call(b.dictionary)?new Uint8Array(b.dictionary):b.dictionary,c=h.deflateSetDictionary(this.strm,e),c!==p)throw new Error(k[c]);this._dict_set=!0}}function e(a,b){var c=new d(b);if(c.push(a,!0),c.err)throw c.msg||k[c.err];return c.result}function f(a,b){return b=b||{},b.raw=!0,e(a,b)}function g(a,b){return b=b||{},b.gzip=!0,e(a,b)}var h=a("./zlib/deflate"),i=a("./utils/common"),j=a("./utils/strings"),k=a("./zlib/messages"),l=a("./zlib/zstream"),m=Object.prototype.toString,n=0,o=4,p=0,q=1,r=2,s=-1,t=0,u=8;d.prototype.push=function(a,b){var c,d,e=this.strm,f=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?o:n,"string"==typeof a?e.input=j.string2buf(a):"[object ArrayBuffer]"===m.call(a)?e.input=new Uint8Array(a):e.input=a,e.next_in=0,e.avail_in=e.input.length;do{if(0===e.avail_out&&(e.output=new i.Buf8(f),e.next_out=0,e.avail_out=f),c=h.deflate(e,d),c!==q&&c!==p)return this.onEnd(c),this.ended=!0,!1;0!==e.avail_out&&(0!==e.avail_in||d!==o&&d!==r)||("string"===this.options.to?this.onData(j.buf2binstring(i.shrinkBuf(e.output,e.next_out))):this.onData(i.shrinkBuf(e.output,e.next_out)))}while((e.avail_in>0||0===e.avail_out)&&c!==q);return d===o?(c=h.deflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===p):d!==r||(this.onEnd(p),e.avail_out=0,!0)},d.prototype.onData=function(a){this.chunks.push(a)},d.prototype.onEnd=function(a){a===p&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=i.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Deflate=d,c.deflate=e,c.deflateRaw=f,c.gzip=g},{"./utils/common":62,"./utils/strings":63,"./zlib/deflate":67,"./zlib/messages":72,"./zlib/zstream":74}],61:[function(a,b,c){"use strict";function d(a){if(!(this instanceof d))return new d(a);this.options=h.assign({chunkSize:16384,windowBits:0,to:""},a||{});var b=this.options;b.raw&&b.windowBits>=0&&b.windowBits<16&&(b.windowBits=-b.windowBits,0===b.windowBits&&(b.windowBits=-15)),!(b.windowBits>=0&&b.windowBits<16)||a&&a.windowBits||(b.windowBits+=32),b.windowBits>15&&b.windowBits<48&&0===(15&b.windowBits)&&(b.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new l,this.strm.avail_out=0;var c=g.inflateInit2(this.strm,b.windowBits);if(c!==j.Z_OK)throw new Error(k[c]);this.header=new m,g.inflateGetHeader(this.strm,this.header)}function e(a,b){var c=new d(b);if(c.push(a,!0),c.err)throw c.msg||k[c.err];return c.result}function f(a,b){return b=b||{},b.raw=!0,e(a,b)}var g=a("./zlib/inflate"),h=a("./utils/common"),i=a("./utils/strings"),j=a("./zlib/constants"),k=a("./zlib/messages"),l=a("./zlib/zstream"),m=a("./zlib/gzheader"),n=Object.prototype.toString;d.prototype.push=function(a,b){var c,d,e,f,k,l,m=this.strm,o=this.options.chunkSize,p=this.options.dictionary,q=!1;if(this.ended)return!1;d=b===~~b?b:b===!0?j.Z_FINISH:j.Z_NO_FLUSH,"string"==typeof a?m.input=i.binstring2buf(a):"[object ArrayBuffer]"===n.call(a)?m.input=new Uint8Array(a):m.input=a,m.next_in=0,m.avail_in=m.input.length;do{if(0===m.avail_out&&(m.output=new h.Buf8(o),m.next_out=0,m.avail_out=o),c=g.inflate(m,j.Z_NO_FLUSH),c===j.Z_NEED_DICT&&p&&(l="string"==typeof p?i.string2buf(p):"[object ArrayBuffer]"===n.call(p)?new Uint8Array(p):p,c=g.inflateSetDictionary(this.strm,l)),c===j.Z_BUF_ERROR&&q===!0&&(c=j.Z_OK,q=!1),c!==j.Z_STREAM_END&&c!==j.Z_OK)return this.onEnd(c),this.ended=!0,!1;m.next_out&&(0!==m.avail_out&&c!==j.Z_STREAM_END&&(0!==m.avail_in||d!==j.Z_FINISH&&d!==j.Z_SYNC_FLUSH)||("string"===this.options.to?(e=i.utf8border(m.output,m.next_out),f=m.next_out-e,k=i.buf2string(m.output,e),m.next_out=f,m.avail_out=o-f,f&&h.arraySet(m.output,m.output,e,f,0),this.onData(k)):this.onData(h.shrinkBuf(m.output,m.next_out)))),0===m.avail_in&&0===m.avail_out&&(q=!0)}while((m.avail_in>0||0===m.avail_out)&&c!==j.Z_STREAM_END);return c===j.Z_STREAM_END&&(d=j.Z_FINISH),d===j.Z_FINISH?(c=g.inflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===j.Z_OK):d!==j.Z_SYNC_FLUSH||(this.onEnd(j.Z_OK),m.avail_out=0,!0)},d.prototype.onData=function(a){this.chunks.push(a)},d.prototype.onEnd=function(a){a===j.Z_OK&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=h.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Inflate=d,c.inflate=e,c.inflateRaw=f,c.ungzip=e},{"./utils/common":62,"./utils/strings":63,"./zlib/constants":65,"./zlib/gzheader":68,"./zlib/inflate":70,"./zlib/messages":72,"./zlib/zstream":74}],62:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;c.assign=function(a){for(var b=Array.prototype.slice.call(arguments,1);b.length;){var c=b.shift();if(c){if("object"!=typeof c)throw new TypeError(c+"must be non-object");for(var d in c)c.hasOwnProperty(d)&&(a[d]=c[d])}}return a},c.shrinkBuf=function(a,b){return a.length===b?a:a.subarray?a.subarray(0,b):(a.length=b,a)};var e={arraySet:function(a,b,c,d,e){if(b.subarray&&a.subarray)return void a.set(b.subarray(c,c+d),e);for(var f=0;f=252?6:j>=248?5:j>=240?4:j>=224?3:j>=192?2:1;i[254]=i[254]=1,c.string2buf=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;f>>6,b[g++]=128|63&c):c<65536?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},c.buf2binstring=function(a){return d(a,a.length)},c.binstring2buf=function(a){for(var b=new e.Buf8(a.length),c=0,d=b.length;c4)j[e++]=65533,c+=g-1;else{for(f&=2===g?31:3===g?15:7;g>1&&c1?j[e++]=65533:f<65536?j[e++]=f:(f-=65536,j[e++]=55296|f>>10&1023,j[e++]=56320|1023&f)}return d(j,e)},c.utf8border=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return c<0?b:0===c?b:c+i[a[c]]>b?c:b}},{"./common":62}],64:[function(a,b,c){"use strict";function d(a,b,c,d){for(var e=65535&a|0,f=a>>>16&65535|0,g=0;0!==c;){g=c>2e3?2e3:c,c-=g;do e=e+b[d++]|0,f=f+e|0;while(--g);e%=65521,f%=65521}return e|f<<16|0; 14 | }b.exports=d},{}],65:[function(a,b,c){"use strict";b.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],66:[function(a,b,c){"use strict";function d(){for(var a,b=[],c=0;c<256;c++){a=c;for(var d=0;d<8;d++)a=1&a?3988292384^a>>>1:a>>>1;b[c]=a}return b}function e(a,b,c,d){var e=f,g=d+c;a^=-1;for(var h=d;h>>8^e[255&(a^b[h])];return a^-1}var f=d();b.exports=e},{}],67:[function(a,b,c){"use strict";function d(a,b){return a.msg=I[b],b}function e(a){return(a<<1)-(a>4?9:0)}function f(a){for(var b=a.length;--b>=0;)a[b]=0}function g(a){var b=a.state,c=b.pending;c>a.avail_out&&(c=a.avail_out),0!==c&&(E.arraySet(a.output,b.pending_buf,b.pending_out,c,a.next_out),a.next_out+=c,b.pending_out+=c,a.total_out+=c,a.avail_out-=c,b.pending-=c,0===b.pending&&(b.pending_out=0))}function h(a,b){F._tr_flush_block(a,a.block_start>=0?a.block_start:-1,a.strstart-a.block_start,b),a.block_start=a.strstart,g(a.strm)}function i(a,b){a.pending_buf[a.pending++]=b}function j(a,b){a.pending_buf[a.pending++]=b>>>8&255,a.pending_buf[a.pending++]=255&b}function k(a,b,c,d){var e=a.avail_in;return e>d&&(e=d),0===e?0:(a.avail_in-=e,E.arraySet(b,a.input,a.next_in,e,c),1===a.state.wrap?a.adler=G(a.adler,b,e,c):2===a.state.wrap&&(a.adler=H(a.adler,b,e,c)),a.next_in+=e,a.total_in+=e,e)}function l(a,b){var c,d,e=a.max_chain_length,f=a.strstart,g=a.prev_length,h=a.nice_match,i=a.strstart>a.w_size-la?a.strstart-(a.w_size-la):0,j=a.window,k=a.w_mask,l=a.prev,m=a.strstart+ka,n=j[f+g-1],o=j[f+g];a.prev_length>=a.good_match&&(e>>=2),h>a.lookahead&&(h=a.lookahead);do if(c=b,j[c+g]===o&&j[c+g-1]===n&&j[c]===j[f]&&j[++c]===j[f+1]){f+=2,c++;do;while(j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&fg){if(a.match_start=b,g=d,d>=h)break;n=j[f+g-1],o=j[f+g]}}while((b=l[b&k])>i&&0!==--e);return g<=a.lookahead?g:a.lookahead}function m(a){var b,c,d,e,f,g=a.w_size;do{if(e=a.window_size-a.lookahead-a.strstart,a.strstart>=g+(g-la)){E.arraySet(a.window,a.window,g,g,0),a.match_start-=g,a.strstart-=g,a.block_start-=g,c=a.hash_size,b=c;do d=a.head[--b],a.head[b]=d>=g?d-g:0;while(--c);c=g,b=c;do d=a.prev[--b],a.prev[b]=d>=g?d-g:0;while(--c);e+=g}if(0===a.strm.avail_in)break;if(c=k(a.strm,a.window,a.strstart+a.lookahead,e),a.lookahead+=c,a.lookahead+a.insert>=ja)for(f=a.strstart-a.insert,a.ins_h=a.window[f],a.ins_h=(a.ins_h<a.pending_buf_size-5&&(c=a.pending_buf_size-5);;){if(a.lookahead<=1){if(m(a),0===a.lookahead&&b===J)return ua;if(0===a.lookahead)break}a.strstart+=a.lookahead,a.lookahead=0;var d=a.block_start+c;if((0===a.strstart||a.strstart>=d)&&(a.lookahead=a.strstart-d,a.strstart=d,h(a,!1),0===a.strm.avail_out))return ua;if(a.strstart-a.block_start>=a.w_size-la&&(h(a,!1),0===a.strm.avail_out))return ua}return a.insert=0,b===M?(h(a,!0),0===a.strm.avail_out?wa:xa):a.strstart>a.block_start&&(h(a,!1),0===a.strm.avail_out)?ua:ua}function o(a,b){for(var c,d;;){if(a.lookahead=ja&&(a.ins_h=(a.ins_h<=ja)if(d=F._tr_tally(a,a.strstart-a.match_start,a.match_length-ja),a.lookahead-=a.match_length,a.match_length<=a.max_lazy_match&&a.lookahead>=ja){a.match_length--;do a.strstart++,a.ins_h=(a.ins_h<=ja&&(a.ins_h=(a.ins_h<4096)&&(a.match_length=ja-1)),a.prev_length>=ja&&a.match_length<=a.prev_length){e=a.strstart+a.lookahead-ja,d=F._tr_tally(a,a.strstart-1-a.prev_match,a.prev_length-ja),a.lookahead-=a.prev_length-1,a.prev_length-=2;do++a.strstart<=e&&(a.ins_h=(a.ins_h<=ja&&a.strstart>0&&(e=a.strstart-1,d=g[e],d===g[++e]&&d===g[++e]&&d===g[++e])){f=a.strstart+ka;do;while(d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&ea.lookahead&&(a.match_length=a.lookahead)}if(a.match_length>=ja?(c=F._tr_tally(a,1,a.match_length-ja),a.lookahead-=a.match_length,a.strstart+=a.match_length,a.match_length=0):(c=F._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++),c&&(h(a,!1),0===a.strm.avail_out))return ua}return a.insert=0,b===M?(h(a,!0),0===a.strm.avail_out?wa:xa):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?ua:va}function r(a,b){for(var c;;){if(0===a.lookahead&&(m(a),0===a.lookahead)){if(b===J)return ua;break}if(a.match_length=0,c=F._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++,c&&(h(a,!1),0===a.strm.avail_out))return ua}return a.insert=0,b===M?(h(a,!0),0===a.strm.avail_out?wa:xa):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?ua:va}function s(a,b,c,d,e){this.good_length=a,this.max_lazy=b,this.nice_length=c,this.max_chain=d,this.func=e}function t(a){a.window_size=2*a.w_size,f(a.head),a.max_lazy_match=D[a.level].max_lazy,a.good_match=D[a.level].good_length,a.nice_match=D[a.level].nice_length,a.max_chain_length=D[a.level].max_chain,a.strstart=0,a.block_start=0,a.lookahead=0,a.insert=0,a.match_length=a.prev_length=ja-1,a.match_available=0,a.ins_h=0}function u(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=$,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new E.Buf16(2*ha),this.dyn_dtree=new E.Buf16(2*(2*fa+1)),this.bl_tree=new E.Buf16(2*(2*ga+1)),f(this.dyn_ltree),f(this.dyn_dtree),f(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new E.Buf16(ia+1),this.heap=new E.Buf16(2*ea+1),f(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new E.Buf16(2*ea+1),f(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function v(a){var b;return a&&a.state?(a.total_in=a.total_out=0,a.data_type=Z,b=a.state,b.pending=0,b.pending_out=0,b.wrap<0&&(b.wrap=-b.wrap),b.status=b.wrap?na:sa,a.adler=2===b.wrap?0:1,b.last_flush=J,F._tr_init(b),O):d(a,Q)}function w(a){var b=v(a);return b===O&&t(a.state),b}function x(a,b){return a&&a.state?2!==a.state.wrap?Q:(a.state.gzhead=b,O):Q}function y(a,b,c,e,f,g){if(!a)return Q;var h=1;if(b===T&&(b=6),e<0?(h=0,e=-e):e>15&&(h=2,e-=16),f<1||f>_||c!==$||e<8||e>15||b<0||b>9||g<0||g>X)return d(a,Q);8===e&&(e=9);var i=new u;return a.state=i,i.strm=a,i.wrap=h,i.gzhead=null,i.w_bits=e,i.w_size=1<N||b<0)return a?d(a,Q):Q;if(h=a.state,!a.output||!a.input&&0!==a.avail_in||h.status===ta&&b!==M)return d(a,0===a.avail_out?S:Q);if(h.strm=a,c=h.last_flush,h.last_flush=b,h.status===na)if(2===h.wrap)a.adler=0,i(h,31),i(h,139),i(h,8),h.gzhead?(i(h,(h.gzhead.text?1:0)+(h.gzhead.hcrc?2:0)+(h.gzhead.extra?4:0)+(h.gzhead.name?8:0)+(h.gzhead.comment?16:0)),i(h,255&h.gzhead.time),i(h,h.gzhead.time>>8&255),i(h,h.gzhead.time>>16&255),i(h,h.gzhead.time>>24&255),i(h,9===h.level?2:h.strategy>=V||h.level<2?4:0),i(h,255&h.gzhead.os),h.gzhead.extra&&h.gzhead.extra.length&&(i(h,255&h.gzhead.extra.length),i(h,h.gzhead.extra.length>>8&255)),h.gzhead.hcrc&&(a.adler=H(a.adler,h.pending_buf,h.pending,0)),h.gzindex=0,h.status=oa):(i(h,0),i(h,0),i(h,0),i(h,0),i(h,0),i(h,9===h.level?2:h.strategy>=V||h.level<2?4:0),i(h,ya),h.status=sa);else{var m=$+(h.w_bits-8<<4)<<8,n=-1;n=h.strategy>=V||h.level<2?0:h.level<6?1:6===h.level?2:3,m|=n<<6,0!==h.strstart&&(m|=ma),m+=31-m%31,h.status=sa,j(h,m),0!==h.strstart&&(j(h,a.adler>>>16),j(h,65535&a.adler)),a.adler=1}if(h.status===oa)if(h.gzhead.extra){for(k=h.pending;h.gzindex<(65535&h.gzhead.extra.length)&&(h.pending!==h.pending_buf_size||(h.gzhead.hcrc&&h.pending>k&&(a.adler=H(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending!==h.pending_buf_size));)i(h,255&h.gzhead.extra[h.gzindex]),h.gzindex++;h.gzhead.hcrc&&h.pending>k&&(a.adler=H(a.adler,h.pending_buf,h.pending-k,k)),h.gzindex===h.gzhead.extra.length&&(h.gzindex=0,h.status=pa)}else h.status=pa;if(h.status===pa)if(h.gzhead.name){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=H(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindexk&&(a.adler=H(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.gzindex=0,h.status=qa)}else h.status=qa;if(h.status===qa)if(h.gzhead.comment){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=H(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindexk&&(a.adler=H(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.status=ra)}else h.status=ra;if(h.status===ra&&(h.gzhead.hcrc?(h.pending+2>h.pending_buf_size&&g(a),h.pending+2<=h.pending_buf_size&&(i(h,255&a.adler),i(h,a.adler>>8&255),a.adler=0,h.status=sa)):h.status=sa),0!==h.pending){if(g(a),0===a.avail_out)return h.last_flush=-1,O}else if(0===a.avail_in&&e(b)<=e(c)&&b!==M)return d(a,S);if(h.status===ta&&0!==a.avail_in)return d(a,S);if(0!==a.avail_in||0!==h.lookahead||b!==J&&h.status!==ta){var o=h.strategy===V?r(h,b):h.strategy===W?q(h,b):D[h.level].func(h,b);if(o!==wa&&o!==xa||(h.status=ta),o===ua||o===wa)return 0===a.avail_out&&(h.last_flush=-1),O;if(o===va&&(b===K?F._tr_align(h):b!==N&&(F._tr_stored_block(h,0,0,!1),b===L&&(f(h.head),0===h.lookahead&&(h.strstart=0,h.block_start=0,h.insert=0))),g(a),0===a.avail_out))return h.last_flush=-1,O}return b!==M?O:h.wrap<=0?P:(2===h.wrap?(i(h,255&a.adler),i(h,a.adler>>8&255),i(h,a.adler>>16&255),i(h,a.adler>>24&255),i(h,255&a.total_in),i(h,a.total_in>>8&255),i(h,a.total_in>>16&255),i(h,a.total_in>>24&255)):(j(h,a.adler>>>16),j(h,65535&a.adler)),g(a),h.wrap>0&&(h.wrap=-h.wrap),0!==h.pending?O:P)}function B(a){var b;return a&&a.state?(b=a.state.status,b!==na&&b!==oa&&b!==pa&&b!==qa&&b!==ra&&b!==sa&&b!==ta?d(a,Q):(a.state=null,b===sa?d(a,R):O)):Q}function C(a,b){var c,d,e,g,h,i,j,k,l=b.length;if(!a||!a.state)return Q;if(c=a.state,g=c.wrap,2===g||1===g&&c.status!==na||c.lookahead)return Q;for(1===g&&(a.adler=G(a.adler,b,l,0)),c.wrap=0,l>=c.w_size&&(0===g&&(f(c.head),c.strstart=0,c.block_start=0,c.insert=0),k=new E.Buf8(c.w_size),E.arraySet(k,b,l-c.w_size,c.w_size,0),b=k,l=c.w_size),h=a.avail_in,i=a.next_in,j=a.input,a.avail_in=l,a.next_in=0,a.input=b,m(c);c.lookahead>=ja;){d=c.strstart,e=c.lookahead-(ja-1);do c.ins_h=(c.ins_h<>>24,p>>>=w,q-=w,w=v>>>16&255,0===w)C[h++]=65535&v;else{if(!(16&w)){if(0===(64&w)){v=r[(65535&v)+(p&(1<>>=w,q-=w),q<15&&(p+=B[f++]<>>24,p>>>=w,q-=w,w=v>>>16&255,!(16&w)){if(0===(64&w)){v=s[(65535&v)+(p&(1<k){a.msg="invalid distance too far back",c.mode=d;break a}if(p>>>=w,q-=w,w=h-i,y>w){if(w=y-w,w>m&&c.sane){a.msg="invalid distance too far back",c.mode=d;break a}if(z=0,A=o,0===n){if(z+=l-w,w2;)C[h++]=A[z++],C[h++]=A[z++],C[h++]=A[z++],x-=3;x&&(C[h++]=A[z++],x>1&&(C[h++]=A[z++]))}else{z=h-y;do C[h++]=C[z++],C[h++]=C[z++],C[h++]=C[z++],x-=3;while(x>2);x&&(C[h++]=C[z++],x>1&&(C[h++]=C[z++]))}break}}break}}while(f>3,f-=x,q-=x<<3,p&=(1<>>24&255)+(a>>>8&65280)+((65280&a)<<8)+((255&a)<<24)}function e(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new s.Buf16(320),this.work=new s.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function f(a){var b;return a&&a.state?(b=a.state,a.total_in=a.total_out=b.total=0,a.msg="",b.wrap&&(a.adler=1&b.wrap),b.mode=L,b.last=0,b.havedict=0,b.dmax=32768,b.head=null,b.hold=0,b.bits=0,b.lencode=b.lendyn=new s.Buf32(pa),b.distcode=b.distdyn=new s.Buf32(qa),b.sane=1,b.back=-1,D):G}function g(a){var b;return a&&a.state?(b=a.state,b.wsize=0,b.whave=0,b.wnext=0,f(a)):G}function h(a,b){var c,d;return a&&a.state?(d=a.state,b<0?(c=0,b=-b):(c=(b>>4)+1,b<48&&(b&=15)),b&&(b<8||b>15)?G:(null!==d.window&&d.wbits!==b&&(d.window=null),d.wrap=c,d.wbits=b,g(a))):G}function i(a,b){var c,d;return a?(d=new e,a.state=d,d.window=null,c=h(a,b),c!==D&&(a.state=null),c):G}function j(a){return i(a,sa)}function k(a){if(ta){var b;for(q=new s.Buf32(512),r=new s.Buf32(32),b=0;b<144;)a.lens[b++]=8;for(;b<256;)a.lens[b++]=9;for(;b<280;)a.lens[b++]=7;for(;b<288;)a.lens[b++]=8;for(w(y,a.lens,0,288,q,0,a.work,{bits:9}),b=0;b<32;)a.lens[b++]=5;w(z,a.lens,0,32,r,0,a.work,{bits:5}),ta=!1}a.lencode=q,a.lenbits=9,a.distcode=r,a.distbits=5}function l(a,b,c,d){var e,f=a.state;return null===f.window&&(f.wsize=1<=f.wsize?(s.arraySet(f.window,b,c-f.wsize,f.wsize,0),f.wnext=0,f.whave=f.wsize):(e=f.wsize-f.wnext,e>d&&(e=d),s.arraySet(f.window,b,c-d,e,f.wnext),d-=e,d?(s.arraySet(f.window,b,c-d,d,0),f.wnext=d,f.whave=f.wsize):(f.wnext+=e,f.wnext===f.wsize&&(f.wnext=0),f.whave>>8&255,c.check=u(c.check,Ba,2,0),m=0,n=0,c.mode=M;break}if(c.flags=0,c.head&&(c.head.done=!1),!(1&c.wrap)||(((255&m)<<8)+(m>>8))%31){a.msg="incorrect header check",c.mode=ma;break}if((15&m)!==K){a.msg="unknown compression method",c.mode=ma;break}if(m>>>=4,n-=4,wa=(15&m)+8,0===c.wbits)c.wbits=wa;else if(wa>c.wbits){a.msg="invalid window size",c.mode=ma;break}c.dmax=1<>8&1),512&c.flags&&(Ba[0]=255&m,Ba[1]=m>>>8&255,c.check=u(c.check,Ba,2,0)),m=0,n=0,c.mode=N;case N:for(;n<32;){if(0===i)break a;i--,m+=e[g++]<>>8&255,Ba[2]=m>>>16&255,Ba[3]=m>>>24&255,c.check=u(c.check,Ba,4,0)),m=0,n=0,c.mode=O;case O:for(;n<16;){if(0===i)break a;i--,m+=e[g++]<>8),512&c.flags&&(Ba[0]=255&m,Ba[1]=m>>>8&255,c.check=u(c.check,Ba,2,0)),m=0,n=0,c.mode=P;case P:if(1024&c.flags){for(;n<16;){if(0===i)break a;i--,m+=e[g++]<>>8&255,c.check=u(c.check,Ba,2,0)),m=0,n=0}else c.head&&(c.head.extra=null);c.mode=Q;case Q:if(1024&c.flags&&(q=c.length,q>i&&(q=i),q&&(c.head&&(wa=c.head.extra_len-c.length,c.head.extra||(c.head.extra=new Array(c.head.extra_len)),s.arraySet(c.head.extra,e,g,q,wa)),512&c.flags&&(c.check=u(c.check,e,q,g)),i-=q,g+=q,c.length-=q),c.length))break a;c.length=0,c.mode=R;case R:if(2048&c.flags){if(0===i)break a;q=0;do wa=e[g+q++],c.head&&wa&&c.length<65536&&(c.head.name+=String.fromCharCode(wa));while(wa&&q>9&1,c.head.done=!0),a.adler=c.check=0,c.mode=W;break;case U:for(;n<32;){if(0===i)break a;i--,m+=e[g++]<>>=7&n,n-=7&n,c.mode=ja;break}for(;n<3;){if(0===i)break a;i--,m+=e[g++]<>>=1,n-=1,3&m){case 0:c.mode=Y;break;case 1:if(k(c),c.mode=ca,b===C){m>>>=2,n-=2;break a}break;case 2:c.mode=_;break;case 3:a.msg="invalid block type",c.mode=ma}m>>>=2,n-=2;break;case Y:for(m>>>=7&n,n-=7&n;n<32;){if(0===i)break a;i--,m+=e[g++]<>>16^65535)){a.msg="invalid stored block lengths",c.mode=ma;break}if(c.length=65535&m,m=0,n=0,c.mode=Z,b===C)break a;case Z:c.mode=$;case $:if(q=c.length){if(q>i&&(q=i),q>j&&(q=j),0===q)break a;s.arraySet(f,e,g,q,h),i-=q,g+=q,j-=q,h+=q,c.length-=q;break}c.mode=W;break;case _:for(;n<14;){if(0===i)break a;i--,m+=e[g++]<>>=5,n-=5,c.ndist=(31&m)+1,m>>>=5,n-=5,c.ncode=(15&m)+4,m>>>=4,n-=4,c.nlen>286||c.ndist>30){a.msg="too many length or distance symbols",c.mode=ma;break}c.have=0,c.mode=aa;case aa:for(;c.have>>=3,n-=3}for(;c.have<19;)c.lens[Ca[c.have++]]=0;if(c.lencode=c.lendyn,c.lenbits=7,ya={bits:c.lenbits},xa=w(x,c.lens,0,19,c.lencode,0,c.work,ya),c.lenbits=ya.bits,xa){a.msg="invalid code lengths set",c.mode=ma;break}c.have=0,c.mode=ba;case ba:for(;c.have>>24,ra=Aa>>>16&255,sa=65535&Aa,!(qa<=n);){if(0===i)break a;i--,m+=e[g++]<>>=qa,n-=qa,c.lens[c.have++]=sa;else{if(16===sa){for(za=qa+2;n>>=qa,n-=qa,0===c.have){a.msg="invalid bit length repeat",c.mode=ma;break}wa=c.lens[c.have-1],q=3+(3&m),m>>>=2,n-=2}else if(17===sa){for(za=qa+3;n>>=qa,n-=qa,wa=0,q=3+(7&m),m>>>=3,n-=3}else{for(za=qa+7;n>>=qa,n-=qa,wa=0,q=11+(127&m),m>>>=7,n-=7}if(c.have+q>c.nlen+c.ndist){a.msg="invalid bit length repeat",c.mode=ma;break}for(;q--;)c.lens[c.have++]=wa}}if(c.mode===ma)break;if(0===c.lens[256]){a.msg="invalid code -- missing end-of-block",c.mode=ma;break}if(c.lenbits=9,ya={bits:c.lenbits},xa=w(y,c.lens,0,c.nlen,c.lencode,0,c.work,ya),c.lenbits=ya.bits,xa){a.msg="invalid literal/lengths set",c.mode=ma;break}if(c.distbits=6,c.distcode=c.distdyn,ya={bits:c.distbits},xa=w(z,c.lens,c.nlen,c.ndist,c.distcode,0,c.work,ya),c.distbits=ya.bits,xa){a.msg="invalid distances set",c.mode=ma;break}if(c.mode=ca,b===C)break a;case ca:c.mode=da;case da:if(i>=6&&j>=258){a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,v(a,p),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,c.mode===W&&(c.back=-1);break}for(c.back=0;Aa=c.lencode[m&(1<>>24,ra=Aa>>>16&255,sa=65535&Aa,!(qa<=n);){if(0===i)break a;i--,m+=e[g++]<>ta)],qa=Aa>>>24,ra=Aa>>>16&255,sa=65535&Aa,!(ta+qa<=n);){if(0===i)break a;i--,m+=e[g++]<>>=ta,n-=ta,c.back+=ta}if(m>>>=qa,n-=qa,c.back+=qa,c.length=sa,0===ra){c.mode=ia;break}if(32&ra){c.back=-1,c.mode=W;break}if(64&ra){a.msg="invalid literal/length code",c.mode=ma;break}c.extra=15&ra,c.mode=ea;case ea:if(c.extra){for(za=c.extra;n>>=c.extra,n-=c.extra,c.back+=c.extra}c.was=c.length,c.mode=fa;case fa:for(;Aa=c.distcode[m&(1<>>24,ra=Aa>>>16&255,sa=65535&Aa,!(qa<=n);){if(0===i)break a;i--,m+=e[g++]<>ta)],qa=Aa>>>24,ra=Aa>>>16&255,sa=65535&Aa,!(ta+qa<=n);){if(0===i)break a;i--,m+=e[g++]<>>=ta,n-=ta,c.back+=ta}if(m>>>=qa,n-=qa,c.back+=qa,64&ra){a.msg="invalid distance code",c.mode=ma;break}c.offset=sa,c.extra=15&ra,c.mode=ga;case ga:if(c.extra){for(za=c.extra;n>>=c.extra,n-=c.extra,c.back+=c.extra}if(c.offset>c.dmax){a.msg="invalid distance too far back",c.mode=ma;break}c.mode=ha;case ha:if(0===j)break a;if(q=p-j,c.offset>q){if(q=c.offset-q,q>c.whave&&c.sane){a.msg="invalid distance too far back",c.mode=ma;break}q>c.wnext?(q-=c.wnext,r=c.wsize-q):r=c.wnext-q,q>c.length&&(q=c.length),pa=c.window}else pa=f,r=h-c.offset,q=c.length;q>j&&(q=j),j-=q,c.length-=q;do f[h++]=pa[r++];while(--q);0===c.length&&(c.mode=da);break;case ia:if(0===j)break a;f[h++]=c.length,j--,c.mode=da;break;case ja:if(c.wrap){for(;n<32;){if(0===i)break a;i--,m|=e[g++]<=1&&0===P[G];G--);if(H>G&&(H=G),0===G)return p[q++]=20971520,p[q++]=20971520,s.bits=1,0;for(F=1;F0&&(a===h||1!==G))return-1;for(Q[1]=0,D=1;Df||a===j&&L>g)return 1;for(;;){z=D-J,r[E]y?(A=R[S+r[E]],B=N[O+r[E]]):(A=96,B=0),t=1<>J)+u]=z<<24|A<<16|B|0;while(0!==u);for(t=1<>=1;if(0!==t?(M&=t-1,M+=t):M=0,E++,0===--P[D]){if(D===G)break;D=b[c+r[E]]}if(D>H&&(M&w)!==v){for(0===J&&(J=H),x+=F,I=D-J,K=1<f||a===j&&L>g)return 1;v=M&w,p[v]=H<<24|I<<16|x-q|0}}return 0!==M&&(p[x+M]=D-J<<24|64<<16|0),s.bits=H,0}},{"../utils/common":62}],72:[function(a,b,c){"use strict";b.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],73:[function(a,b,c){"use strict";function d(a){for(var b=a.length;--b>=0;)a[b]=0}function e(a,b,c,d,e){this.static_tree=a,this.extra_bits=b,this.extra_base=c,this.elems=d,this.max_length=e,this.has_stree=a&&a.length}function f(a,b){this.dyn_tree=a,this.max_code=0,this.stat_desc=b}function g(a){return a<256?ia[a]:ia[256+(a>>>7)]}function h(a,b){a.pending_buf[a.pending++]=255&b,a.pending_buf[a.pending++]=b>>>8&255}function i(a,b,c){a.bi_valid>X-c?(a.bi_buf|=b<>X-a.bi_valid,a.bi_valid+=c-X):(a.bi_buf|=b<>>=1,c<<=1;while(--b>0);return c>>>1}function l(a){16===a.bi_valid?(h(a,a.bi_buf),a.bi_buf=0,a.bi_valid=0):a.bi_valid>=8&&(a.pending_buf[a.pending++]=255&a.bi_buf,a.bi_buf>>=8,a.bi_valid-=8)}function m(a,b){var c,d,e,f,g,h,i=b.dyn_tree,j=b.max_code,k=b.stat_desc.static_tree,l=b.stat_desc.has_stree,m=b.stat_desc.extra_bits,n=b.stat_desc.extra_base,o=b.stat_desc.max_length,p=0;for(f=0;f<=W;f++)a.bl_count[f]=0;for(i[2*a.heap[a.heap_max]+1]=0, 15 | c=a.heap_max+1;co&&(f=o,p++),i[2*d+1]=f,d>j||(a.bl_count[f]++,g=0,d>=n&&(g=m[d-n]),h=i[2*d],a.opt_len+=h*(f+g),l&&(a.static_len+=h*(k[2*d+1]+g)));if(0!==p){do{for(f=o-1;0===a.bl_count[f];)f--;a.bl_count[f]--,a.bl_count[f+1]+=2,a.bl_count[o]--,p-=2}while(p>0);for(f=o;0!==f;f--)for(d=a.bl_count[f];0!==d;)e=a.heap[--c],e>j||(i[2*e+1]!==f&&(a.opt_len+=(f-i[2*e+1])*i[2*e],i[2*e+1]=f),d--)}}function n(a,b,c){var d,e,f=new Array(W+1),g=0;for(d=1;d<=W;d++)f[d]=g=g+c[d-1]<<1;for(e=0;e<=b;e++){var h=a[2*e+1];0!==h&&(a[2*e]=k(f[h]++,h))}}function o(){var a,b,c,d,f,g=new Array(W+1);for(c=0,d=0;d>=7;d8?h(a,a.bi_buf):a.bi_valid>0&&(a.pending_buf[a.pending++]=a.bi_buf),a.bi_buf=0,a.bi_valid=0}function r(a,b,c,d){q(a),d&&(h(a,c),h(a,~c)),G.arraySet(a.pending_buf,a.window,b,c,a.pending),a.pending+=c}function s(a,b,c,d){var e=2*b,f=2*c;return a[e]>1;c>=1;c--)t(a,f,c);e=i;do c=a.heap[1],a.heap[1]=a.heap[a.heap_len--],t(a,f,1),d=a.heap[1],a.heap[--a.heap_max]=c,a.heap[--a.heap_max]=d,f[2*e]=f[2*c]+f[2*d],a.depth[e]=(a.depth[c]>=a.depth[d]?a.depth[c]:a.depth[d])+1,f[2*c+1]=f[2*d+1]=e,a.heap[1]=e++,t(a,f,1);while(a.heap_len>=2);a.heap[--a.heap_max]=a.heap[1],m(a,b),n(f,j,a.bl_count)}function w(a,b,c){var d,e,f=-1,g=b[1],h=0,i=7,j=4;for(0===g&&(i=138,j=3),b[2*(c+1)+1]=65535,d=0;d<=c;d++)e=g,g=b[2*(d+1)+1],++h=3&&0===a.bl_tree[2*ea[b]+1];b--);return a.opt_len+=3*(b+1)+5+5+4,b}function z(a,b,c,d){var e;for(i(a,b-257,5),i(a,c-1,5),i(a,d-4,4),e=0;e>>=1)if(1&c&&0!==a.dyn_ltree[2*b])return I;if(0!==a.dyn_ltree[18]||0!==a.dyn_ltree[20]||0!==a.dyn_ltree[26])return J;for(b=32;b0?(a.strm.data_type===K&&(a.strm.data_type=A(a)),v(a,a.l_desc),v(a,a.d_desc),g=y(a),e=a.opt_len+3+7>>>3,f=a.static_len+3+7>>>3,f<=e&&(e=f)):e=f=c+5,c+4<=e&&b!==-1?C(a,b,c,d):a.strategy===H||f===e?(i(a,(M<<1)+(d?1:0),3),u(a,ga,ha)):(i(a,(N<<1)+(d?1:0),3),z(a,a.l_desc.max_code+1,a.d_desc.max_code+1,g+1),u(a,a.dyn_ltree,a.dyn_dtree)),p(a),d&&q(a)}function F(a,b,c){return a.pending_buf[a.d_buf+2*a.last_lit]=b>>>8&255,a.pending_buf[a.d_buf+2*a.last_lit+1]=255&b,a.pending_buf[a.l_buf+a.last_lit]=255&c,a.last_lit++,0===b?a.dyn_ltree[2*c]++:(a.matches++,b--,a.dyn_ltree[2*(ja[c]+R+1)]++,a.dyn_dtree[2*g(b)]++),a.last_lit===a.lit_bufsize-1}var G=a("../utils/common"),H=4,I=0,J=1,K=2,L=0,M=1,N=2,O=3,P=258,Q=29,R=256,S=R+1+Q,T=30,U=19,V=2*S+1,W=15,X=16,Y=7,Z=256,$=16,_=17,aa=18,ba=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],ca=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],da=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],ea=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],fa=512,ga=new Array(2*(S+2));d(ga);var ha=new Array(2*T);d(ha);var ia=new Array(fa);d(ia);var ja=new Array(P-O+1);d(ja);var ka=new Array(Q);d(ka);var la=new Array(T);d(la);var ma,na,oa,pa=!1;c._tr_init=B,c._tr_stored_block=C,c._tr_flush_block=E,c._tr_tally=F,c._tr_align=D},{"../utils/common":62}],74:[function(a,b,c){"use strict";function d(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}b.exports=d},{}]},{},[10])(10)}); -------------------------------------------------------------------------------- /ybat.css: -------------------------------------------------------------------------------- 1 | body { 2 | overflow: hidden; 3 | } 4 | 5 | form { 6 | height: 100% 7 | } 8 | 9 | hr { 10 | width: 100%; 11 | } 12 | 13 | .container { 14 | display: block; 15 | position: absolute; 16 | height: auto; 17 | bottom: 0; 18 | top: 0; 19 | left: 0; 20 | right: 0; 21 | } 22 | 23 | .column_wrap { 24 | position: relative; 25 | } 26 | 27 | .left { 28 | width: calc(30% - 10px); 29 | position: absolute; 30 | left: 0; 31 | top: 0; 32 | bottom: 0; 33 | height: auto; 34 | display: block; 35 | margin: 10px 0 10px 10px; 36 | } 37 | 38 | .right { 39 | border-left: 1px solid gray; 40 | margin: 10px 10px 10px calc(30% + 10px); 41 | padding-left: 10px; 42 | } 43 | 44 | #images, #classes, #bboxes { 45 | width: 175px; 46 | margin-top: 10px; 47 | } 48 | 49 | #bboxes { 50 | width: 130px; 51 | } 52 | 53 | #imageList, #classList, #description { 54 | width: 100%; 55 | margin-top: 10px; 56 | margin-bottom: 10px; 57 | height: 18%; 58 | } 59 | 60 | #saveClasses, #saveBboxes, #saveVocBboxes, #saveCocoBboxes, #restoreBboxes, #cropImages { 61 | float: right; 62 | margin-left: 10px; 63 | margin-top: 10px; 64 | margin-bottom: 10px; 65 | } 66 | 67 | #imageSearch { 68 | margin-top: 10px; 69 | } 70 | 71 | #imageInformation, #bboxInformation { 72 | height: 20px; 73 | width: 100%; 74 | } 75 | 76 | #voc { 77 | margin-top: 10px; 78 | } 79 | 80 | #vocFolder { 81 | width: 95px; 82 | } 83 | 84 | #saveVocBboxes, #saveCocoBboxes { 85 | margin-top: 0; 86 | margin-bottom: 0; 87 | } -------------------------------------------------------------------------------- /ybat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Ybat - YOLO BBox Annotation Tool 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 | 47 | 48 | 49 | 50 |
51 |
52 | 53 |
54 |
55 | 56 |
57 | SHORTCUTS: 58 |
    59 |
  • 60 | Mouse WHEEL - zoom in/out image 61 |
  • 62 |
  • 63 | Mouse RIGHT BUTTON - pan image 64 |
  • 65 |
  • 66 | Arrows LEFT and RIGHT - cycle images 67 |
  • 68 |
  • 69 | Arrows Up and DOWN - cycle classes 70 |
  • 71 |
  • 72 | Key DELETE - remove selected Bbox 73 |
  • 74 |
75 | 76 |

Version: 0.2.5 | Copyright © 2018-2019 Draining Sun.

77 |
78 |
79 |
80 | 81 | 85 |
86 | 87 | -------------------------------------------------------------------------------- /ybat.js: -------------------------------------------------------------------------------- 1 | 2 | (() => { 3 | "use strict" 4 | 5 | // Parameters 6 | const saveInterval = 60 // Bbox recovery save in seconds 7 | const fontBaseSize = 30 // Text size in pixels 8 | const fontColor = "#001f3f" // Base font color 9 | const borderColor = "#001f3f" // Base bbox border color 10 | const backgroundColor = "rgba(0, 116, 217, 0.2)" // Base bbox fill color 11 | const markedFontColor = "#FF4136" // Marked bbox font color 12 | const markedBorderColor = "#FF4136" // Marked bbox border color 13 | const markedBackgroundColor = "rgba(255, 133, 27, 0.2)" // Marked bbox fill color 14 | const minBBoxWidth = 5 // Minimal width of bbox 15 | const minBBoxHeight = 5 // Minimal height of bbox 16 | const scrollSpeed = 1.1 // Multiplying factor of wheel speed 17 | const minZoom = 0.1 // Smallest zoom allowed 18 | const maxZoom = 5 // Largest zoom allowed 19 | const edgeSize = 5 // Resize activation area in pixels 20 | const resetCanvasOnChange = true // Whether to return to default position and zoom on image change 21 | const defaultScale = 0.5 // Default zoom level for images. Can be overridden with fittedZoom 22 | const drawCenterX = true // Whether to draw a cross in the middle of bbox 23 | const drawGuidelines = true // Whether to draw guidelines for cursor 24 | const fittedZoom = true // Whether to fit image in the screen by it's largest dimension. Overrides defaultScale 25 | 26 | // Main containers 27 | let canvas = null 28 | let images = {} 29 | let classes = {} 30 | let bboxes = {} 31 | 32 | const extensions = ["jpg", "jpeg", "png", "JPG", "JPEG", "PNG"] 33 | 34 | let currentImage = null 35 | let currentClass = null 36 | let currentBbox = null 37 | let imageListIndex = 0 38 | let classListIndex = 0 39 | 40 | // Scaling containers 41 | let scale = defaultScale 42 | let canvasX = 0 43 | let canvasY = 0 44 | let screenX = 0 45 | let screenY = 0 46 | 47 | // Mouse container 48 | const mouse = { 49 | x: 0, 50 | y: 0, 51 | realX: 0, 52 | realY: 0, 53 | buttonL: false, 54 | buttonR: false, 55 | startRealX: 0, 56 | startRealY: 0 57 | } 58 | 59 | // Prevent context menu on right click - it's used for panning 60 | document.addEventListener("contextmenu", function (e) { 61 | e.preventDefault() 62 | }, false) 63 | 64 | const isSupported = () => { 65 | try { 66 | const key = "__some_random_key_1234%(*^()^)___" 67 | 68 | localStorage.setItem(key, key) 69 | localStorage.removeItem(key) 70 | 71 | return true 72 | } catch (e) { 73 | return false 74 | } 75 | } 76 | 77 | // Save bboxes to local storage every X seconds 78 | if (isSupported() === true) { 79 | setInterval(() => { 80 | if (Object.keys(bboxes).length > 0) { 81 | localStorage.setItem("bboxes", JSON.stringify(bboxes)) 82 | } 83 | }, saveInterval * 1000) 84 | } else { 85 | alert("Restore function is not supported. If you need it, use Chrome or Firefox instead.") 86 | } 87 | 88 | // Start everything 89 | document.onreadystatechange = () => { 90 | if (document.readyState === "complete") { 91 | listenCanvas() 92 | listenCanvasMouse() 93 | listenImageLoad() 94 | listenImageSelect() 95 | listenClassLoad() 96 | listenClassSelect() 97 | listenBboxLoad() 98 | listenBboxSave() 99 | listenBboxVocSave() 100 | listenBboxCocoSave() 101 | listenBboxRestore() 102 | listenKeyboard() 103 | listenImageSearch() 104 | listenImageCrop() 105 | } 106 | } 107 | 108 | const listenCanvas = () => { 109 | canvas = new Canvas("canvas", document.getElementById("right").clientWidth, window.innerHeight - 20) 110 | 111 | canvas.on("draw", (context) => { 112 | if (currentImage !== null) { 113 | drawImage(context) 114 | drawNewBbox(context) 115 | drawExistingBboxes(context) 116 | drawCross(context) 117 | } else { 118 | drawIntro(context) 119 | } 120 | }).start() 121 | } 122 | 123 | const drawImage = (context) => { 124 | context.drawImage(currentImage.object, zoomX(0), zoomY(0), zoom(currentImage.width), zoom(currentImage.height)) 125 | } 126 | 127 | const drawIntro = (context) => { 128 | setFontStyles(context, false) 129 | context.fillText("USAGE:", zoomX(20), zoomY(50)) 130 | context.fillText("1. Load your images (jpg, png). Might be slow if many or big.", zoomX(20), zoomY(100)) 131 | context.fillText("2. Load your classes (yolo *.names format).", zoomX(20), zoomY(150)) 132 | context.fillText("3. Load or restore, if any, bboxes (zipped yolo/voc/coco files).", zoomX(20), zoomY(200)) 133 | context.fillText("NOTES:", zoomX(20), zoomY(300)) 134 | context.fillText("1: Images and classes must be loaded before bbox load.", zoomX(20), zoomY(350)) 135 | context.fillText("2: Reloading images will RESET BBOXES!", zoomX(20), zoomY(400)) 136 | context.fillText("3: Check out README.md for more information.", zoomX(20), zoomY(450)) 137 | } 138 | 139 | const drawNewBbox = (context) => { 140 | if (mouse.buttonL === true && currentClass !== null && currentBbox === null) { 141 | const width = (mouse.realX - mouse.startRealX) 142 | const height = (mouse.realY - mouse.startRealY) 143 | 144 | setBBoxStyles(context, true) 145 | context.strokeRect(zoomX(mouse.startRealX), zoomY(mouse.startRealY), zoom(width), zoom(height)) 146 | context.fillRect(zoomX(mouse.startRealX), zoomY(mouse.startRealY), zoom(width), zoom(height)) 147 | 148 | drawX(context, mouse.startRealX, mouse.startRealY, width, height) 149 | 150 | setBboxCoordinates(mouse.startRealX, mouse.startRealY, width, height) 151 | } 152 | } 153 | 154 | const drawExistingBboxes = (context) => { 155 | const currentBboxes = bboxes[currentImage.name] 156 | 157 | for (let className in currentBboxes) { 158 | currentBboxes[className].forEach(bbox => { 159 | setFontStyles(context, bbox.marked) 160 | context.fillText(className, zoomX(bbox.x), zoomY(bbox.y - 2)) 161 | 162 | setBBoxStyles(context, bbox.marked) 163 | context.strokeRect(zoomX(bbox.x), zoomY(bbox.y), zoom(bbox.width), zoom(bbox.height)) 164 | context.fillRect(zoomX(bbox.x), zoomY(bbox.y), zoom(bbox.width), zoom(bbox.height)) 165 | 166 | drawX(context, bbox.x, bbox.y, bbox.width, bbox.height) 167 | 168 | if (bbox.marked === true) { 169 | setBboxCoordinates(bbox.x, bbox.y, bbox.width, bbox.height) 170 | } 171 | }) 172 | } 173 | } 174 | 175 | const drawX = (context, x, y, width, height) => { 176 | if (drawCenterX === true) { 177 | const centerX = x + width / 2 178 | const centerY = y + height / 2 179 | 180 | context.beginPath() 181 | context.moveTo(zoomX(centerX), zoomY(centerY - 10)) 182 | context.lineTo(zoomX(centerX), zoomY(centerY + 10)) 183 | context.stroke() 184 | 185 | context.beginPath() 186 | context.moveTo(zoomX(centerX - 10), zoomY(centerY)) 187 | context.lineTo(zoomX(centerX + 10), zoomY(centerY)) 188 | context.stroke() 189 | } 190 | } 191 | 192 | const drawCross = (context) => { 193 | if (drawGuidelines === true) { 194 | context.setLineDash([5]) 195 | 196 | context.beginPath() 197 | context.moveTo(zoomX(mouse.realX), zoomY(0)) 198 | context.lineTo(zoomX(mouse.realX), zoomY(currentImage.height)) 199 | context.stroke() 200 | 201 | context.beginPath() 202 | context.moveTo(zoomX(0), zoomY(mouse.realY)) 203 | context.lineTo(zoomX(currentImage.width), zoomY(mouse.realY)) 204 | context.stroke() 205 | } 206 | } 207 | 208 | const setBBoxStyles = (context, marked) => { 209 | context.setLineDash([]) 210 | 211 | if (marked === false) { 212 | context.strokeStyle = borderColor 213 | context.fillStyle = backgroundColor 214 | } else { 215 | context.strokeStyle = markedBorderColor 216 | context.fillStyle = markedBackgroundColor 217 | } 218 | } 219 | 220 | const setBboxCoordinates = (x, y, width, height) => { 221 | const x2 = x + width 222 | const y2 = y + height 223 | 224 | document.getElementById("bboxInformation").innerHTML = `${width}x${height} (${x}, ${y}) (${x2}, ${y2})` 225 | } 226 | 227 | const setFontStyles = (context, marked) => { 228 | if (marked === false) { 229 | context.fillStyle = fontColor 230 | } else { 231 | context.fillStyle = markedFontColor 232 | } 233 | 234 | context.font = context.font.replace(/\d+px/, `${zoom(fontBaseSize)}px`) 235 | } 236 | 237 | const listenCanvasMouse = () => { 238 | canvas.element.addEventListener("wheel", trackWheel, {passive: false}) 239 | canvas.element.addEventListener("mousemove", trackPointer) 240 | canvas.element.addEventListener("mousedown", trackPointer) 241 | canvas.element.addEventListener("mouseup", trackPointer) 242 | canvas.element.addEventListener("mouseout", trackPointer) 243 | } 244 | 245 | const trackWheel = (event) => { 246 | if (event.deltaY < 0) { 247 | scale = Math.min(maxZoom, scale * scrollSpeed) 248 | } else { 249 | scale = Math.max(minZoom, scale * (1 / scrollSpeed)) 250 | } 251 | 252 | canvasX = mouse.realX 253 | canvasY = mouse.realY 254 | screenX = mouse.x 255 | screenY = mouse.y 256 | 257 | mouse.realX = zoomXInv(mouse.x) 258 | mouse.realY = zoomYInv(mouse.y) 259 | 260 | event.preventDefault() 261 | } 262 | 263 | const trackPointer = (event) => { 264 | mouse.bounds = canvas.element.getBoundingClientRect() 265 | mouse.x = event.clientX - mouse.bounds.left 266 | mouse.y = event.clientY - mouse.bounds.top 267 | 268 | const xx = mouse.realX 269 | const yy = mouse.realY 270 | 271 | mouse.realX = zoomXInv(mouse.x) 272 | mouse.realY = zoomYInv(mouse.y) 273 | 274 | if (event.type === "mousedown") { 275 | mouse.startRealX = mouse.realX 276 | mouse.startRealY = mouse.realY 277 | 278 | if (event.which === 3) { 279 | mouse.buttonR = true 280 | } else if (event.which === 1) { 281 | mouse.buttonL = true 282 | } 283 | } else if (event.type === "mouseup" || event.type === "mouseout") { 284 | if (mouse.buttonL === true && currentImage !== null && currentClass !== null) { 285 | const movedWidth = Math.max((mouse.startRealX - mouse.realX), (mouse.realX - mouse.startRealX)) 286 | const movedHeight = Math.max((mouse.startRealY - mouse.realY), (mouse.realY - mouse.startRealY)) 287 | 288 | if (movedWidth > minBBoxWidth && movedHeight > minBBoxHeight) { // Only add if bbox is big enough 289 | if (currentBbox === null) { // And only when no other bbox is selected 290 | storeNewBbox(movedWidth, movedHeight) 291 | } else { // Bbox was moved or resized - update original data 292 | updateBboxAfterTransform() 293 | } 294 | } else { // (un)Mark a bbox 295 | setBboxMarkedState() 296 | 297 | if (currentBbox !== null) { // Bbox was moved or resized - update original data 298 | updateBboxAfterTransform() 299 | } 300 | } 301 | } 302 | 303 | mouse.buttonR = false 304 | mouse.buttonL = false 305 | } 306 | 307 | moveBbox() 308 | resizeBbox() 309 | changeCursorByLocation() 310 | 311 | panImage(xx, yy) 312 | } 313 | 314 | const storeNewBbox = (movedWidth, movedHeight) => { 315 | const bbox = { 316 | x: Math.min(mouse.startRealX, mouse.realX), 317 | y: Math.min(mouse.startRealY, mouse.realY), 318 | width: movedWidth, 319 | height: movedHeight, 320 | marked: true, 321 | class: currentClass 322 | } 323 | 324 | if (typeof bboxes[currentImage.name] === "undefined") { 325 | bboxes[currentImage.name] = {} 326 | } 327 | 328 | if (typeof bboxes[currentImage.name][currentClass] === "undefined") { 329 | bboxes[currentImage.name][currentClass] = [] 330 | } 331 | 332 | bboxes[currentImage.name][currentClass].push(bbox) 333 | 334 | currentBbox = { 335 | bbox: bbox, 336 | index: bboxes[currentImage.name][currentClass].length - 1, 337 | originalX: bbox.x, 338 | originalY: bbox.y, 339 | originalWidth: bbox.width, 340 | originalHeight: bbox.height, 341 | moving: false, 342 | resizing: null 343 | } 344 | } 345 | 346 | const updateBboxAfterTransform = () => { 347 | if (currentBbox.resizing !== null) { 348 | if (currentBbox.bbox.width < 0) { 349 | currentBbox.bbox.width = Math.abs(currentBbox.bbox.width) 350 | currentBbox.bbox.x -= currentBbox.bbox.width 351 | } 352 | 353 | if (currentBbox.bbox.height < 0) { 354 | currentBbox.bbox.height = Math.abs(currentBbox.bbox.height) 355 | currentBbox.bbox.y -= currentBbox.bbox.height 356 | } 357 | 358 | currentBbox.resizing = null 359 | } 360 | 361 | currentBbox.bbox.marked = true 362 | currentBbox.originalX = currentBbox.bbox.x 363 | currentBbox.originalY = currentBbox.bbox.y 364 | currentBbox.originalWidth = currentBbox.bbox.width 365 | currentBbox.originalHeight = currentBbox.bbox.height 366 | currentBbox.moving = false 367 | } 368 | 369 | const setBboxMarkedState = () => { 370 | if (currentBbox === null || (currentBbox.moving === false && currentBbox.resizing === null)) { 371 | const currentBboxes = bboxes[currentImage.name] 372 | 373 | let wasInside = false 374 | let smallestBbox = Number.MAX_SAFE_INTEGER 375 | 376 | for (let className in currentBboxes) { 377 | for (let i = 0; i < currentBboxes[className].length; i++) { 378 | const bbox = currentBboxes[className][i] 379 | 380 | bbox.marked = false 381 | 382 | const endX = bbox.x + bbox.width 383 | const endY = bbox.y + bbox.height 384 | const size = bbox.width * bbox.height 385 | 386 | if (mouse.startRealX >= bbox.x && mouse.startRealX <= endX 387 | && mouse.startRealY >= bbox.y && mouse.startRealY <= endY) { 388 | 389 | wasInside = true 390 | 391 | if (size < smallestBbox) { // Make sure select the inner if it's inside a bigger one 392 | smallestBbox = size 393 | currentBbox = { 394 | bbox: bbox, 395 | index: i, 396 | originalX: bbox.x, 397 | originalY: bbox.y, 398 | originalWidth: bbox.width, 399 | originalHeight: bbox.height, 400 | moving: false, 401 | resizing: null 402 | } 403 | } 404 | } 405 | } 406 | } 407 | 408 | if (wasInside === false) { // No more selected bbox 409 | currentBbox = null 410 | } 411 | } 412 | } 413 | 414 | const moveBbox = () => { 415 | if (mouse.buttonL === true && currentBbox !== null) { 416 | const endX = currentBbox.bbox.x + currentBbox.bbox.width 417 | const endY = currentBbox.bbox.y + currentBbox.bbox.height 418 | 419 | // Only if pointer inside the bbox 420 | if (mouse.startRealX >= (currentBbox.bbox.x + edgeSize) && mouse.startRealX <= (endX - edgeSize) 421 | && mouse.startRealY >= (currentBbox.bbox.y + edgeSize) && mouse.startRealY <= (endY - edgeSize)) { 422 | 423 | currentBbox.moving = true 424 | } 425 | 426 | if (currentBbox.moving === true) { 427 | currentBbox.bbox.x = currentBbox.originalX + (mouse.realX - mouse.startRealX) 428 | currentBbox.bbox.y = currentBbox.originalY + (mouse.realY - mouse.startRealY) 429 | } 430 | } 431 | } 432 | 433 | const resizeBbox = () => { 434 | if (mouse.buttonL === true && currentBbox !== null) { 435 | const topLeftX = currentBbox.bbox.x 436 | const topLeftY = currentBbox.bbox.y 437 | const bottomLeftX = currentBbox.bbox.x 438 | const bottomLeftY = currentBbox.bbox.y + currentBbox.bbox.height 439 | const topRightX = currentBbox.bbox.x + currentBbox.bbox.width 440 | const topRightY = currentBbox.bbox.y 441 | const bottomRightX = currentBbox.bbox.x + currentBbox.bbox.width 442 | const bottomRightY = currentBbox.bbox.y + currentBbox.bbox.height 443 | 444 | // Get correct corner 445 | if (mouse.startRealX >= (topLeftX - edgeSize) && mouse.startRealX <= (topLeftX + edgeSize) 446 | && mouse.startRealY >= (topLeftY - edgeSize) && mouse.startRealY <= (topLeftY + edgeSize)) { 447 | 448 | currentBbox.resizing = "topLeft" 449 | } else if (mouse.startRealX >= (bottomLeftX - edgeSize) && mouse.startRealX <= (bottomLeftX + edgeSize) 450 | && mouse.startRealY >= (bottomLeftY - edgeSize) && mouse.startRealY <= (bottomLeftY + edgeSize)) { 451 | 452 | currentBbox.resizing = "bottomLeft" 453 | } else if (mouse.startRealX >= (topRightX - edgeSize) && mouse.startRealX <= (topRightX + edgeSize) 454 | && mouse.startRealY >= (topRightY - edgeSize) && mouse.startRealY <= (topRightY + edgeSize)) { 455 | 456 | currentBbox.resizing = "topRight" 457 | } else if (mouse.startRealX >= (bottomRightX - edgeSize) && mouse.startRealX <= (bottomRightX + edgeSize) 458 | && mouse.startRealY >= (bottomRightY - edgeSize) && mouse.startRealY <= (bottomRightY + edgeSize)) { 459 | 460 | currentBbox.resizing = "bottomRight" 461 | } 462 | 463 | if (currentBbox.resizing === "topLeft") { 464 | currentBbox.bbox.x = mouse.realX 465 | currentBbox.bbox.y = mouse.realY 466 | currentBbox.bbox.width = currentBbox.originalX + currentBbox.originalWidth - mouse.realX 467 | currentBbox.bbox.height = currentBbox.originalY + currentBbox.originalHeight - mouse.realY 468 | } else if (currentBbox.resizing === "bottomLeft") { 469 | currentBbox.bbox.x = mouse.realX 470 | currentBbox.bbox.y = mouse.realY - (mouse.realY - currentBbox.originalY) 471 | currentBbox.bbox.width = currentBbox.originalX + currentBbox.originalWidth - mouse.realX 472 | currentBbox.bbox.height = mouse.realY - currentBbox.originalY 473 | } else if (currentBbox.resizing === "topRight") { 474 | currentBbox.bbox.x = mouse.realX - (mouse.realX - currentBbox.originalX) 475 | currentBbox.bbox.y = mouse.realY 476 | currentBbox.bbox.width = mouse.realX - currentBbox.originalX 477 | currentBbox.bbox.height = currentBbox.originalY + currentBbox.originalHeight - mouse.realY 478 | } else if (currentBbox.resizing === "bottomRight") { 479 | currentBbox.bbox.x = mouse.realX - (mouse.realX - currentBbox.originalX) 480 | currentBbox.bbox.y = mouse.realY - (mouse.realY - currentBbox.originalY) 481 | currentBbox.bbox.width = mouse.realX - currentBbox.originalX 482 | currentBbox.bbox.height = mouse.realY - currentBbox.originalY 483 | } 484 | } 485 | } 486 | 487 | const changeCursorByLocation = () => { 488 | if (currentImage !== null) { 489 | const currentBboxes = bboxes[currentImage.name] 490 | 491 | for (let className in currentBboxes) { 492 | for (let i = 0; i < currentBboxes[className].length; i++) { 493 | const bbox = currentBboxes[className][i] 494 | 495 | const endX = bbox.x + bbox.width 496 | const endY = bbox.y + bbox.height 497 | 498 | if (mouse.realX >= (bbox.x + edgeSize) && mouse.realX <= (endX - edgeSize) 499 | && mouse.realY >= (bbox.y + edgeSize) && mouse.realY <= (endY - edgeSize)) { 500 | 501 | document.body.style.cursor = "pointer" 502 | 503 | break 504 | } else { 505 | document.body.style.cursor = "default" 506 | } 507 | } 508 | } 509 | 510 | if (currentBbox !== null) { 511 | const topLeftX = currentBbox.bbox.x 512 | const topLeftY = currentBbox.bbox.y 513 | const bottomLeftX = currentBbox.bbox.x 514 | const bottomLeftY = currentBbox.bbox.y + currentBbox.bbox.height 515 | const topRightX = currentBbox.bbox.x + currentBbox.bbox.width 516 | const topRightY = currentBbox.bbox.y 517 | const bottomRightX = currentBbox.bbox.x + currentBbox.bbox.width 518 | const bottomRightY = currentBbox.bbox.y + currentBbox.bbox.height 519 | 520 | if (mouse.realX >= (topLeftX + edgeSize) && mouse.realX <= (bottomRightX - edgeSize) 521 | && mouse.realY >= (topLeftY + edgeSize) && mouse.realY <= (bottomRightY - edgeSize)) { 522 | 523 | document.body.style.cursor = "move" 524 | } else if (mouse.realX >= (topLeftX - edgeSize) && mouse.realX <= (topLeftX + edgeSize) 525 | && mouse.realY >= (topLeftY - edgeSize) && mouse.realY <= (topLeftY + edgeSize)) { 526 | document.body.style.cursor = "nwse-resize" 527 | 528 | } else if (mouse.realX >= (bottomLeftX - edgeSize) && mouse.realX <= (bottomLeftX + edgeSize) 529 | && mouse.realY >= (bottomLeftY - edgeSize) && mouse.realY <= (bottomLeftY + edgeSize)) { 530 | 531 | document.body.style.cursor = "nesw-resize" 532 | } else if (mouse.realX >= (topRightX - edgeSize) && mouse.realX <= (topRightX + edgeSize) 533 | && mouse.realY >= (topRightY - edgeSize) && mouse.realY <= (topRightY + edgeSize)) { 534 | 535 | document.body.style.cursor = "nesw-resize" 536 | } else if (mouse.realX >= (bottomRightX - edgeSize) && mouse.realX <= (bottomRightX + edgeSize) 537 | && mouse.realY >= (bottomRightY - edgeSize) && mouse.realY <= (bottomRightY + edgeSize)) { 538 | 539 | document.body.style.cursor = "nwse-resize" 540 | } 541 | } 542 | } 543 | } 544 | 545 | const panImage= (xx, yy) => { 546 | if (mouse.buttonR === true) { 547 | canvasX -= mouse.realX - xx 548 | canvasY -= mouse.realY - yy 549 | 550 | mouse.realX = zoomXInv(mouse.x) 551 | mouse.realY = zoomYInv(mouse.y) 552 | } 553 | } 554 | 555 | const zoom = (number) => { 556 | return Math.floor(number * scale) 557 | } 558 | 559 | const zoomX = (number) => { 560 | return Math.floor((number - canvasX) * scale + screenX) 561 | } 562 | 563 | const zoomY = (number) => { 564 | return Math.floor((number - canvasY) * scale + screenY) 565 | } 566 | 567 | const zoomXInv = (number) => { 568 | return Math.floor((number - screenX) * (1 / scale) + canvasX) 569 | } 570 | 571 | const zoomYInv = (number) => { 572 | return Math.floor((number - screenY) * (1 / scale) + canvasY) 573 | } 574 | 575 | const listenImageLoad = () => { 576 | document.getElementById("images").addEventListener("change", (event) => { 577 | const imageList = document.getElementById("imageList") 578 | 579 | const files = event.target.files 580 | 581 | if (files.length > 0) { 582 | resetImageList() 583 | 584 | document.body.style.cursor = "wait" 585 | 586 | for (let i = 0; i < files.length; i++) { 587 | const nameParts = files[i].name.split(".") 588 | 589 | if (extensions.indexOf(nameParts[nameParts.length - 1]) !== -1) { 590 | images[files[i].name] = { 591 | meta: files[i], 592 | index: i 593 | } 594 | 595 | const option = document.createElement("option") 596 | 597 | option.value = files[i].name 598 | option.innerHTML = files[i].name 599 | 600 | if (i === 0) { 601 | option.selected = true 602 | } 603 | 604 | imageList.appendChild(option) 605 | } 606 | } 607 | 608 | const imageArray = Object.keys(images) 609 | 610 | let async = imageArray.length 611 | 612 | for (let image in images) { 613 | const reader = new FileReader() 614 | 615 | reader.addEventListener("load", () => { 616 | const imageObject = new Image() 617 | 618 | imageObject.addEventListener("load", (event) => { 619 | images[image].width = event.target.width 620 | images[image].height = event.target.height 621 | 622 | if (--async === 0) { 623 | document.body.style.cursor = "default" 624 | 625 | setCurrentImage(images[imageArray[0]]) 626 | 627 | if (Object.keys(classes).length > 0) { 628 | document.getElementById("bboxes").disabled = false 629 | document.getElementById("restoreBboxes").disabled = false 630 | } 631 | } 632 | }) 633 | 634 | imageObject.src = reader.result 635 | }) 636 | 637 | reader.readAsDataURL(images[image].meta) 638 | } 639 | } 640 | }) 641 | } 642 | 643 | const resetImageList = () => { 644 | const imageList = document.getElementById("imageList") 645 | 646 | imageList.innerHTML = "" 647 | 648 | images = {} 649 | bboxes = {} 650 | currentImage = null 651 | } 652 | 653 | const setCurrentImage = (image) => { 654 | if (resetCanvasOnChange === true) { 655 | resetCanvasPlacement() 656 | } 657 | 658 | if (fittedZoom === true) { 659 | fitZoom(image) 660 | } 661 | 662 | const reader = new FileReader() 663 | 664 | reader.addEventListener("load", () => { 665 | const dataUrl = reader.result 666 | const imageObject = new Image() 667 | 668 | imageObject.addEventListener("load", () => { 669 | currentImage = { 670 | name: image.meta.name, 671 | object: imageObject, 672 | width: image.width, 673 | height: image.height 674 | } 675 | }) 676 | 677 | imageObject.src = dataUrl 678 | 679 | document.getElementById("imageInformation") 680 | .innerHTML = `${image.width}x${image.height}, ${formatBytes(image.meta.size)}` 681 | }) 682 | 683 | reader.readAsDataURL(image.meta) 684 | 685 | if (currentBbox !== null) { 686 | currentBbox.bbox.marked = false // We unmark via reference 687 | currentBbox = null // and the we delete 688 | } 689 | } 690 | 691 | const fitZoom = (image) => { 692 | if (image.width > image.height) { 693 | scale = canvas.width / image.width 694 | } else { 695 | scale = canvas.height / image.height 696 | } 697 | } 698 | 699 | const formatBytes = (bytes, decimals) => { 700 | if (bytes === 0) { 701 | return "0 Bytes" 702 | } 703 | 704 | const k = 1024 705 | const dm = decimals || 2 706 | const sizes = ["Bytes", "KB", "MB"] 707 | const i = Math.floor(Math.log(bytes) / Math.log(k)) 708 | 709 | return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i] 710 | } 711 | 712 | const listenImageSelect = () => { 713 | const imageList = document.getElementById("imageList") 714 | 715 | imageList.addEventListener("change", () => { 716 | imageListIndex = imageList.selectedIndex 717 | 718 | setCurrentImage(images[imageList.options[imageListIndex].innerHTML]) 719 | }) 720 | } 721 | 722 | const listenClassLoad = () => { 723 | const classesElement = document.getElementById("classes") 724 | 725 | classesElement.addEventListener("click", () => { 726 | classesElement.value = null 727 | }) 728 | 729 | classesElement.addEventListener("change", (event) => { 730 | const files = event.target.files 731 | 732 | if (files.length > 0) { 733 | resetClassList() 734 | 735 | const nameParts = files[0].name.split(".") 736 | 737 | if (nameParts[nameParts.length - 1] === "txt") { 738 | const reader = new FileReader() 739 | 740 | reader.addEventListener("load", () => { 741 | const lines = reader.result 742 | 743 | const rows = lines.split(/[\r\n]+/) 744 | 745 | if (rows.length > 0) { 746 | const classList = document.getElementById("classList") 747 | 748 | for (let i = 0; i < rows.length; i++) { 749 | rows[i] = rows[i].trim() 750 | 751 | if (rows[i] !== "") { 752 | classes[rows[i]] = i 753 | 754 | const option = document.createElement("option") 755 | 756 | option.value = i 757 | option.innerHTML = rows[i] 758 | 759 | if (i === 0) { 760 | option.selected = true 761 | currentClass = rows[i] 762 | } 763 | 764 | classList.appendChild(option) 765 | } 766 | } 767 | 768 | setCurrentClass() 769 | 770 | if (Object.keys(images).length > 0) { 771 | document.getElementById("bboxes").disabled = false 772 | document.getElementById("restoreBboxes").disabled = false 773 | } 774 | } 775 | }) 776 | 777 | reader.readAsText(files[0]) 778 | } 779 | } 780 | }) 781 | } 782 | 783 | const resetClassList = () => { 784 | document.getElementById("classList").innerHTML = "" 785 | 786 | classes = {} 787 | currentClass = null 788 | } 789 | 790 | const setCurrentClass = () => { 791 | const classList = document.getElementById("classList") 792 | 793 | currentClass = classList.options[classList.selectedIndex].text 794 | 795 | if (currentBbox !== null) { 796 | currentBbox.bbox.marked = false // We unmark via reference 797 | currentBbox = null // and the we delete 798 | } 799 | } 800 | 801 | const listenClassSelect = () => { 802 | const classList = document.getElementById("classList") 803 | 804 | classList.addEventListener("change", () => { 805 | classListIndex = classList.selectedIndex 806 | 807 | setCurrentClass() 808 | }) 809 | } 810 | 811 | const listenBboxLoad = () => { 812 | const bboxesElement = document.getElementById("bboxes") 813 | 814 | bboxesElement.addEventListener("click", () => { 815 | bboxesElement.value = null 816 | }) 817 | 818 | bboxesElement.addEventListener("change", (event) => { 819 | const files = event.target.files 820 | 821 | if (files.length > 0) { 822 | resetBboxes() 823 | 824 | for (let i = 0; i < files.length; i++) { 825 | const reader = new FileReader() 826 | 827 | const extension = files[i].name.split(".").pop() 828 | 829 | reader.addEventListener("load", () => { 830 | if (extension === "txt" || extension === "xml" || extension === "json") { 831 | storeBbox(files[i].name, reader.result) 832 | } else { 833 | const zip = new JSZip() 834 | 835 | zip.loadAsync(reader.result) 836 | .then((result) => { 837 | for (let filename in result.files) { 838 | result.file(filename).async("string") 839 | .then((text) => { 840 | storeBbox(filename, text) 841 | }) 842 | } 843 | }) 844 | } 845 | }) 846 | 847 | if (extension === "txt" || extension === "xml" || extension === "json") { 848 | reader.readAsText(files[i]) 849 | } else { 850 | reader.readAsArrayBuffer(event.target.files[i]) 851 | } 852 | } 853 | } 854 | }) 855 | } 856 | 857 | const resetBboxes = () => { 858 | bboxes = {} 859 | } 860 | 861 | const storeBbox = (filename, text) => { 862 | let image = null 863 | let bbox = null 864 | 865 | const extension = filename.split(".").pop() 866 | 867 | if (extension === "txt" || extension === "xml") { 868 | for (let i = 0; i < extensions.length; i++) { 869 | const imageName = filename.replace(`.${extension}`, `.${extensions[i]}`) 870 | 871 | if (typeof images[imageName] !== "undefined") { 872 | image = images[imageName] 873 | 874 | if (typeof bboxes[imageName] === "undefined") { 875 | bboxes[imageName] = {} 876 | } 877 | 878 | bbox = bboxes[imageName] 879 | 880 | if (extension === "txt") { 881 | const rows = text.split(/[\r\n]+/) 882 | 883 | for (let i = 0; i < rows.length; i++) { 884 | const cols = rows[i].split(" ") 885 | 886 | cols[0] = parseInt(cols[0]) 887 | 888 | for (let className in classes) { 889 | if (classes[className] === cols[0]) { 890 | if (typeof bbox[className] === "undefined") { 891 | bbox[className] = [] 892 | } 893 | 894 | // Reverse engineer actual position and dimensions from yolo format 895 | const width = cols[3] * image.width 896 | const x = cols[1] * image.width - width * 0.5 897 | const height = cols[4] * image.height 898 | const y = cols[2] * image.height - height * 0.5 899 | 900 | bbox[className].push({ 901 | x: Math.floor(x), 902 | y: Math.floor(y), 903 | width: Math.floor(width), 904 | height: Math.floor(height), 905 | marked: false, 906 | class: className 907 | }) 908 | 909 | break 910 | } 911 | } 912 | } 913 | } else if (extension === "xml") { 914 | const parser = new DOMParser() 915 | const xmlDoc = parser.parseFromString(text, "text/xml") 916 | 917 | const objects = xmlDoc.getElementsByTagName("object") 918 | 919 | for (let i = 0; i < objects.length; i++) { 920 | const objectName = objects[i].getElementsByTagName("name")[0].childNodes[0].nodeValue 921 | 922 | for (let className in classes) { 923 | if (className === objectName) { 924 | if (typeof bbox[className] === "undefined") { 925 | bbox[className] = [] 926 | } 927 | 928 | const bndBox = objects[i].getElementsByTagName("bndbox")[0] 929 | 930 | const bndBoxX = bndBox.getElementsByTagName("xmin")[0].childNodes[0].nodeValue 931 | const bndBoxY = bndBox.getElementsByTagName("ymin")[0].childNodes[0].nodeValue 932 | const bndBoxMaxX = bndBox.getElementsByTagName("xmax")[0].childNodes[0].nodeValue 933 | const bndBoxMaxY = bndBox.getElementsByTagName("ymax")[0].childNodes[0].nodeValue 934 | 935 | bbox[className].push({ 936 | x: parseInt(bndBoxX), 937 | y: parseInt(bndBoxY), 938 | width: parseInt(bndBoxMaxX) - parseInt(bndBoxX), 939 | height: parseInt(bndBoxMaxY) - parseInt(bndBoxY), 940 | marked: false, 941 | class: className 942 | }) 943 | 944 | break 945 | } 946 | } 947 | } 948 | } 949 | } 950 | } 951 | } else { 952 | const json = JSON.parse(text) 953 | 954 | for (let i = 0; i < json.annotations.length; i++) { 955 | let imageName = null 956 | let categoryName = null 957 | 958 | for (let j = 0; j < json.images.length; j++) { 959 | if (json.annotations[i].image_id === json.images[j].id) { 960 | imageName = json.images[j].file_name 961 | 962 | if (typeof images[imageName] !== "undefined") { 963 | image = images[imageName] 964 | 965 | if (typeof bboxes[imageName] === "undefined") { 966 | bboxes[imageName] = {} 967 | } 968 | 969 | bbox = bboxes[imageName] 970 | 971 | break 972 | } 973 | } 974 | } 975 | 976 | for (let j = 0; j < json.categories.length; j++) { 977 | if (json.annotations[i].category_id === json.categories[j].id) { 978 | categoryName = json.categories[j].name 979 | 980 | break 981 | } 982 | } 983 | 984 | for (let className in classes) { 985 | if (className === categoryName) { 986 | if (typeof bbox[className] === "undefined") { 987 | bbox[className] = [] 988 | } 989 | 990 | const bboxX = json.annotations[i].bbox[0] 991 | const bboxY = json.annotations[i].bbox[1] 992 | const bboxWidth = json.annotations[i].bbox[2] 993 | const bboxHeight = json.annotations[i].bbox[3] 994 | 995 | bbox[className].push({ 996 | x: bboxX, 997 | y: bboxY, 998 | width: bboxWidth, 999 | height: bboxHeight, 1000 | marked: false, 1001 | class: className 1002 | }) 1003 | 1004 | break 1005 | } 1006 | } 1007 | } 1008 | } 1009 | } 1010 | 1011 | const listenBboxSave = () => { 1012 | document.getElementById("saveBboxes").addEventListener("click", () => { 1013 | const zip = new JSZip() 1014 | 1015 | for (let imageName in bboxes) { 1016 | const image = images[imageName] 1017 | 1018 | const name = imageName.split(".") 1019 | 1020 | name[name.length - 1] = "txt" 1021 | 1022 | const result = [] 1023 | 1024 | for (let className in bboxes[imageName]) { 1025 | for (let i = 0; i < bboxes[imageName][className].length; i++) { 1026 | const bbox = bboxes[imageName][className][i] 1027 | 1028 | // Prepare data for yolo format 1029 | const x = (bbox.x + bbox.width / 2) / image.width 1030 | const y = (bbox.y + bbox.height / 2) / image.height 1031 | const width = bbox.width / image.width 1032 | const height = bbox.height / image.height 1033 | 1034 | result.push(`${classes[className]} ${x} ${y} ${width} ${height}`) 1035 | } 1036 | } 1037 | 1038 | zip.file(name.join("."), result.join("\n")) 1039 | } 1040 | 1041 | zip.generateAsync({type: "blob"}) 1042 | .then((blob) => { 1043 | saveAs(blob, "bboxes_yolo.zip") 1044 | }) 1045 | }) 1046 | } 1047 | 1048 | const listenBboxVocSave = () => { 1049 | document.getElementById("saveVocBboxes").addEventListener("click", () => { 1050 | const folderPath = document.getElementById("vocFolder").value 1051 | 1052 | const zip = new JSZip() 1053 | 1054 | for (let imageName in bboxes) { 1055 | const image = images[imageName] 1056 | 1057 | const name = imageName.split(".") 1058 | 1059 | name[name.length - 1] = "xml" 1060 | 1061 | const result = [ 1062 | "", 1063 | "", 1064 | `${folderPath}`, 1065 | `${imageName}`, 1066 | "", 1067 | "", 1068 | "Unknown", 1069 | "", 1070 | "", 1071 | `${image.width}`, 1072 | `${image.height}`, 1073 | "3", 1074 | "", 1075 | "0" 1076 | ] 1077 | 1078 | for (let className in bboxes[imageName]) { 1079 | for (let i = 0; i < bboxes[imageName][className].length; i++) { 1080 | const bbox = bboxes[imageName][className][i] 1081 | 1082 | result.push("") 1083 | result.push(`${className}`) 1084 | result.push("Unspecified") 1085 | result.push("0") 1086 | result.push("0") 1087 | result.push("0") 1088 | 1089 | result.push("") 1090 | result.push(`${bbox.x}`) 1091 | result.push(`${bbox.y}`) 1092 | result.push(`${bbox.x + bbox.width}`) 1093 | result.push(`${bbox.y + bbox.height}`) 1094 | result.push("") 1095 | 1096 | result.push("") 1097 | } 1098 | } 1099 | 1100 | result.push("") 1101 | 1102 | if (result.length > 15) { 1103 | zip.file(name.join("."), result.join("\n")) 1104 | } 1105 | } 1106 | 1107 | zip.generateAsync({type: "blob"}) 1108 | .then((blob) => { 1109 | saveAs(blob, "bboxes_voc.zip") 1110 | }) 1111 | }) 1112 | } 1113 | 1114 | const listenBboxCocoSave = () => { 1115 | document.getElementById("saveCocoBboxes").addEventListener("click", () => { 1116 | const zip = new JSZip() 1117 | 1118 | const result = { 1119 | images: [], 1120 | type: "instances", 1121 | annotations: [], 1122 | categories: [] 1123 | } 1124 | 1125 | for (let className in classes) { 1126 | result.categories.push({ 1127 | supercategory: "none", 1128 | id: classes[className] + 1, 1129 | name: className 1130 | }) 1131 | } 1132 | 1133 | for (let imageName in images) { 1134 | result.images.push({ 1135 | id: images[imageName].index + 1, 1136 | file_name: imageName, //eslint-disable-line camelcase 1137 | width: images[imageName].width, 1138 | height: images[imageName].height 1139 | }) 1140 | } 1141 | 1142 | let id = 0 1143 | 1144 | for (let imageName in bboxes) { 1145 | const image = images[imageName] 1146 | 1147 | for (let className in bboxes[imageName]) { 1148 | for (let i = 0; i < bboxes[imageName][className].length; i++) { 1149 | const bbox = bboxes[imageName][className][i] 1150 | 1151 | const segmentation = [ 1152 | bbox.x, bbox.y, 1153 | bbox.x, bbox.y + bbox.height, 1154 | bbox.x + bbox.width, bbox.y + bbox.height, 1155 | bbox.x + bbox.width, bbox.y 1156 | ] 1157 | 1158 | result.annotations.push({ 1159 | segmentation: segmentation, 1160 | area: bbox.width * bbox.height, 1161 | iscrowd: 0, 1162 | ignore: 0, 1163 | image_id: image.index + 1, //eslint-disable-line camelcase 1164 | bbox: [bbox.x, bbox.y, bbox.width, bbox.height], 1165 | category_id: classes[className] + 1, //eslint-disable-line camelcase 1166 | id: ++id 1167 | }) 1168 | } 1169 | } 1170 | 1171 | zip.file("coco.json", JSON.stringify(result)) 1172 | } 1173 | 1174 | zip.generateAsync({type: "blob"}) 1175 | .then((blob) => { 1176 | saveAs(blob, "bboxes_coco.zip") 1177 | }) 1178 | }) 1179 | } 1180 | 1181 | const listenBboxRestore = () => { 1182 | document.getElementById("restoreBboxes").addEventListener("click", () => { 1183 | const item = localStorage.getItem("bboxes") 1184 | 1185 | if (item) { 1186 | bboxes = JSON.parse(item) 1187 | } 1188 | }) 1189 | } 1190 | 1191 | const listenKeyboard = () => { 1192 | const imageList = document.getElementById("imageList") 1193 | const classList = document.getElementById("classList") 1194 | 1195 | document.addEventListener("keydown", (event) => { 1196 | const key = event.keyCode || event.charCode 1197 | 1198 | if (key === 46 || (key === 8 && event.metaKey === true)) { 1199 | if (currentBbox !== null) { 1200 | bboxes[currentImage.name][currentBbox.bbox.class].splice(currentBbox.index, 1) 1201 | currentBbox = null 1202 | 1203 | document.body.style.cursor = "default" 1204 | } 1205 | 1206 | event.preventDefault() 1207 | } 1208 | 1209 | if (key === 37) { 1210 | if (imageList.length > 1) { 1211 | imageList.options[imageListIndex].selected = false 1212 | 1213 | if (imageListIndex === 0) { 1214 | imageListIndex = imageList.length - 1 1215 | } else { 1216 | imageListIndex-- 1217 | } 1218 | 1219 | imageList.options[imageListIndex].selected = true 1220 | imageList.selectedIndex = imageListIndex 1221 | 1222 | setCurrentImage(images[imageList.options[imageListIndex].innerHTML]) 1223 | 1224 | document.body.style.cursor = "default" 1225 | } 1226 | 1227 | event.preventDefault() 1228 | } 1229 | 1230 | if (key === 39) { 1231 | if (imageList.length > 1) { 1232 | imageList.options[imageListIndex].selected = false 1233 | 1234 | if (imageListIndex === imageList.length - 1) { 1235 | imageListIndex = 0 1236 | } else { 1237 | imageListIndex++ 1238 | } 1239 | 1240 | imageList.options[imageListIndex].selected = true 1241 | imageList.selectedIndex = imageListIndex 1242 | 1243 | setCurrentImage(images[imageList.options[imageListIndex].innerHTML]) 1244 | 1245 | document.body.style.cursor = "default" 1246 | } 1247 | 1248 | event.preventDefault() 1249 | } 1250 | 1251 | if (key === 38) { 1252 | if (classList.length > 1) { 1253 | classList.options[classListIndex].selected = false 1254 | 1255 | if (classListIndex === 0) { 1256 | classListIndex = classList.length - 1 1257 | } else { 1258 | classListIndex-- 1259 | } 1260 | 1261 | classList.options[classListIndex].selected = true 1262 | classList.selectedIndex = classListIndex 1263 | 1264 | setCurrentClass() 1265 | } 1266 | 1267 | event.preventDefault() 1268 | } 1269 | 1270 | if (key === 40) { 1271 | if (classList.length > 1) { 1272 | classList.options[classListIndex].selected = false 1273 | 1274 | if (classListIndex === classList.length - 1) { 1275 | classListIndex = 0 1276 | } else { 1277 | classListIndex++ 1278 | } 1279 | 1280 | classList.options[classListIndex].selected = true 1281 | classList.selectedIndex = classListIndex 1282 | 1283 | setCurrentClass() 1284 | } 1285 | 1286 | event.preventDefault() 1287 | } 1288 | }) 1289 | } 1290 | 1291 | const resetCanvasPlacement = () => { 1292 | scale = defaultScale 1293 | canvasX = 0 1294 | canvasY = 0 1295 | screenX = 0 1296 | screenY = 0 1297 | 1298 | mouse.x = 0 1299 | mouse.y = 0 1300 | mouse.realX = 0 1301 | mouse.realY = 0 1302 | mouse.buttonL = 0 1303 | mouse.buttonR = 0 1304 | mouse.startRealX = 0 1305 | mouse.startRealY = 0 1306 | } 1307 | 1308 | const listenImageSearch = () => { 1309 | document.getElementById("imageSearch").addEventListener("input", (event) => { 1310 | const value = event.target.value 1311 | 1312 | for (let imageName in images) { 1313 | if (imageName.indexOf(value) !== -1) { 1314 | document.getElementById("imageList").selectedIndex = images[imageName].index 1315 | 1316 | setCurrentImage(images[imageName]) 1317 | 1318 | break 1319 | } 1320 | } 1321 | }) 1322 | } 1323 | 1324 | const listenImageCrop = () => { 1325 | document.getElementById("cropImages").addEventListener("click", () => { 1326 | const zip = new JSZip() 1327 | 1328 | let x = 0 1329 | 1330 | for (let imageName in bboxes) { 1331 | const image = images[imageName] 1332 | 1333 | for (let className in bboxes[imageName]) { 1334 | for (let i = 0; i < bboxes[imageName][className].length; i++) { 1335 | x++ 1336 | 1337 | if (x === 1) { 1338 | document.body.style.cursor = "wait" // Mark as busy 1339 | } 1340 | 1341 | const bbox = bboxes[imageName][className][i] 1342 | 1343 | const reader = new FileReader() 1344 | 1345 | reader.addEventListener("load", () => { 1346 | const dataUrl = reader.result 1347 | const imageObject = new Image() 1348 | 1349 | imageObject.addEventListener("load", () => { 1350 | const temporaryCanvas = document.createElement("canvas") 1351 | 1352 | temporaryCanvas.style.display = "none" 1353 | temporaryCanvas.width = bbox.width 1354 | temporaryCanvas.height = bbox.height 1355 | 1356 | temporaryCanvas.getContext("2d").drawImage( 1357 | imageObject, 1358 | bbox.x, 1359 | bbox.y, 1360 | bbox.width, 1361 | bbox.height, 1362 | 0, 1363 | 0, 1364 | bbox.width, 1365 | bbox.height 1366 | ) 1367 | 1368 | temporaryCanvas.toBlob((blob) => { 1369 | const imageNameParts = imageName.split(".") 1370 | 1371 | imageNameParts[imageNameParts.length - 2] += `-${className}-${i}` 1372 | 1373 | zip.file(imageNameParts.join("."), blob) 1374 | 1375 | if (--x === 0) { 1376 | document.body.style.cursor = "default" 1377 | 1378 | zip.generateAsync({type: "blob"}) 1379 | .then((blob) => { 1380 | saveAs(blob, "crops.zip") 1381 | }) 1382 | } 1383 | }, image.meta.type) 1384 | }) 1385 | 1386 | imageObject.src = dataUrl 1387 | }) 1388 | 1389 | reader.readAsDataURL(image.meta) 1390 | } 1391 | } 1392 | } 1393 | }) 1394 | } 1395 | })() 1396 | --------------------------------------------------------------------------------