(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.TerminalKit = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const termkit = require( './termkit.js' ) ;



/*
	Custom 256-colors palette for ScreenBuffer, each color is 24 bits.
	Enhance ScreenBuffer without relying on ScreenBufferHD.

	The original 6x6x6 colors cube shipped with terminal software is rather boring and miss the spot.
	Lot of useless colors, lot of over-saturated hideous colors, and useful colors cannot be shaded/hilighted easily.
	It also wastes plenty of color registers with 24 its grayscale colors... way too much details on this area that is unlikely to be used.

	This new custom palette have 4 regions:

	* The first 16 colors are reserved, it always maps to ANSI colors and depends on the terminal settings entirely.
	* Then comes a 216-color adaptive palette based upon 12 user-provided colors that represent 12 hue of the wheel,
	  with auto-generated variation of shades, tint and desaturation.
	  That is: 12 colors * 6 level of tint/shade * 3 level of desaturation = 216 colors.
	  For maximum reliance, they are computed using the HCL (Lab) colorspace.
	  This provides some good hilight and dim effect.
	* Then comes 13 extra colors fully defined by the user, they must be precise and beautiful colors that are missing
	  in the 216-colors part, and that have great added values.
	* And finally comes 11 gray scale colors (going 0%, 10%, 20%, ..., 100% of HCL lightness)
*/

/*
	Color names scheme:
	* ansi colors: the ANSI color, without prefix or suffix
	* adaptatives colors: '@' + color name + '~'{0,2} (desaturation level) + '+'{0,2} or '-'{0,3} (lightness or brightness level)
	* extra colors: '*' + color name
	* grayscale colors: '@' + color name
*/

const defaultAdaptivePaletteDef = [
	{ names: [ 'red' ] , code: '#e32322' } ,
	{ names: [ 'orange' ] , code: '#f18e1c' } ,
	{ names: [ 'gold' , 'yellow-orange' , 'amber' ] , code: '#fdc60b' } ,
	{ names: [ 'yellow' ] , code: '#f4e500' } ,
	{ names: [ 'chartreuse' , 'yellow-green' ] , code: '#8cbb26' } ,
	{ names: [ 'green' ] , code: '#25ad28' } ,
	{ names: [ 'turquoise' , 'turquoise-green' ] , code: '#1bc17d' } ,
	{ names: [ 'cyan' , 'turquoise-blue' ] , code: '#0dc0cd' } ,
	{ names: [ 'blue' ] , code: '#2a60b0' } ,
	{ names: [ 'indigo' ] , code: '#3b3ba2' } ,
	{ names: [ 'violet' , 'purple' ] , code: '#713795' } ,
	{ names: [ 'magenta' ] , code: '#bd0a7d' }
] ;



// 13 extra colors
const defaultExtraPaletteDef = [
	{ names: [ 'crimson' ] , code: '#dc143c' } ,
	{ names: [ 'vermilion' , 'cinnabar' ] , code: '#e34234' } ,
	{ names: [ 'brown' ] , code: '#a52a2a' } ,
	{ names: [ 'bronze' ] , code: '#cd7f32' } ,
	{ names: [ 'coquelicot' ] , code: '#ff3800' } ,
	//{ names: [ 'flame' ] , code: '#e25822' } ,
	//{ names: [ 'salmon' ] , code: '#ff8c69' } ,
	{ names: [ 'coral-pink' ] , code: '#f88379' } ,
	{ names: [ 'see-green' ] , code: '#2e8b57' } ,
	{ names: [ 'medium-spring-green' ] , code: '#00fa9a' } ,
	{ names: [ 'olivine' ] , code: '#9ab973' } ,
	{ names: [ 'royal-blue' ] , code: '#4169e1' } ,
	{ names: [ 'purple' ] , code: '#800080' } ,
	//{ names: [ 'tyrian-purple' ] , code: '#66023c' } ,
	//{ names: [ 'purple-heart' ] , code: '#69359c' } ,
	{ names: [ 'lavender-purple' ] , code: '#967bb6' } ,
	//{ names: [ 'classic-rose' , 'light-pink' ] , code: '#fbcce7' } ,
	{ names: [ 'pink' ] , code: '#ffc0cb' }
	//{ names: [ 'lime' , 'lemon-lime' ] , code: '#bfff00' } ,
] ;



const ansiColorIndex = {
	black: 0 ,
	red: 1 ,
	green: 2 ,
	yellow: 3 ,
	blue: 4 ,
	magenta: 5 ,
	violet: 5 ,
	cyan: 6 ,
	white: 7 ,
	grey: 8 ,
	gray: 8 ,
	'bright-black': 8 ,
	'bright-red': 9 ,
	'bright-green': 10 ,
	'bright-yellow': 11 ,
	'bright-blue': 12 ,
	'bright-magenta': 13 ,
	'bright-violet': 13 ,
	'bright-cyan': 14 ,
	'bright-white': 15
} ;


function Palette( options = {} ) {
	this.term = options.term || termkit.terminal ;
	this.system = !! options.system ;
	this.adaptivePaletteDef = this.system ? null : options.adaptivePaletteDef || defaultAdaptivePaletteDef ;
	this.extraPaletteDef = this.system ? null : options.extraPaletteDef || defaultExtraPaletteDef ;
	this.escape = [] ;
	this.bgEscape = [] ;
	this.chromaColors = [] ;
	this.colorIndex = {} ;

	// Because that function is often passed as argument... easier to bind it here once for all
	this.colorNameToIndex = this.colorNameToIndex.bind( this ) ;

	this.generate() ;
}

module.exports = Palette ;



Palette.prototype.colorNameToIndex = function( name ) {
	name = name.toLowerCase() ;
	return this.colorIndex[ name ] || termkit.colorNameToIndex( name ) ;
} ;



Palette.prototype.generate = function() {
	this.generateDefaultMapping() ;
	this.generateAnsiColorNames() ;
	this.generateAdaptive() ;
	this.generateExtra() ;
	this.generateGrayscale() ;
} ;



// It just generates default terminal mapping for 256 colors
Palette.prototype.generateDefaultMapping = function() {
	var register ;

	for ( register = 0 ; register < 256 ; register ++ ) {
		this.escape[ register ] = this.term.str.color256( register ) ;
		this.bgEscape[ register ] = this.term.str.bgColor256( register ) ;
	}
} ;



// It just generates default terminal mapping for 256 colors
Palette.prototype.generateAnsiColorNames = function() {
	var name , strippedName ;

	for ( name in ansiColorIndex ) {
		strippedName = name.replace( /-/g , '' ) ;
		this.colorIndex[ name ] = ansiColorIndex[ name ] ;

		if ( strippedName !== name ) {
			this.colorIndex[ strippedName ] = ansiColorIndex[ name ] ;
		}
	}
} ;



Palette.prototype.generateAdaptive = function() {
	if ( this.system ) { return ; }

	var i , j , z , register ,
		baseChromaColors , chromaColor ,
		saturationMark , lightnessMark , suffix ;

	baseChromaColors = this.adaptivePaletteDef.map( color => termkit.chroma( color.code ) ) ;

	register = 16 ;

	for ( z = 0 ; z >= -2 ; z -- ) {
		if ( z > 0 ) {
			saturationMark = '!'.repeat( z ) ;
		}
		else if ( z < 0 ) {
			saturationMark = '~'.repeat( -z ) ;
		}
		else {
			saturationMark = '' ;
		}


		for ( j = 2 ; j >= -3 ; j -- ) {
			if ( j > 0 ) {
				lightnessMark = '+'.repeat( j ) ;
			}
			else if ( j < 0 ) {
				lightnessMark = '-'.repeat( -j ) ;
			}
			else {
				lightnessMark = '' ;
			}

			suffix = saturationMark + lightnessMark ;

			for ( i = 0 ; i < 12 ; i ++ ) {
				chromaColor = this.clStep( baseChromaColors[ i ] , z , j ) ;
				this.addColor( register , chromaColor , this.adaptivePaletteDef[ i ].names , '@' , suffix ) ;
				register ++ ;
			}
		}
	}
} ;



Palette.prototype.generateExtra = function() {
	if ( this.system ) { return ; }

	var i , register ;

	register = 232 ;

	for ( i = 0 ; i < 13 && i < this.extraPaletteDef.length ; i ++ ) {
		this.addColor( register , termkit.chroma( this.extraPaletteDef[ i ].code ) , this.extraPaletteDef[ i ].names , '*' ) ;
		register ++ ;
	}
} ;



const grayscaleNames = [
	[ 'black' ] ,
	[ 'darkest-gray' ] ,
	[ 'darker-gray' ] ,
	[ 'dark-gray' ] ,
	[ 'dark-medium-gray' ] ,
	[ 'medium-gray' , 'gray' ] ,
	[ 'light-medium-gray' ] ,
	[ 'light-gray' ] ,
	[ 'lighter-gray' ] ,
	[ 'lightest-gray' ] ,
	[ 'white' ]
] ;

Palette.prototype.generateGrayscale = function() {
	if ( this.system ) { return ; }

	var i , register , chromaColor ;

	register = 245 ;

	for ( i = 0 ; i <= 10 ; i ++ ) {
		chromaColor = termkit.chroma( 0 , 0 , 10 * i , 'hcl' ) ;
		this.addColor( register , chromaColor , grayscaleNames[ i ] , '@' ) ;
		register ++ ;
	}
} ;



Palette.prototype.getRgb = function( register ) {
	var chromaColor = this.chromaColors[ register ] ;
	if ( ! chromaColor ) { return null ; }
	var [ r , g , b ] = chromaColor.rgb() ;
	return { r , g , b } ;
} ;



Palette.prototype.addColor = function( register , chromaColor , names , prefix = '' , suffix = '' ) {
	var targetRegister ,
		[ r , g , b ] = chromaColor.rgb() ;

	this.chromaColors[ register ] = chromaColor ;

	if ( this.term.support.trueColor ) {
		this.escape[ register ] = this.term.str.colorRgb( r , g , b ) ;
		this.bgEscape[ register ] = this.term.str.bgColorRgb( r , g , b ) ;
	}
	else if ( this.term.support['256colors'] ) {
		targetRegister = this.term.registerForRgb(
			{ r , g , b } ,
			r === g && g === b ? 232 : 0 ,	// minRegister is the start of the grayscale if r=g=b
			255
		) ;

		this.escape[ register ] = this.term.str.color256( targetRegister ) ;
		this.bgEscape[ register ] = this.term.str.bgColor256( targetRegister ) ;
	}
	else {
		targetRegister = this.term.registerForRgb( { r , g , b } , 0 , 15 ) ;
		this.escape[ register ] = this.term.str.color256( targetRegister ) ;
		this.bgEscape[ register ] = this.term.str.bgColor256( targetRegister ) ;
	}

	names.forEach( name => {
		var strippedName = prefix + name.replace( /-/g , '' ) + suffix ;
		name = prefix + name + suffix ;
		this.colorIndex[ name ] = register ;

		if ( strippedName !== name ) {
			this.colorIndex[ strippedName ] = register ;
		}
	} ) ;
} ;



const FIX_STEP = 1.1 ;

Palette.prototype.clStep = function( chromaColor , cAdjust , lAdjust , fixRgb = true ) {
	var c , l , rgb , avg , sortedChannels , preserveLOverC ;

	if ( ! cAdjust && ! lAdjust ) { return chromaColor ; }

	c = chromaColor.get( 'hcl.c' ) ;
	l = chromaColor.get( 'hcl.l' ) ;

	/*
	c += c * cAdjust / 3 ;
	l += l * lAdjust / 4 ;
	//*/

	c *= ( cAdjust > 0 ? 1.6 : 1.7 ) ** cAdjust ;
	l *= ( lAdjust > 0 ? 1.2 : 1.35 ) ** lAdjust ;

	chromaColor = chromaColor.set( 'hcl.c' , c ).set( 'hcl.l' , l ) ;

	if ( ! fixRgb || ! chromaColor.clipped ) { return chromaColor ; }

	// RGB is clipped and should be fixed.
	// The most critical part is when the hue get changed, since it's arguably the most important information.
	// Lightness is somewhat important too, but less than hue and a bit more than the Chroma.
	// Chroma will be preserved if the adjustement is greater on it than on lightness.

	//preserveLOverC = Math.abs( lAdjust ) >= Math.abs( cAdjust ) ;
	preserveLOverC = Math.abs( lAdjust ) >= cAdjust ;

	for ( ;; ) {
		// chromaColor.clipped is not reliable since integer rounding counts as clipping...
		rgb = chromaColor._rgb._unclipped ;
		rgb.length = 3 ;

		if ( rgb.every( channel => channel > -5 && channel < 260 ) ) { return chromaColor ; }

		sortedChannels = [ ... rgb ].sort( ( a , b ) => a - b ) ;

		//console.log( "Clipped!" , rgb , chromaColor.rgb() ) ;

		if ( sortedChannels[ 2 ] >= 256 ) {
			// Clipping will affect hue!
			avg = ( sortedChannels[ 0 ] + sortedChannels[ 1 ] + sortedChannels[ 2 ] ) / 3 ;

			if ( preserveLOverC ) {
				// Desaturate a bit and retry
				c = chromaColor.get( 'hcl.c' ) ;
				c /= FIX_STEP ;
				chromaColor = chromaColor.set( 'hcl.c' , c ) ;
			}
			else {
				// Darken a bit and retry
				l = chromaColor.get( 'hcl.l' ) ;
				l /= FIX_STEP ;
				chromaColor = chromaColor.set( 'hcl.l' , l ) ;
			}

			// It was too bright anyway, let it be clipped
			if ( avg > 255 ) { return chromaColor ; }
		}
		else if ( sortedChannels[ 1 ] < 0 ) {
			// Clipping will affect hue!
			avg = ( sortedChannels[ 0 ] + sortedChannels[ 1 ] + sortedChannels[ 2 ] ) / 3 ;

			if ( preserveLOverC ) {
				// Desaturate a bit and retry
				c = chromaColor.get( 'hcl.c' ) ;
				c /= FIX_STEP ;
				chromaColor = chromaColor.set( 'hcl.c' , c ) ;
			}
			else {
				// Lighten a bit and retry
				l = chromaColor.get( 'hcl.l' ) ;
				l *= FIX_STEP ;
				chromaColor = chromaColor.set( 'hcl.l' , l ) ;
			}

			// It was too dark anyway, let it be clipped
			if ( avg < 0 ) { return chromaColor ; }
		}
		else {
			// This clipping (lowest channel below 0) will not affect hue, only lightness, let it be clipped
			return chromaColor ;
		}
	}
} ;


},{"./termkit.js":56}],2:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const termkit = require( './termkit.js' ) ;



/*
	Rect: rectangular region, clipping, iterators for blitters, etc...

	new Rect( xmin , ymin , xmax , ymax )
	new Rect( object ) having properties: xmin , ymin , xmax , ymax
	new Rect( Terminal )
	new Rect( ScreenBuffer )
*/
function Rect( xmin , ymin , xmax , ymax ) {
	var src = xmin ;

	this.xmin = 0 ;
	this.xmax = 0 ;
	this.ymin = 0 ;
	this.ymax = 0 ;
	this.width = 0 ;
	this.height = 0 ;
	this.isNull = true ;

	if ( src && ( typeof src === 'object' || typeof src === 'function' ) ) {
		if ( src instanceof termkit.Terminal ) {
			this.set( {
				xmin: 1 ,
				ymin: 1 ,
				xmax: src.width ,
				ymax: src.height
			} ) ;
		}
		else if ( src instanceof termkit.ScreenBuffer ) {
			this.set( {
				xmin: 0 ,
				ymin: 0 ,
				xmax: src.width - 1 ,
				ymax: src.height - 1
			} ) ;
		}
		else if ( src instanceof termkit.TextBuffer ) {
			this.set( {
				xmin: 0 ,
				ymin: 0 ,
				xmax: src.width - 1 ,
				ymax: src.height - 1
			} ) ;
		}
		else if ( src instanceof Rect ) {
			this.set( src ) ;
		}
		else if ( src.xmin !== undefined || src.ymin !== undefined || src.xmax !== undefined || src.ymax !== undefined ) {
			this.set( {
				xmin: src.xmin !== undefined ? src.xmin : 0 ,
				ymin: src.ymin !== undefined ? src.ymin : 0 ,
				xmax: src.xmax !== undefined ? src.xmax : 1 ,
				ymax: src.ymax !== undefined ? src.ymax : 1
			} ) ;
		}
		else if ( src.x !== undefined || src.y !== undefined || src.width !== undefined || src.height !== undefined ) {
			this.set( {
				xmin: src.x !== undefined ? src.x : 0 ,
				ymin: src.y !== undefined ? src.y : 0 ,
				xmax: src.width !== undefined ? src.x + src.width - 1 : 1 ,
				ymax: src.height !== undefined ? src.y + src.height - 1 : 1
			} ) ;
		}
	}
	else {
		this.set( {
			xmin: xmin !== undefined ? xmin : 0 ,
			ymin: ymin !== undefined ? ymin : 0 ,
			xmax: xmax !== undefined ? xmax : 1 ,
			ymax: ymax !== undefined ? ymax : 1
		} ) ;
	}
}

module.exports = Rect ;



// Backward compatibility
Rect.create = ( ... args ) => new Rect( ... args ) ;



Rect.prototype.set = function( data ) {
	if ( data.xmin !== undefined ) { this.xmin = Math.floor( data.xmin ) ; }
	if ( data.xmax !== undefined ) { this.xmax = Math.floor( data.xmax ) ; }
	if ( data.ymin !== undefined ) { this.ymin = Math.floor( data.ymin ) ; }
	if ( data.ymax !== undefined ) { this.ymax = Math.floor( data.ymax ) ; }

	this.width = this.xmax - this.xmin + 1 ;
	this.height = this.ymax - this.ymin + 1 ;
	this.isNull = this.xmin > this.xmax || this.ymin > this.ymax ;
} ;



// Set the reset size, keeping *min and adjusting *max
Rect.prototype.setSize = function( data ) {
	if ( data.width !== undefined ) {
		this.width = Math.floor( data.width ) ;
		this.xmax = this.xmin + this.width - 1 ;
	}

	if ( data.height !== undefined ) {
		this.height = Math.floor( data.height ) ;
		this.ymax = this.ymin + this.height - 1 ;
	}

	this.isNull = this.xmin > this.xmax || this.ymin > this.ymax ;
} ;



Rect.prototype.isInside = function( x , y ) {
	return x >= this.xmin && x <= this.xmax && y >= this.ymin && y <= this.ymax ;
} ;



// Clip the src according to the dst, offset* are offsets of the srcRect relative to the dst coordinate system
Rect.prototype.clip = function( dstRect , offsetX , offsetY , dstClipping ) {
	var srcRect = this ;

	offsetX = offsetX || 0 ;
	offsetY = offsetY || 0 ;

	srcRect.set( {
		xmin: Math.max( srcRect.xmin , dstRect.xmin - offsetX ) ,
		ymin: Math.max( srcRect.ymin , dstRect.ymin - offsetY ) ,
		xmax: Math.min( srcRect.xmax , dstRect.xmax - offsetX ) ,
		ymax: Math.min( srcRect.ymax , dstRect.ymax - offsetY )
	} ) ;

	if ( dstClipping ) {
		dstRect.set( {
			xmin: Math.max( dstRect.xmin , srcRect.xmin + offsetX ) ,
			ymin: Math.max( dstRect.ymin , srcRect.ymin + offsetY ) ,
			xmax: Math.min( dstRect.xmax , srcRect.xmax + offsetX ) ,
			ymax: Math.min( dstRect.ymax , srcRect.ymax + offsetY )
		} ) ;
	}

	return this ;
} ;



// Merge with another Rect, enlarge the current Rect so that it includes the Rect argument
Rect.prototype.merge = function( rect ) {
	this.set( {
		xmin: Math.min( this.xmin , rect.xmin ) ,
		ymin: Math.min( this.ymin , rect.ymin ) ,
		xmax: Math.max( this.xmax , rect.xmax ) ,
		ymax: Math.max( this.ymax , rect.ymax )
	} ) ;

	return this ;
} ;



/*
	Given a srcRect, a dstRect, offsetX and offsetY, return an array of up to 4 objects consisting of the same properties
	found in entry, wrapping the src into the dst, i.e. the src is always fully visible in the dst, it is just as if
	the dst where circular

	Mandatory params:
		* dstRect
		* srcRect
		* offsetX
		* offsetY
	Optionnal params:
		* wrapOnly: 'x' , 'y' (only wrap along that axis)
*/
Rect.wrappingRect = function( p ) {
	var regions = [] , nw , ne , sw , se ;


	// Originate, North-West region
	nw = {
		srcRect: new Rect( p.srcRect ) ,
		dstRect: new Rect( p.dstRect ) ,
		offsetX: p.offsetX ,
		offsetY: p.offsetY
	} ;

	// Modulate offsets so they are in-range
	if ( p.wrapOnly !== 'y' ) {
		nw.offsetX = nw.offsetX % p.dstRect.width ;
		if ( nw.offsetX < 0 ) { nw.offsetX += p.dstRect.width ; }
	}

	if ( p.wrapOnly !== 'x' ) {
		nw.offsetY = nw.offsetY % p.dstRect.height ;
		if ( nw.offsetY < 0 ) { nw.offsetY += p.dstRect.height ; }
	}

	// Mutual clipping
	nw.srcRect.clip( nw.dstRect , nw.offsetX , nw.offsetY , true ) ;
	if ( ! nw.srcRect.isNull ) { regions.push( nw ) ; }

	// Wrap-x North-Est region
	if ( nw.srcRect.width < p.srcRect.width && p.wrapOnly !== 'y' ) {
		ne = {
			srcRect: new Rect( p.srcRect ) ,
			dstRect: new Rect( p.dstRect ) ,
			offsetX: nw.offsetX - p.dstRect.width ,
			offsetY: nw.offsetY
		} ;

		// Mutual clipping
		ne.srcRect.clip( ne.dstRect , ne.offsetX , ne.offsetY , true ) ;
		if ( ! ne.srcRect.isNull ) { regions.push( ne ) ; }
	}


	// Wrap-y South-West region
	if ( nw.srcRect.height < p.srcRect.height && p.wrapOnly !== 'x' ) {
		sw = {
			srcRect: new Rect( p.srcRect ) ,
			dstRect: new Rect( p.dstRect ) ,
			offsetX: nw.offsetX ,
			offsetY: nw.offsetY - p.dstRect.height
		} ;

		// Mutual clipping
		sw.srcRect.clip( sw.dstRect , sw.offsetX , sw.offsetY , true ) ;
		if ( ! sw.srcRect.isNull ) { regions.push( sw ) ; }
	}


	// Wrap-x + wrap-y South-Est region, do it only if it has wrapped already
	if ( ne && sw ) {
		se = {
			srcRect: new Rect( p.srcRect ) ,
			dstRect: new Rect( p.dstRect ) ,
			offsetX: nw.offsetX - p.dstRect.width ,
			offsetY: nw.offsetY - p.dstRect.height
		} ;

		// Mutual clipping
		se.srcRect.clip( se.dstRect , se.offsetX , se.offsetY , true ) ;
		if ( ! se.srcRect.isNull ) { regions.push( se ) ; }
	}

	return regions ;
} ;



/*
	This iterator generate synchronous line or cell for dst & src Rect.
	It is totally buffer agnostic.
	Buffer specificities should be added in p.context by the callee.

	Iterator.
	Mandatory params:
		* dstRect: Rect describing the dst geometry
		* srcRect: Rect describing the src geometry
		* type: 'line' or 'cell'
	Optionnal params:
		* context: an object that will be transmitted as is to the iterator
		* dstClipRect: a clipping Rect for the dst
		* srcClipRect: a clipping Rect for the src
		* offsetX: the X-offset of the origin of the srcRect relative to the dst coordinate system
		* offsetY: the Y-offset of the origin of the srcRect relative to the dst coordinate system
		* multiply: the byte size of a cell by which all offset should be multiplied
*/
Rect.regionIterator = function( p , iterator ) {
	var i , j , srcX , srcY , dstX , dstY , srcStart , dstStart , isFullWidth ;

	if ( ! p.multiply ) { p.multiply = 1 ; }
	if ( ! p.offsetX ) { p.offsetX = 0 ; }
	if ( ! p.offsetY ) { p.offsetY = 0 ; }

	if ( p.dstClipRect ) { p.dstClipRect.clip( p.dstRect ) ; }
	else { p.dstClipRect = new Rect( p.dstRect ) ; }

	if ( p.srcClipRect ) { p.srcClipRect.clip( p.srcRect ) ; }
	else { p.srcClipRect = new Rect( p.srcRect ) ; }

	// Mutual clipping
	p.srcClipRect.clip( p.dstClipRect , p.offsetX , p.offsetY , true ) ;

	// If out of bounds, or if everything is clipped away, return now
	if ( p.dstRect.isNull || p.srcClipRect.isNull || p.dstClipRect.isNull ) { return ; }

	switch ( p.type ) {
		case 'line' :
			for ( j = 0 ; j < p.srcClipRect.height ; j ++ ) {
				srcY = p.srcClipRect.ymin + j ;
				dstY = p.dstClipRect.ymin + j ;

				iterator( {
					context: p.context ,
					srcXmin: p.srcClipRect.xmin ,
					srcXmax: p.srcClipRect.xmax ,
					srcY: srcY ,
					srcStart: ( srcY * p.srcRect.width + p.srcClipRect.xmin ) * p.multiply ,
					srcEnd: ( srcY * p.srcRect.width + p.srcClipRect.xmax + 1 ) * p.multiply ,
					dstXmin: p.dstClipRect.xmin ,
					dstXmax: p.dstClipRect.xmax ,
					dstY: dstY ,
					dstStart: ( dstY * p.dstRect.width + p.dstClipRect.xmin ) * p.multiply ,
					dstEnd: ( dstY * p.dstRect.width + p.dstClipRect.xmax + 1 ) * p.multiply
					//, lastLine: j === p.srcClipRect.height - 1
				} ) ;
			}
			break ;

		case 'reversedLine' :
			// Same than 'line' but start from the last line.
			// Useful for copying overlapping region of the same buffer, when the src rect has a lower Y-offset than the dst rect.
			for ( j = p.srcClipRect.height - 1 ; j >= 0 ; j -- ) {
				srcY = p.srcClipRect.ymin + j ;
				dstY = p.dstClipRect.ymin + j ;

				iterator( {
					context: p.context ,
					srcXmin: p.srcClipRect.xmin ,
					srcXmax: p.srcClipRect.xmax ,
					srcY: srcY ,
					srcStart: ( srcY * p.srcRect.width + p.srcClipRect.xmin ) * p.multiply ,
					srcEnd: ( srcY * p.srcRect.width + p.srcClipRect.xmax + 1 ) * p.multiply ,
					dstXmin: p.dstClipRect.xmin ,
					dstXmax: p.dstClipRect.xmax ,
					dstY: dstY ,
					dstStart: ( dstY * p.dstRect.width + p.dstClipRect.xmin ) * p.multiply ,
					dstEnd: ( dstY * p.dstRect.width + p.dstClipRect.xmax + 1 ) * p.multiply
				} ) ;
			}
			break ;

		case 'cell' :
			for ( j = 0 ; j < p.srcClipRect.height ; j ++ ) {
				for ( i = 0 ; i < p.srcClipRect.width ; i ++ ) {
					srcX = p.srcClipRect.xmin + i ;
					srcY = p.srcClipRect.ymin + j ;

					dstX = p.dstClipRect.xmin + i ;
					dstY = p.dstClipRect.ymin + j ;

					srcStart = ( srcY * p.srcRect.width + srcX ) * p.multiply ;
					dstStart = ( dstY * p.dstRect.width + dstX ) * p.multiply ;

					isFullWidth = iterator( {
						context: p.context ,
						srcX: srcX ,
						srcY: srcY ,
						srcStart: srcStart ,
						srcEnd: srcStart + p.multiply ,
						dstX: dstX ,
						dstY: dstY ,
						dstStart: dstStart ,
						dstEnd: dstStart + p.multiply ,
						startOfBlitLine: ! i ,
						endOfBlitLine: i === p.srcClipRect.width - 1
					} ) ;

					if ( isFullWidth ) { i ++ ; }
				}
			}
			break ;
	}
} ;



/*
	This is the tile-variant of the regionIterator.

	Iterator.
	Mandatory params:
		* dstRect
		* srcRect
		* type: 'line' or 'cell'
	Optionnal params:
		* context: an object that will be transmitted as is to the iterator
		* dstClipRect
		* srcClipRect
		* offsetX
		* offsetY
		* multiply
*/
Rect.tileIterator = function( p , iterator ) {
	var srcI , srcJ , srcX , srcY , dstI , dstJ , dstX , dstY , streak , srcStart , dstStart ;

	if ( ! p.multiply ) { p.multiply = 1 ; }
	if ( ! p.offsetX ) { p.offsetX = 0 ; }
	if ( ! p.offsetY ) { p.offsetY = 0 ; }

	if ( p.dstClipRect ) { p.dstClipRect.clip( p.dstRect ) ; }
	else { p.dstClipRect = new Rect( p.dstRect ) ; }

	if ( p.srcClipRect ) { p.srcClipRect.clip( p.srcRect ) ; }
	else { p.srcClipRect = new Rect( p.srcRect ) ; }


	switch ( p.type ) {
		case 'cell' :
			for ( dstJ = 0 ; dstJ < p.dstClipRect.height ; dstJ ++ ) {
				srcJ = ( dstJ - p.offsetY ) % p.srcClipRect.height ;
				if ( srcJ < 0 ) { srcJ += p.srcClipRect.height ; }

				for ( dstI = 0 ; dstI < p.dstClipRect.width ; dstI ++ ) {
					srcI = ( dstI - p.offsetX ) % p.srcClipRect.width ;
					if ( srcI < 0 ) { srcI += p.srcClipRect.width ; }

					srcX = p.srcClipRect.xmin + srcI ;
					srcY = p.srcClipRect.ymin + srcJ ;

					dstX = p.dstClipRect.xmin + dstI ;
					dstY = p.dstClipRect.ymin + dstJ ;

					srcStart = ( srcY * p.srcRect.width + srcX ) * p.multiply ;
					dstStart = ( dstY * p.dstRect.width + dstX ) * p.multiply ;

					iterator( {
						context: p.context ,
						srcX: srcX ,
						srcY: srcY ,
						srcStart: srcStart ,
						srcEnd: srcStart + p.multiply ,
						dstX: dstX ,
						dstY: dstY ,
						dstStart: dstStart ,
						dstEnd: dstStart + p.multiply
					} ) ;
				}
			}
			break ;

		case 'line' :
			for ( dstJ = 0 ; dstJ < p.dstClipRect.height ; dstJ ++ ) {
				srcJ = ( dstJ - p.offsetY ) % p.srcClipRect.height ;
				if ( srcJ < 0 ) { srcJ += p.srcClipRect.height ; }

				dstI = 0 ;
				while ( dstI < p.dstClipRect.width ) {
					srcI = ( dstI - p.offsetX ) % p.srcClipRect.width ;
					if ( srcI < 0 ) { srcI += p.srcClipRect.width ; }

					streak = Math.min( p.srcClipRect.width - srcI , p.dstClipRect.width - dstI ) ;

					srcX = p.srcClipRect.xmin + srcI ;
					srcY = p.srcClipRect.ymin + srcJ ;

					dstX = p.dstClipRect.xmin + dstI ;
					dstY = p.dstClipRect.ymin + dstJ ;

					srcStart = ( srcY * p.srcRect.width + srcX ) * p.multiply ;
					dstStart = ( dstY * p.dstRect.width + dstX ) * p.multiply ;

					iterator( {
						context: p.context ,
						srcXmin: srcX ,
						srcXmax: srcX + streak - 1 ,
						srcY: srcY ,
						srcStart: srcStart ,
						srcEnd: srcStart + streak * p.multiply ,
						dstXmin: dstX ,
						dstXmax: dstX + streak - 1 ,
						dstY: dstY ,
						dstStart: dstStart ,
						dstEnd: dstStart + streak * p.multiply
					} ) ;

					dstI += streak ;
				}
			}
			break ;
	}
} ;



/*
	This is the wrap-variant of the regionIterator.

	Iterator.
	Mandatory params:
		* dstRect
		* srcRect
		* type: 'line' or 'cell'
	Optionnal params:
		* context: an object that will be transmitted as is to the iterator
		* dstClipRect
		* srcClipRect
		* offsetX
		* offsetY
		* multiply
		* wrapOnly: 'x' , 'y' (only wrap along that axis)
*/
Rect.wrapIterator = function( p , iterator ) {
	var i , regions ;

	regions = Rect.wrappingRect( {
		dstRect: p.dstClipRect ,
		srcRect: p.srcClipRect ,
		offsetX: p.offsetX ,
		offsetY: p.offsetY ,
		wrapOnly: p.wrap
	} ) ;

	for ( i = 0 ; i < regions.length ; i ++ ) {
		p.dstClipRect = regions[ i ].dstRect ;
		p.srcClipRect = regions[ i ].srcRect ;
		p.offsetX = regions[ i ].offsetX ;
		p.offsetY = regions[ i ].offsetY ;

		Rect.regionIterator( p , iterator ) ;
	}
} ;


},{"./termkit.js":56}],3:[function(require,module,exports){
(function (Buffer){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const misc = require( './misc.js' ) ;

const fs = require( 'fs' ) ;
const string = require( 'string-kit' ) ;
const NextGenEvents = require( 'nextgen-events' ) ;



/*
	options:
		* width: buffer width (default to dst.width)
		* height: buffer height (default to dst.height)
		* dst: writting destination
		* inline: for terminal dst only, draw inline instead of at some position (do not moveTo)
		* x: default position in the dst
		* y: default position in the dst
		* wrap: default wrapping behavior of .put()
		* noFill: do not call .fill() with default values at ScreenBuffer creation
		* blending: false/null or true or object (blending options): default blending params (can be overriden by .draw())
		* palette: Palette instance
*/
function ScreenBuffer( options = {} ) {
	this.dst = options.dst ;	// a terminal or another screenBuffer
	this.inline = !! options.inline ;	// it's a terminal and we want to draw inline to it (no moveTo)
	this.width = Math.floor( options.width ) || ( options.dst ? options.dst.width : 1 ) ;
	this.height = Math.floor( options.height ) || ( options.dst ? options.dst.height : 1 ) ;
	this.x = options.x !== undefined ? options.x : ( options.dst && options.dst instanceof termkit.Terminal ? 1 : 0 ) ;	// eslint-disable-line
	this.y = options.y !== undefined ? options.y : ( options.dst && options.dst instanceof termkit.Terminal ? 1 : 0 ) ;	// eslint-disable-line
	this.cx = 0 ;
	this.cy = 0 ;
	this.ch = false ;	// cursor hidden
	this.lastCh = null ;	// cursor hidden on last terminal draw, avoid unecessary escape sequence output
	this.lastBuffer = null ;
	this.lastBufferUpToDate = false ;
	this.blending = options.blending || false ;
	this.wrap = !! options.wrap ;
	this.buffer = Buffer.allocUnsafe( this.width * this.height * this.ITEM_SIZE ) ;

	this.palette = options.palette || ( this.dst && this.dst.palette ) ;

	if ( ! options.noFill ) { this.fill() ; }
}

module.exports = ScreenBuffer ;

ScreenBuffer.prototype = Object.create( NextGenEvents.prototype ) ;
ScreenBuffer.prototype.constructor = ScreenBuffer ;
ScreenBuffer.prototype.bitsPerColor = 8 ;

// Backward compatibility
ScreenBuffer.create = ( ... args ) => new ScreenBuffer( ... args ) ;



const termkit = require( './termkit.js' ) ;
const Rect = termkit.Rect ;



/*
	options:
		* attr: attributes passed to .put()
		* transparencyChar: a char that is transparent
		* transparencyType: bit flags for the transparency char
*/
ScreenBuffer.createFromString = function( options , data ) {
	var x , y , length , attr , attrTrans , width , height , lineWidth , screenBuffer ;

	// Manage options
	if ( ! options ) { options = {} ; }

	if ( typeof data !== 'string' ) {
		if ( ! data.toString ) { throw new Error( '[terminal] ScreenBuffer.createFromDataString(): argument #1 should be a string or provide a .toString() method.' ) ; }
		data = data.toString() ;
	}

	// Transform the data into an array of lines
	data = termkit.stripControlChars( data , true ).split( '\n' ) ;

	// Compute the buffer size
	width = 0 ;
	height = data.length ;

	attr = options.attr !== undefined ? options.attr : ScreenBuffer.prototype.DEFAULT_ATTR ;
	if ( attr && typeof attr === 'object' && ! attr.BYTES_PER_ELEMENT ) { attr = ScreenBuffer.object2attr( attr ) ; }

	attrTrans = attr ;

	if ( options.transparencyChar ) {
		if ( ! options.transparencyType ) { attrTrans |= TRANSPARENCY ; }
		else { attrTrans |= options.transparencyType & TRANSPARENCY ; }
	}

	// Compute the width of the screenBuffer
	for ( y = 0 ; y < data.length ; y ++ ) {
		lineWidth = string.unicode.width( data[ y ] ) ;
		if ( lineWidth > width ) { width = lineWidth ; }
	}

	// Create the buffer with the right width & height
	screenBuffer = new ScreenBuffer( { width: width , height: height } ) ;

	// Fill the buffer with data
	for ( y = 0 ; y < data.length ; y ++ ) {
		if ( ! options.transparencyChar ) {
			screenBuffer.put( { x: 0 , y: y , attr: attr } , data[ y ] ) ;
		}
		else {
			length = data[ y ].length ;

			for ( x = 0 ; x < length ; x ++ ) {
				if ( data[ y ][ x ] === options.transparencyChar ) {
					screenBuffer.put( { x: x , y: y , attr: attrTrans } , data[ y ][ x ] ) ;
				}
				else {
					screenBuffer.put( { x: x , y: y , attr: attr } , data[ y ][ x ] ) ;
				}
			}
		}
	}

	return screenBuffer ;
} ;



// Backward compatibility
ScreenBuffer.createFromChars = ScreenBuffer.createFromString ;



// Shared
ScreenBuffer.prototype.setClearAttr = function( attr ) {
	this.CLEAR_ATTR = this.object2attr( attr ) ;
	this.CLEAR_BUFFER = Buffer.allocUnsafe( this.ITEM_SIZE ) ;

	if ( Buffer.isBuffer( this.CLEAR_ATTR ) ) {
		// ScreenBufferHD
		this.CLEAR_ATTR.copy( this.CLEAR_BUFFER ) ;
	}
	else { // if ( this.ATTR_SIZE === 4 ) {
		this.CLEAR_BUFFER.writeInt32BE( this.CLEAR_ATTR , 0 ) ;
	}

	this.CLEAR_BUFFER.write( ' \x00\x00\x00' , this.ATTR_SIZE ) ;	// space
} ;



/*
	options:
		attr: optional, the attribute to fill (default to DEFAULT_ATTR)
		char: optional, the buffer will be filled with that char (default to space)
		region: optional, a Rect compliant object defining the region to fill, instead a filling the whole ScreenBuffer
		start: optional (internal), start offset
		end: optional (internal), end offset
		clearBuffer: optional (internal), a Buffer to use to clear (instead of char+attr)
		buffer: optional (internal), used when we want to clear a Buffer instance, not a ScreenBuffer instance
*/
// Shared
ScreenBuffer.prototype.fill = function( options ) {
	var i , attr , char , start , end , region ,
		srcRect , toRect ,
		clearBuffer = this.CLEAR_BUFFER ,
		buffer = this.buffer ;

	if ( options && typeof options === 'object' ) {
		if ( options.char || options.attr ) {
			clearBuffer = Buffer.allocUnsafe( this.ITEM_SIZE ) ;

			// Write the attributes
			attr = options.attr !== undefined ? options.attr : this.DEFAULT_ATTR ;
			if ( attr && typeof attr === 'object' && ! attr.BYTES_PER_ELEMENT ) { attr = this.object2attr( attr ) ; }

			this.writeAttr( clearBuffer , attr , 0 ) ;

			// Write the character
			char = options.char && typeof options.char === 'string' ? options.char : ' ' ;
			//char = punycode.ucs2.encode( [ punycode.ucs2.decode( termkit.stripControlChars( char ) )[ 0 ] ] ) ;
			char = string.unicode.firstChar( termkit.stripControlChars( char ) ) ;

			//clearBuffer.write( char , this.ATTR_SIZE , this.CHAR_SIZE ) ;
			this.writeChar( clearBuffer , char , 0 ) ;
		}
		else if ( options.clearBuffer ) {
			clearBuffer = options.clearBuffer ;
		}

		// This option is used when we want to clear a Buffer instance, not a ScreenBuffer instance
		if ( options.buffer ) { buffer = options.buffer ; }

		start = options.start ? Math.floor( options.start / this.ITEM_SIZE ) : 0 ;
		end = options.end ? Math.floor( options.end / this.ITEM_SIZE ) : buffer.length / this.ITEM_SIZE ;
		region = options.region ? options.region : null ;
	}
	else {
		start = 0 ;
		end = buffer.length / this.ITEM_SIZE ;
	}

	if ( region ) {
		srcRect = new Rect( 0 , 0 , 0 , 0 ) ;

		toRect = new Rect( region ) ;
		toRect.clip( new Rect( this ) ) ;
		if ( toRect.isNull ) { return ; }

		// We use the blitter to fill the region
		Rect.tileIterator( {
			type: 'line' ,
			context: { srcBuffer: clearBuffer , dstBuffer: this.buffer } ,
			srcRect: srcRect ,
			dstRect: new Rect( this ) ,
			dstClipRect: toRect ,
			multiply: this.ITEM_SIZE
		} , this.blitterLineIterator.bind( this ) ) ;
	}
	else {
		for ( i = start ; i < end ; i ++ ) {
			clearBuffer.copy( buffer , i * this.ITEM_SIZE ) ;
		}
	}
} ;



// Clear the buffer: fill it with blank
// Shared
ScreenBuffer.prototype.clear = ScreenBuffer.prototype.fill ;



ScreenBuffer.prototype.preserveMarkupFormat = misc.preserveMarkupFormat ;
ScreenBuffer.prototype.parseMarkup = string.markupMethod.bind( misc.markupOptions ) ;



/*
	put( options , str )
	put( options , format , [arg1] , [arg2] , ... )

	options:
		* x: bypass this.cx
		* y: bypass this.cy
		* markup: boolean or 'ansi' or 'legacyAnsi', true if the text contains markup that should be interpreted,
		 'ansi' if it contains ansi code, 'legacyAnsi' is bold is bright fg and blink is bright bg
		* attr: standard attributes
		* resumeAttr: (internal) attr code to resume to
		* wrap: text wrapping, when the cursor move beyond the last column, it is moved to the begining of the next line
		* newLine: if true, then \r and \n produce new lines, false by default: .put() does not manage lines
		* clip: if set, it is object describing the clipping area, nothing is written outside of it,
		  and if the 'wrap' option is set, wrapping is done on its boundary.
		  Properties:
			* x
			* y
			* width
			* height
		* clipChar: a char that is display just before clipping something
		* direction: 'right' (default), 'left', 'up', 'down' or 'none'/null (do not move after puting a char)
		* dx: x increment after each character (default: 1)
		* dy: y increment after each character (default: 0)
*/
// Shared
ScreenBuffer.prototype.put = function( options , str , ... args ) {
	var startX , startY , x , y , dx , dy , baseAttr , attr , attrObject , wrap ,
		lastValidOffset = -1 ,
		xmin = 0 ,
		ymin = 0 ,
		xmax = this.width - 1 ,
		ymax = this.height - 1 ;

	// Manage options
	if ( ! options ) { options = {} ; }

	wrap = options.wrap !== undefined ? options.wrap : this.wrap ;

	startX = x = Math.floor( options.x !== undefined ? options.x : this.cx ) ;
	startY = y = Math.floor( options.y !== undefined ? options.y : this.cy ) ;

	if ( options.clip ) {
		xmin = options.clip.x ;
		ymin = options.clip.y ;
		xmax = xmin + options.clip.width - 1 ;
		ymax = ymin + options.clip.height - 1 ;
	}

	// Process directions/increments

	switch ( options.direction ) {
		//case 'right' : // not needed, use the default dx & dy
		case 'left' :
			dx = -1 ;
			break ;
		case 'up' :
			dx = 0 ;
			dy = -1 ;
			break ;
		case 'down' :
			dx = 0 ;
			dy = 1 ;
			break ;
		case null :
		case 'none' :
			dx = 0 ;
			dy = 0 ;
			break ;
		case 'right' :
		default :
			dx = 1 ;
			dy = 0 ;
			break ;
	}

	// Overide
	if ( typeof options.dx === 'number' ) { dx = options.dx ; }
	if ( typeof options.dy === 'number' ) { dy = options.dy ; }


	// Process attributes
	attr = options.attr !== undefined ? options.attr : this.DEFAULT_ATTR ;
	if ( attr && typeof attr === 'object' && ! attr.BYTES_PER_ELEMENT ) { attr = this.object2attr( attr ) ; }
	baseAttr = attr ;

	// It's already in the correct format
	if ( options.resumeAttr !== undefined ) { attr = options.resumeAttr ; }


	// Process the input string
	if ( typeof str !== 'string' ) {
		if ( str.toString ) { str = str.toString() ; }
		else { return ; }
	}

	if ( args.length ) {
		str = options.markup === true ? this.preserveMarkupFormat( str , ... args ) : string.format( str , ... args ) ;
	}



	// The processing of raw chunk of text
	var processRaw = part => {
		//part = termkit.stripControlChars( part ) ;

		var characters = string.unicode.toArray( part ) ,
			iMax = characters.length ;

		for ( let i = 0 ; i < iMax ; i ++ ) {
			let offset = ( y * this.width + x ) * this.ITEM_SIZE ;
			let char = characters[ i ] ;
			let charCode = char.charCodeAt( 0 ) ;

			if ( charCode < 0x20 || charCode === 0x7f ) {
				if ( options.newLine && ( charCode === 0x0a || charCode === 0x0d ) ) {
					if ( dx ) {
						x = startX ;
						y ++ ;
					}
					else {
						y = startY ;
						x ++ ;
					}

					continue ;
				}
				else {
					char = ' ' ;	// Space
					charCode = 0x20 ;
				}
			}

			let isFullWidth = string.unicode.isFullWidth( char ) ;
			let inBounds = false ;

			if ( y >= ymin && y <= ymax ) {
				if ( isFullWidth ) {
					if ( x + isFullWidth * dx >= xmin && x + isFullWidth * ( dx || 1 ) <= xmax ) {
						// This is a full-width char! Needs extra care!

						if ( dx < 0 ) { offset -= this.ITEM_SIZE ; }
						lastValidOffset = offset ;

						// Check if we are writing on a fullwidth char
						if ( this.hasTrailingFullWidth( this.buffer , offset ) && x ) { this.removeFullWidth( this.buffer , offset - this.ITEM_SIZE ) ; }

						// Write the attributes
						this.writeAttr( this.buffer , attr , offset , this.LEADING_FULLWIDTH ) ;

						// Write the character
						this.writeChar( this.buffer , char , offset ) ;

						offset += this.ITEM_SIZE ;

						// Check if we are writing on a fullwidth char
						if ( this.hasLeadingFullWidth( this.buffer , offset ) && x < xmax ) { this.removeFullWidth( this.buffer , offset + this.ITEM_SIZE ) ; }

						// Write the attributes
						this.writeAttr( this.buffer , attr , offset , this.TRAILING_FULLWIDTH ) ;

						// Write a blank character
						this.writeChar( this.buffer , ' ' , offset ) ;

						inBounds = true ;
					}
				}
				else {
					if ( x >= xmin && x <= xmax ) {
						// Check if we are writing on a fullwidth char
						if ( this.hasLeadingFullWidth( this.buffer , offset ) && x < xmax ) { this.removeFullWidth( this.buffer , offset + this.ITEM_SIZE ) ; }
						else if ( this.hasTrailingFullWidth( this.buffer , offset ) && x ) { this.removeFullWidth( this.buffer , offset - this.ITEM_SIZE ) ; }

						// Write the attributes
						this.writeAttr( this.buffer , attr , offset ) ;

						// Write the character
						this.writeChar( this.buffer , char , offset ) ;

						lastValidOffset = offset ;
						inBounds = true ;
					}
				}
			}
			else {
				// Optimization : early out when there is no chance anymore char would be put...
				if ( ( y > ymax && dy >= 0 ) || ( y < ymin && dy < 0 ) ) {
					return true ;
				}
			}

			if ( ! inBounds && lastValidOffset >= 0 && options.clipChar ) {
				// Check if we are writing on a fullwidth char
				if ( this.hasLeadingFullWidth( this.buffer , lastValidOffset ) && x < xmax ) { this.removeFullWidth( this.buffer , lastValidOffset + this.ITEM_SIZE ) ; }
				else if ( this.hasTrailingFullWidth( this.buffer , lastValidOffset ) && x ) { this.removeFullWidth( this.buffer , lastValidOffset - this.ITEM_SIZE ) ; }

				// Write the character
				this.writeChar( this.buffer , options.clipChar , lastValidOffset ) ;

				lastValidOffset = -1 ;
			}

			x += dx * ( 1 + isFullWidth ) ;
			y += dy ;

			if ( wrap ) {
				if ( x < xmin ) {
					x = xmax ;
					y -- ;
				}
				else if ( x > xmax ) {
					x = xmin ;
					y ++ ;
				}
			}
		}
	} ;


	if ( ! options.markup ) {
		processRaw( str ) ;
	}
	else {
		const defaultAttrObject = this.attr2object( this.DEFAULT_ATTR ) ;
		const baseAttrObject = this.attr2object( baseAttr ) ;

		let legacyColor = false ;
		let parts = null ;

		switch ( options.markup ) {
			case 'ansi' :
				parts = string.ansi.parse( str ) ;
				break ;
			case 'legacyAnsi' :
				parts = string.ansi.parse( str ) ;
				legacyColor = true ;
				break ;
			case true :
				parts = this.parseMarkup( str ) ;
				break ;
		}

		for ( let part of parts ) {
			attrObject = Object.assign( {} , part.specialReset ? defaultAttrObject : baseAttrObject , part ) ;
			delete attrObject.text ;

			// Remove incompatible flags
			if ( attrObject.defaultColor && attrObject.color ) { delete attrObject.defaultColor ; }
			if ( attrObject.bgDefaultColor && attrObject.bgColor ) { delete attrObject.bgDefaultColor ; }

			attr = this.object2attr( attrObject , undefined , legacyColor ) ;

			if ( part.text ) {
				if ( processRaw( part.text ) ) { break ; }
			}
		}
	}

	this.cx = x ;
	this.cy = y ;

	return attr ;
} ;



/*
	options:
		* x: bypass this.cx
		* y: bypass this.cy
*/
// Shared
ScreenBuffer.prototype.get = function( options ) {
	var x , y , offset ;

	// Manage options
	if ( ! options ) { options = {} ; }

	x = options.x !== undefined ? options.x : this.cx ;
	y = options.y !== undefined ? options.y : this.cy ;

	if ( typeof x !== 'number' || x < 0 || x >= this.width ) { return null ; }
	x = Math.floor( x ) ;

	if ( typeof y !== 'number' || y < 0 || y >= this.height ) { return null ; }
	y = Math.floor( y ) ;

	offset = ( y * this.width + x ) * this.ITEM_SIZE ;

	return {
		attr: this.attr2object( this.readAttr( this.buffer , offset ) ) ,
		char: this.readChar( this.buffer , offset )
	} ;
} ;



// Resize a screenBuffer, using a Rect
// Shared
ScreenBuffer.prototype.resize = function( fromRect ) {
	// Do not reference directly the userland variable, clone it
	fromRect = new Rect( fromRect ) ;

	var offsetX = -fromRect.xmin ,
		offsetY = -fromRect.ymin ;

	// Create the toRect region
	var toRect = new Rect( {
		xmin: 0 ,
		ymin: 0 ,
		xmax: fromRect.width - 1 ,
		ymax: fromRect.height - 1
	} ) ;

	fromRect.clip( new Rect( this ) ) ;

	if ( toRect.isNull ) { return false ; }

	// Generate a new buffer
	var resizedBuffer = Buffer.allocUnsafe( toRect.width * toRect.height * this.ITEM_SIZE ) ;
	this.fill( { buffer: resizedBuffer } ) ;

	// We use the blitter to reconstruct the buffer geometry
	Rect.regionIterator( {
		type: 'line' ,
		context: { srcBuffer: this.buffer , dstBuffer: resizedBuffer } ,
		dstRect: toRect ,
		dstClipRect: new Rect( toRect ) ,
		srcRect: new Rect( this ) ,
		srcClipRect: fromRect ,
		offsetX: offsetX ,
		offsetY: offsetY ,
		multiply: this.ITEM_SIZE
	} , this.blitterLineIterator.bind( this ) ) ;

	// Now, we have to replace the old buffer with the new, and set the width & height
	this.width = toRect.width ;
	this.height = toRect.height ;
	this.buffer = resizedBuffer ;

	// Disable the lastBuffer, so `draw( { delta: true } )` will not be bugged
	this.lastBuffer = null ;

	// This exists to improve compatibilities with the Terminal object
	this.emit( 'resize' , this.width , this.height ) ;

	return true ;
} ;



// Shared
ScreenBuffer.prototype.draw = function( options ) {
	if ( ! options || typeof options !== 'object' ) { options = {} ; }

	// Transmitted options (do not edit the user provided options, clone them)
	var tr = {
		dst: options.dst || this.dst ,
		inline: options.inline !== undefined ? !! options.inline : this.inline ,
		offsetX: options.x !== undefined ? Math.floor( options.x ) : Math.floor( this.x ) ,
		offsetY: options.y !== undefined ? Math.floor( options.y ) : Math.floor( this.y ) ,
		dstClipRect: options.dstClipRect ? new Rect( options.dstClipRect ) : undefined ,
		srcClipRect: options.srcClipRect ? new Rect( options.srcClipRect ) : undefined ,
		delta: options.delta ,
		blending: options.blending !== undefined ? options.blending : this.blending ,
		wrap: options.wrap ,
		tile: options.tile
	} ;

	if ( tr.dst instanceof ScreenBuffer ) {
		return this.blitter( tr ) ;
	}
	else if ( tr.dst instanceof termkit.Terminal ) {
		return this.terminalBlitter( tr ) ;
	}
} ;



// Shared
ScreenBuffer.prototype.moveTo = function( x , y ) {
	this.cx = Math.max( 0 , Math.min( x , this.width - 1 ) ) ;
	this.cy = Math.max( 0 , Math.min( y , this.height - 1 ) ) ;
} ;



// Shared
ScreenBuffer.prototype.drawCursor = function( options ) {
	if ( ! options || typeof options !== 'object' ) { options = {} ; }

	var dst = options.dst || this.dst ;

	if ( dst instanceof ScreenBuffer ) {
		if ( this.ch ) {
			dst.ch = true ;
		}
		else {
			dst.ch = false ;
			dst.moveTo( this.cx + this.x , this.cy + this.y ) ;
		}
	}
	else if ( dst instanceof termkit.Terminal ) {
		if ( this.ch ) {
			if ( this.ch !== this.lastCh ) { dst.hideCursor() ; }
		}
		else {
			if ( this.ch !== this.lastCh ) { dst.hideCursor( false ) ; }
			dst.moveTo(
				Math.max( 1 , Math.min( this.cx + this.x , dst.width ) ) ,
				Math.max( 1 , Math.min( this.cy + this.y , dst.height ) )
			) ;
		}

		this.lastCh = this.ch ;
	}
} ;



// Shared
ScreenBuffer.prototype.blitter = function( p ) {
	var tr , iterator , iteratorCallback ;

	// Default options & iterator
	tr = {
		type: 'line' ,
		context: { srcBuffer: this.buffer , dstBuffer: p.dst.buffer , blending: p.blending } ,
		dstRect: new Rect( p.dst ) ,
		srcRect: new Rect( this ) ,
		dstClipRect: p.dstClipRect || new Rect( p.dst ) ,
		srcClipRect: p.srcClipRect || new Rect( this ) ,
		offsetX: p.offsetX ,
		offsetY: p.offsetY ,
		wrap: p.wrap ,
		tile: p.tile ,
		multiply: this.ITEM_SIZE
	} ;

	iterator = 'regionIterator' ;
	iteratorCallback = this.blitterLineIterator.bind( this ) ;


	// If blending is on, switch to the cell iterator
	if ( p.blending ) {
		tr.type = 'cell' ;
		iteratorCallback = this.blitterCellBlendingIterator.bind( this ) ;
	}

	if ( p.wrap ) { iterator = 'wrapIterator' ; }
	else if ( p.tile ) { iterator = 'tileIterator' ; }
	else { iterator = 'regionIterator' ; }

	Rect[ iterator ]( tr , iteratorCallback ) ;
} ;



// /!\ WARNING: We have to check full-width char in the previous dst x, and strip full-width at the end of the src
// Shared
ScreenBuffer.prototype.blitterLineIterator = function( p ) {
	if ( p.dstStart >= this.ITEM_SIZE ) {
		// Remove overlapping dst fullwidth at the begining
		this.removeLeadingFullWidth( p.context.dstBuffer , p.dstStart - this.ITEM_SIZE ) ;
	}

	p.context.srcBuffer.copy( p.context.dstBuffer , p.dstStart , p.srcStart , p.srcEnd ) ;
	this.removeLeadingFullWidth( p.context.dstBuffer , p.dstEnd - this.ITEM_SIZE ) ;

	if ( p.dstEnd < p.context.dstBuffer.length ) {
		// Remove overlapping dst fullwidth at the end
		this.removeTrailingFullWidth( p.context.dstBuffer , p.dstEnd ) ;
	}
} ;



ScreenBuffer.prototype.blitterCellBlendingIterator = function( p ) {
	var attr = this.readAttr( p.context.srcBuffer , p.srcStart ) ,
		blending = attr & TRANSPARENCY ;

	if ( blending === TRANSPARENCY ) {
		// Fully transparent, do nothing
		return ;
	}

	// First, manage fullwidth chars
	if ( p.startOfBlitLine && p.dstStart >= this.ITEM_SIZE ) {
		// Remove overlapping dst fullwidth at the begining
		this.removeLeadingFullWidth( p.context.dstBuffer , p.dstStart - this.ITEM_SIZE ) ;
	}

	if ( p.endOfBlitLine && p.dstEnd < p.context.dstBuffer.length ) {
		// Remove overlapping dst fullwidth at the end
		this.removeTrailingFullWidth( p.context.dstBuffer , p.dstEnd ) ;
	}

	if ( blending === NONE && ! ( p.endOfBlitLine || ! ( attr & LEADING_FULLWIDTH ) ) ) {
		// Fully opaque, copy it
		p.context.srcBuffer.copy( p.context.dstBuffer , p.dstStart , p.srcStart , p.srcEnd ) ;
		return ;
	}


	// Blending part...

	if ( ! ( blending & FG_TRANSPARENCY ) ) {
		// Copy source foreground color
		p.context.srcBuffer.copy(
			p.context.dstBuffer ,
			p.dstStart + 3 ,
			p.srcStart + 3 ,
			p.srcStart + 4
		) ;
	}

	if ( ! ( blending & BG_TRANSPARENCY ) ) {
		// Copy source background color
		p.context.srcBuffer.copy(
			p.context.dstBuffer ,
			p.dstStart + 2 ,
			p.srcStart + 2 ,
			p.srcStart + 3
		) ;
	}

	if ( ! ( blending & STYLE_TRANSPARENCY ) ) {
		// Copy source style
		p.context.srcBuffer.copy(
			p.context.dstBuffer ,
			p.dstStart + 1 ,
			p.srcStart + 1 ,
			p.srcStart + 2
		) ;
	}

	if ( ! ( blending & CHAR_TRANSPARENCY ) ) {
		if ( p.endOfBlitLine && ( attr & LEADING_FULLWIDTH ) ) {
			// Leading fullwidth at the end of the blit line, output a space instead
			this.writeChar( p.context.dstBuffer , ' ' , p.dstStart ) ;
		}
		else {
			// Copy source character
			p.context.srcBuffer.copy(
				p.context.dstBuffer ,
				p.dstStart + this.ATTR_SIZE ,
				p.srcStart + this.ATTR_SIZE ,
				p.srcEnd
			) ;
		}
	}
} ;



// Shared
ScreenBuffer.prototype.terminalBlitter = function( p ) {
	var tr , iterator , iteratorCallback , context ;

	context = {
		srcBuffer: this.buffer ,
		blending: p.blending ,
		term: p.dst ,
		inline: p.inline ,
		deltaEscapeSequence: p.dst.support.deltaEscapeSequence ,
		rawTerm: p.dst.raw ,
		lastAttr: null ,
		sequence: '' ,
		cells: 0 ,
		moves: 0 ,
		attrs: 0 ,
		writes: 0
	} ;

	// Default options & iterator
	tr = {
		type: 'line' ,
		context: context ,
		dstRect: new Rect( p.dst ) ,
		srcRect: new Rect( this ) ,
		dstClipRect: p.dstClipRect ,
		srcClipRect: p.srcClipRect ,
		offsetX: p.offsetX ,
		offsetY: p.offsetY ,
		multiply: this.ITEM_SIZE
	} ;

	// If in inline mode, the height is virtually infinity
	if ( p.inline ) { tr.dstRect.setSize( { height: Infinity } ) ; }

	if ( p.delta && ! p.inline ) {
		if ( ! this.lastBuffer || this.lastBuffer.length !== this.buffer.length ) {
			this.lastBuffer = Buffer.from( this.buffer ) ;
			iteratorCallback = this.terminalBlitterLineIterator.bind( this ) ;
		}
		else if ( this.lastBufferUpToDate ) {
			context.srcLastBuffer = this.lastBuffer ;

			iteratorCallback = this.terminalBlitterCellIterator.bind( this ) ;
			tr.type = 'cell' ;
		}
		else {
			this.buffer.copy( this.lastBuffer ) ;
			iteratorCallback = this.terminalBlitterLineIterator.bind( this ) ;
		}

		this.lastBufferUpToDate = true ;
	}
	else {
		this.lastBufferUpToDate = false ;
		iteratorCallback = this.terminalBlitterLineIterator.bind( this ) ;
	}


	if ( p.wrap ) { iterator = 'wrapIterator' ; }
	else if ( p.tile ) { iterator = 'tileIterator' ; }
	else { iterator = 'regionIterator' ; }

	Rect[ iterator ]( tr , iteratorCallback ) ;

	// Write remaining sequence
	if ( context.sequence.length ) { context.rawTerm( context.sequence ) ; context.writes ++ ; }

	// Copy buffer to lastBuffer
	// Already done by terminalBlitterCellIterator()
	// if ( p.delta ) { this.buffer.copy( this.lastBuffer ) ; }

	// Return some stats back to the caller
	return {
		cells: context.cells ,
		moves: context.moves ,
		attrs: context.attrs ,
		writes: context.writes
	} ;
} ;



ScreenBuffer.prototype.terminalBlitterLineIterator = function( p ) {
	var offset , attr ;

	if ( ! p.context.inline ) {
		p.context.sequence += p.context.term.optimized.moveTo( p.dstXmin , p.dstY ) ;
		p.context.moves ++ ;
	}

	for ( offset = p.srcStart ; offset < p.srcEnd ; offset += this.ITEM_SIZE ) {
		attr = this.readAttr( p.context.srcBuffer , offset ) ;

		if ( ( attr & TRANSPARENCY ) === TRANSPARENCY ) {
			// Fully transparent, do nothing except moving one char right
			p.context.sequence += p.context.term.optimized.right ;
			continue ;
		}
		else if ( attr & TRAILING_FULLWIDTH ) {
			// Trailing fullwidth cell, the previous char already shifted the cursor to the right
			continue ;
		}

		if ( ( attr & ATTR_MASK ) !== ( p.context.lastAttr & ATTR_MASK ) ) {
			p.context.sequence += p.context.lastAttr === null || ! p.context.deltaEscapeSequence ?
				this.generateEscapeSequence( p.context.term , attr ) :
				this.generateDeltaEscapeSequence( p.context.term , attr , p.context.lastAttr ) ;
			p.context.lastAttr = attr ;
		}

		p.context.sequence += this.readChar( p.context.srcBuffer , offset ) ;
		p.context.cells ++ ;
	}

	if ( p.context.inline ) { //&& ! p.lastLine ) {
		// When we are at the bottom of the screen, the terminal may create a new line
		// using the current attr for background color, just like .eraseLineAfter() would do...
		// So we have to reset *BEFORE* the new line.
		p.context.sequence += p.context.term.optimized.styleReset + '\n' ;
		p.context.attrs ++ ;
		p.context.lastAttr = null ;
	}

	// Output buffering saves a good amount of CPU usage both for the node's processus and the terminal processus
	if ( p.context.sequence.length > OUTPUT_THRESHOLD ) {
		p.context.rawTerm( p.context.sequence ) ;
		p.context.sequence = '' ;
		p.context.writes ++ ;
	}
} ;



ScreenBuffer.prototype.terminalBlitterCellIterator = function( p ) {
	var attr = this.readAttr( p.context.srcBuffer , p.srcStart ) ;

	// If last buffer's cell === current buffer's cell, no need to refresh... skip that now
	if ( p.context.srcLastBuffer ) {
		if (
			attr === this.readAttr( p.context.srcLastBuffer , p.srcStart ) &&
			this.readChar( p.context.srcBuffer , p.srcStart ) === this.readChar( p.context.srcLastBuffer , p.srcStart ) ) {
			return ;
		}

		p.context.srcBuffer.copy( p.context.srcLastBuffer , p.srcStart , p.srcStart , p.srcEnd ) ;
	}

	if ( ( attr & TRANSPARENCY ) === TRANSPARENCY || ( attr & TRAILING_FULLWIDTH ) ) {
		// Fully transparent or trailing fullwidth, do nothing
		// Check that after eventually updating lastBuffer
		return ;
	}

	p.context.cells ++ ;

	if ( p.dstX !== p.context.cx || p.dstY !== p.context.cy ) {
		p.context.sequence += p.context.term.optimized.moveTo( p.dstX , p.dstY ) ;
		p.context.moves ++ ;
	}

	if ( ( attr & ATTR_MASK ) !== ( p.context.lastAttr & ATTR_MASK ) ) {
		p.context.sequence += p.context.lastAttr === null || ! p.context.deltaEscapeSequence ?
			this.generateEscapeSequence( p.context.term , attr ) :
			this.generateDeltaEscapeSequence( p.context.term , attr , p.context.lastAttr ) ;
		p.context.lastAttr = attr ;
		p.context.attrs ++ ;
	}

	p.context.sequence += this.readChar( p.context.srcBuffer , p.srcStart ) ;

	// Output buffering saves a good amount of CPU usage both for the node's processus and the terminal processus
	if ( p.context.sequence.length > OUTPUT_THRESHOLD ) {
		p.context.rawTerm( p.context.sequence ) ;
		p.context.sequence = '' ;
		p.context.writes ++ ;
	}

	// Next expected cursor position
	p.context.cy = p.dstY ;

	if ( attr & LEADING_FULLWIDTH ) {
		p.context.cx = p.dstX + 2 ;
		return true ;	// i.e.: tell the master iterator that this is a full-width char
	}

	p.context.cx = p.dstX + 1 ;
} ;



ScreenBuffer.fromNdarrayImage = function( pixels , options ) {
	var term = options.terminal || termkit.terminal ;

	var x , xMax = pixels.shape[ 0 ] ,
		y , yMax = Math.ceil( pixels.shape[ 1 ] / 2 ) ,
		hasAlpha = pixels.shape[ 2 ] === 4 ,
		maxRegister = term.support['256colors'] ? 255 : 15 ,
		fgColor , bgColor , cache = {} ;

	var image = new ScreenBuffer( {
		width: xMax , height: yMax , blending: true , noFill: true
	} ) ;

	for ( x = 0 ; x < xMax ; x ++ ) {
		for ( y = 0 ; y < yMax ; y ++ ) {
			fgColor = term.registerForRgbCache(
				cache ,
				pixels.get( x , y * 2 , 0 ) ,
				pixels.get( x , y * 2 , 1 ) ,
				pixels.get( x , y * 2 , 2 ) ,
				0 , maxRegister , 1
			) ;

			if ( y * 2 + 1 < pixels.shape[ 1 ] ) {
				bgColor = term.registerForRgbCache(
					cache ,
					pixels.get( x , y * 2 + 1 , 0 ) ,
					pixels.get( x , y * 2 + 1 , 1 ) ,
					pixels.get( x , y * 2 + 1 , 2 ) ,
					0 , maxRegister , 1
				) ;

				image.put(
					{
						x: x ,
						y: y ,
						attr: {
							color: fgColor ,
							fgTransparency: hasAlpha && pixels.get( x , y * 2 , 3 ) < 127 ,
							bgColor: bgColor ,
							bgTransparency: hasAlpha && pixels.get( x , y * 2 + 1 , 3 ) < 127
						}
					} ,
					'▀'
				) ;
			}
			else {
				image.put(
					{
						x: x ,
						y: y ,
						attr: {
							color: fgColor ,
							fgTransparency: hasAlpha && pixels.get( x , y * 2 , 3 ) < 127 ,
							bgTransparency: true
						}
					} ,
					'▀'
				) ;
			}
		}
	}

	return image ;
} ;



ScreenBuffer.loadImage = termkit.image.load.bind( ScreenBuffer , ScreenBuffer.fromNdarrayImage ) ;



// Shared
ScreenBuffer.prototype.dumpChars = function() {
	var y , x , offset , str = '' ;

	for ( y = 0 ; y < this.height ; y ++ ) {
		for ( x = 0 ; x < this.width ; x ++ ) {
			offset = ( y * this.width + x ) * this.ITEM_SIZE ;
			str += this.readChar( this.buffer , offset ) ;
		}

		str += '\n' ;
	}

	return str ;
} ;



ScreenBuffer.prototype.dump = function() {
	var y , x , offset , str = '' , char ;

	for ( y = 0 ; y < this.height ; y ++ ) {
		for ( x = 0 ; x < this.width ; x ++ ) {
			offset = ( y * this.width + x ) * this.ITEM_SIZE ;

			char = this.readChar( this.buffer , offset ) ;
			str += char + ( string.unicode.isFullWidth( char ) ? ' ' : '  ' ) ;

			str += string.format( '%x%x%x%x ' ,
				this.buffer.readUInt8( offset ) ,
				this.buffer.readUInt8( offset + 1 ) ,
				this.buffer.readUInt8( offset + 2 ) ,
				this.buffer.readUInt8( offset + 3 )
			) ;
		}

		str += '\n' ;
	}

	return str ;
} ;



ScreenBuffer.prototype.readAttr = function( buffer , at ) {
	return buffer.readInt32BE( at ) ;
} ;



ScreenBuffer.prototype.writeAttr = function( buffer , attr , at , fullWidth = 0 ) {
	return buffer.writeInt32BE( attr | fullWidth , at ) ;
} ;



ScreenBuffer.prototype.hasLeadingFullWidth = function( buffer , at ) {
	return !! ( buffer.readInt32BE( at ) & LEADING_FULLWIDTH ) ;
} ;



ScreenBuffer.prototype.hasTrailingFullWidth = function( buffer , at ) {
	return !! ( buffer.readInt32BE( at ) & TRAILING_FULLWIDTH ) ;
} ;



ScreenBuffer.prototype.removeLeadingFullWidth = function( buffer , at ) {
	var attr = buffer.readInt32BE( at ) ;
	if ( ! ( attr & LEADING_FULLWIDTH ) ) { return ; }
	attr ^= LEADING_FULLWIDTH ;
	buffer.writeInt32BE( attr , at ) ;
	buffer.write( ' ' , at + this.ATTR_SIZE , this.CHAR_SIZE ) ;
} ;



ScreenBuffer.prototype.removeTrailingFullWidth = function( buffer , at ) {
	var attr = buffer.readInt32BE( at ) ;
	if ( ! ( attr & TRAILING_FULLWIDTH ) ) { return ; }
	attr ^= TRAILING_FULLWIDTH ;
	buffer.writeInt32BE( attr , at ) ;
	buffer.write( ' ' , at + this.ATTR_SIZE , this.CHAR_SIZE ) ;
} ;



ScreenBuffer.prototype.removeFullWidth = function( buffer , at ) {
	var attr = buffer.readInt32BE( at ) ;
	if ( ! ( attr & FULLWIDTH ) ) { return ; }
	attr = attr & REMOVE_FULLWIDTH_FLAG ;
	buffer.writeInt32BE( attr , at ) ;
	buffer.write( ' ' , at + this.ATTR_SIZE , this.CHAR_SIZE ) ;
} ;



ScreenBuffer.prototype.readChar = function( buffer , at ) {
	var bytes ;

	at += this.ATTR_SIZE ;

	if ( buffer[ at ] < 0x80 ) { bytes = 1 ; }
	else if ( buffer[ at ] < 0xc0 ) { return '\x00' ; } // We are in a middle of an unicode multibyte sequence... something was wrong...
	else if ( buffer[ at ] < 0xe0 ) { bytes = 2 ; }
	else if ( buffer[ at ] < 0xf0 ) { bytes = 3 ; }
	else if ( buffer[ at ] < 0xf8 ) { bytes = 4 ; }
	else if ( buffer[ at ] < 0xfc ) { bytes = 5 ; }
	else { bytes = 6 ; }

	if ( bytes > this.CHAR_SIZE ) { return '\x00' ; }

	return buffer.toString( 'utf8' , at , at + bytes ) ;
} ;



ScreenBuffer.prototype.writeChar = function( buffer , char , at ) {
	return buffer.write( char , at + this.ATTR_SIZE , this.CHAR_SIZE ) ;
} ;



ScreenBuffer.prototype.generateEscapeSequence = function( term , attr ) {
	var palette = this.palette || term.palette ;

	var esc = term.optimized.styleReset +
		( attr & FG_DEFAULT_COLOR ? term.optimized.defaultColor : palette.escape[ attr & 255 ] ) +
		( attr & BG_DEFAULT_COLOR ? term.optimized.bgDefaultColor : palette.bgEscape[ ( attr >>> 8 ) & 255 ] ) ;

	// Style part
	if ( attr & BOLD ) { esc += term.optimized.bold ; }
	if ( attr & DIM ) { esc += term.optimized.dim ; }
	if ( attr & ITALIC ) { esc += term.optimized.italic ; }
	if ( attr & UNDERLINE ) { esc += term.optimized.underline ; }
	if ( attr & BLINK ) { esc += term.optimized.blink ; }
	if ( attr & INVERSE ) { esc += term.optimized.inverse ; }
	if ( attr & HIDDEN ) { esc += term.optimized.hidden ; }
	if ( attr & STRIKE ) { esc += term.optimized.strike ; }

	return esc ;
} ;



// Generate only the delta between the last and new attributes, may speed up things for the terminal process
// as well as consume less bandwidth, at the cost of small CPU increase in the application process
ScreenBuffer.prototype.generateDeltaEscapeSequence = function( term , attr , lastAttr ) {
	var palette = this.palette || term.palette ;

	var esc = '' ,
		color = attr & 255 ,
		lastColor = lastAttr & 255 ,
		bgColor = ( attr >>> 8 ) & 255 ,
		lastBgColor = ( lastAttr >>> 8 ) & 255 ;

	if ( attr & FG_DEFAULT_COLOR ) {
		if ( ! ( lastAttr & FG_DEFAULT_COLOR ) ) { esc += term.optimized.defaultColor ; }
	}
	else if ( color !== lastColor || ( lastAttr & FG_DEFAULT_COLOR ) ) {
		esc += palette.escape[ color ] ;
	}

	if ( attr & BG_DEFAULT_COLOR ) {
		if ( ! ( lastAttr & BG_DEFAULT_COLOR ) ) { esc += term.optimized.bgDefaultColor ; }
	}
	else if ( bgColor !== lastBgColor || ( lastAttr & BG_DEFAULT_COLOR ) ) {
		esc += palette.bgEscape[ bgColor ] ;
	}

	if ( ( attr & STYLE_MASK ) !== ( lastAttr & STYLE_MASK ) ) {
		// Bold and dim style are particular: all terminal has noBold = noDim

		if ( ( attr & BOLD_DIM ) !== ( lastAttr & BOLD_DIM ) ) {
			if ( ( ( lastAttr & BOLD ) && ! ( attr & BOLD ) ) ||
				( ( lastAttr & DIM ) && ! ( attr & DIM ) ) ) {
				esc += term.optimized.noBold ;
				if ( attr & BOLD ) { esc += term.optimized.bold ; }
				if ( attr & DIM ) { esc += term.optimized.dim ; }
			}
			else {
				if ( ( attr & BOLD ) && ! ( lastAttr & BOLD ) ) { esc += term.optimized.bold ; }
				if ( ( attr & DIM ) && ! ( lastAttr & DIM ) ) { esc += term.optimized.dim ; }
			}
		}

		if ( ( attr & ITALIC ) !== ( lastAttr & ITALIC ) ) {
			esc += attr & ITALIC ? term.optimized.italic : term.optimized.noItalic ;
		}

		if ( ( attr & UNDERLINE ) !== ( lastAttr & UNDERLINE ) ) {
			esc += attr & UNDERLINE ? term.optimized.underline : term.optimized.noUnderline ;
		}

		if ( ( attr & BLINK ) !== ( lastAttr & BLINK ) ) {
			esc += attr & BLINK ? term.optimized.blink : term.optimized.noBlink ;
		}

		if ( ( attr & INVERSE ) !== ( lastAttr & INVERSE ) ) {
			esc += attr & INVERSE ? term.optimized.inverse : term.optimized.noInverse ;
		}

		if ( ( attr & HIDDEN ) !== ( lastAttr & HIDDEN ) ) {
			esc += attr & HIDDEN ? term.optimized.hidden : term.optimized.noHidden ;
		}

		if ( ( attr & STRIKE ) !== ( lastAttr & STRIKE ) ) {
			esc += attr & STRIKE ? term.optimized.strike : term.optimized.noStrike ;
		}
	}

	return esc ;
} ;



/*
	* lineOffset: how many lines should we scroll:
		>0: scroll down
		<0: scroll up
	* terminalScrolling: if true, the underlying terminal is scrolled, the lastBuffer is scrolled too,
		but the screenbuffer is not drawn
*/
// Shared
ScreenBuffer.prototype.vScroll = function( lineOffset , attr , ymin , ymax , terminalScrolling ) {
	if ( ! lineOffset ) { return ; }

	// Arguments management
	// Backward compatibility, when terminalScrolling was the 2nd argument
	if ( typeof attr === 'boolean' ) {
		terminalScrolling = attr ;
		attr = ymin = ymax = undefined ;
	}

	if ( attr === undefined || attr === null ) {
		attr = this.DEFAULT_ATTR ;
	}
	else if ( attr && typeof attr === 'object' && ! attr.BYTES_PER_ELEMENT ) {
		attr = this.object2attr( attr ) ;
	}

	if ( ymin === undefined || ymin === null ) {
		ymin = 0 ;
		ymax = this.height - 1 ;
	}
	else {
		if ( ymin < 0 ) { ymin = 0 ; }
		if ( ymax > this.height - 1 ) { ymax = this.height - 1 ; }
	}

	var scrollOffset = lineOffset * this.width * this.ITEM_SIZE ,
		startOffset = ymin * this.width * this.ITEM_SIZE ,
		endOffset = ( ymax + 1 ) * this.width * this.ITEM_SIZE ;

	if ( scrollOffset > 0 ) {
		//this.buffer.copy( this.buffer , scrollOffset , 0 , this.buffer.length - scrollOffset ) ;
		//this.fill( { end: scrollOffset } ) ;
		this.buffer.copy( this.buffer , startOffset + scrollOffset , startOffset , endOffset - scrollOffset ) ;
		this.fill( { start: startOffset , end: startOffset + scrollOffset } ) ;
	}
	else {
		//this.buffer.copy( this.buffer , 0 , -scrollOffset , this.buffer.length ) ;
		//this.fill( { start: this.buffer.length + scrollOffset } ) ;
		this.buffer.copy( this.buffer , startOffset , startOffset - scrollOffset , endOffset ) ;
		this.fill( { start: endOffset + scrollOffset , end: endOffset } ) ;
	}

	if ( terminalScrolling && this.dst instanceof termkit.Terminal ) {
		// Scroll lastBuffer accordingly
		if ( this.lastBufferUpToDate && this.lastBuffer ) {
			if ( scrollOffset > 0 ) {
				//this.lastBuffer.copy( this.lastBuffer , scrollOffset , 0 , this.lastBuffer.length - scrollOffset ) ;
				//this.fill( { end: scrollOffset , buffer: this.lastBuffer } ) ;
				this.lastBuffer.copy( this.lastBuffer , startOffset + scrollOffset , startOffset , endOffset - scrollOffset ) ;
				this.fill( { buffer: this.lastBuffer , start: startOffset , end: startOffset + scrollOffset } ) ;
			}
			else {
				//this.lastBuffer.copy( this.lastBuffer , 0 , -scrollOffset , this.lastBuffer.length ) ;
				//this.fill( { start: this.buffer.length + scrollOffset , buffer: this.lastBuffer } ) ;
				this.lastBuffer.copy( this.lastBuffer , startOffset , startOffset - scrollOffset , endOffset ) ;
				this.fill( { buffer: this.lastBuffer , start: endOffset + scrollOffset , end: endOffset } ) ;
			}
		}

		//this.dst.scrollingRegion( this.y , this.y + this.height - 1 ) ;
		this.dst.scrollingRegion( this.y + ymin , this.y + ymax ) ;

		if ( lineOffset > 0 ) { this.dst.scrollDown( lineOffset ) ; }
		else { this.dst.scrollUp( -lineOffset ) ; }

		this.dst.resetScrollingRegion() ;
	}
} ;



ScreenBuffer.prototype.copyRegion = function( from , to , isMove , attr ) {
	var bufferRect = new Rect( this ) ,
		fromRect = new Rect( from ) ,
		toRect = new Rect( to ) ;

	toRect.setSize( fromRect ) ;	// Ensure toRect has the size of fromRect
	fromRect.clip( bufferRect ) ;
	toRect.clip( bufferRect ) ;

	if ( fromRect.isNull ) { return ; }

	if ( ! toRect.isNull ) {
		// We use the blitter to copy the region
		Rect.regionIterator( {
			type: toRect.ymin - fromRect.ymin > 0 ? 'reversedLine' : 'line' ,
			context: { srcBuffer: this.buffer , dstBuffer: this.buffer } ,
			srcRect: new Rect( this ) ,
			srcClipRect: fromRect ,
			dstRect: new Rect( this ) ,
			dstClipRect: toRect ,
			offsetX: toRect.xmin - fromRect.xmin ,
			offsetY: toRect.ymin - fromRect.ymin ,
			multiply: this.ITEM_SIZE
		} , this.blitterLineIterator.bind( this ) ) ;
	}

	if ( isMove ) {
		throw new Error( "Move is not coded ATM" ) ;
	}
} ;





/*
	Methods that are both static and instance member.
	It must be possible to call them without any instance AND invoke instance specific method.
*/



ScreenBuffer.attr2object = function( attr ) {
	var object = {} ;

	// Default color
	if ( attr & FG_DEFAULT_COLOR ) { object.color = 0 ; object.defaultColor = true ; }
	else { object.color = attr & 255 ; }

	if ( attr & BG_DEFAULT_COLOR ) { object.bgColor = 0 ; object.bgDefaultColor = true ; }
	else { object.bgColor = ( attr >>> 8 ) & 255 ; }

	// Style part
	if ( attr & BOLD ) { object.bold = true ; }
	if ( attr & DIM ) { object.dim = true ; }
	if ( attr & ITALIC ) { object.italic = true ; }
	if ( attr & UNDERLINE ) { object.underline = true ; }
	if ( attr & BLINK ) { object.blink = true ; }
	if ( attr & INVERSE ) { object.inverse = true ; }
	if ( attr & HIDDEN ) { object.hidden = true ; }
	if ( attr & STRIKE ) { object.strike = true ; }

	// Blending part
	if ( attr & FG_TRANSPARENCY ) { object.fgTransparency = true ; }
	if ( attr & BG_TRANSPARENCY ) { object.bgTransparency = true ; }
	if ( attr & STYLE_TRANSPARENCY ) { object.styleTransparency = true ; }
	if ( attr & CHAR_TRANSPARENCY ) { object.charTransparency = true ; }
	if ( ( attr & TRANSPARENCY ) === TRANSPARENCY ) { object.transparency = true ; }

	return object ;
} ;

ScreenBuffer.prototype.attr2object = ScreenBuffer.attr2object ;



ScreenBuffer.object2attr = function( object , colorNameToIndex , legacyColor = false ) {
	var attr = 0 , brightFg = false , brightBg = false ;

	if ( ! object || typeof object !== 'object' ) { object = {} ; }
	colorNameToIndex = colorNameToIndex || termkit.colorNameToIndex ;

	// Style part
	if ( object.bold ) {
		if ( legacyColor ) { brightFg = true ; }
		else { attr |= BOLD ; }
	}
	if ( object.dim ) { attr |= DIM ; }
	if ( object.italic ) { attr |= ITALIC ; }
	if ( object.underline ) { attr |= UNDERLINE ; }
	if ( object.blink ) {
		if ( legacyColor ) { brightBg = true ; }
		else { attr |= BLINK ; }
	}
	if ( object.inverse ) { attr |= INVERSE ; }
	if ( object.hidden ) { attr |= HIDDEN ; }
	if ( object.strike ) { attr |= STRIKE ; }

	// Color part
	if ( typeof object.color === 'string' ) {
		if ( object.color === 'default' ) { object.color = 0 ; object.defaultColor = true ; }
		else { object.color = colorNameToIndex( object.color ) ; }
	}

	if ( typeof object.color !== 'number' || object.color < 0 || object.color > 255 ) {
		object.color = 0 ;
		object.defaultColor = true ;
	}
	else {
		object.color = Math.floor( object.color ) ;
		if ( legacyColor && object.color <= 15 ) {
			if ( brightFg && object.color <= 7 ) { object.color += 8 ; }
			else if ( ! brightFg && object.color >= 8 ) { object.color -= 8 ; }
		}
	}

	attr += object.color ;

	// Background color part
	if ( typeof object.bgColor === 'string' ) {
		if ( object.bgColor === 'default' ) { object.bgColor = 0 ; object.bgDefaultColor = true ; }
		else { object.bgColor = colorNameToIndex( object.bgColor ) ; }
	}

	if ( typeof object.bgColor !== 'number' || object.bgColor < 0 || object.bgColor > 255 ) {
		object.bgColor = 0 ;
		object.bgDefaultColor = true ;
	}
	else {
		object.bgColor = Math.floor( object.bgColor ) ;
		if ( legacyColor && object.bgColor <= 15 ) {
			if ( brightBg && object.bgColor <= 7 ) { object.bgColor += 8 ; }
			else if ( ! brightBg && object.bgColor >= 8 ) { object.bgColor -= 8 ; }
		}
	}

	attr += object.bgColor << 8 ;

	// Default color
	if ( object.defaultColor ) { attr |= FG_DEFAULT_COLOR ; }
	if ( object.bgDefaultColor ) { attr |= BG_DEFAULT_COLOR ; }

	// Blending part
	if ( object.transparency ) { attr |= TRANSPARENCY ; }
	if ( object.fgTransparency ) { attr |= FG_TRANSPARENCY ; }
	if ( object.bgTransparency ) { attr |= BG_TRANSPARENCY ; }
	if ( object.styleTransparency ) { attr |= STYLE_TRANSPARENCY ; }
	if ( object.charTransparency ) { attr |= CHAR_TRANSPARENCY ; }

	return attr ;
} ;

ScreenBuffer.prototype.object2attr = function( object , colorNameToIndex = this.palette && this.palette.colorNameToIndex , legacyColor = false ) {
	return ScreenBuffer.object2attr( object , colorNameToIndex , legacyColor ) ;
} ;



// Add some attributes from an object to an existing attr integer
ScreenBuffer.attrAndObject = function( attr , object , colorNameToIndex ) {
	if ( ! object || typeof object !== 'object' ) { return attr ; }
	colorNameToIndex = colorNameToIndex || termkit.colorNameToIndex ;

	// Color part
	if ( object.defaultColor || object.color === 'default' ) {
		attr -= attr & 255 ;
		attr |= FG_DEFAULT_COLOR ;
	}
	else if ( typeof object.color === 'string' ) {
		attr = attr - ( attr & 255 ) + colorNameToIndex( object.color ) ;
		if ( attr & FG_DEFAULT_COLOR ) { attr ^= FG_DEFAULT_COLOR ; }
	}
	else if ( typeof object.color === 'number' && object.color >= 0 && object.color <= 255 ) {
		attr = attr - ( attr & 255 ) + object.color ;
		if ( attr & FG_DEFAULT_COLOR ) { attr ^= FG_DEFAULT_COLOR ; }
	}

	// Background color part
	if ( object.bgDefaultColor || object.bgColor === 'default' ) {
		attr -= ( ( ( attr >>> 8 ) & 255 ) << 8 ) ;
		attr |= BG_DEFAULT_COLOR ;
	}
	else if ( typeof object.bgColor === 'string' ) {
		attr = attr - ( ( ( attr >>> 8 ) & 255 ) << 8 ) + ( colorNameToIndex( object.bgColor ) << 8 ) ;
		if ( attr & BG_DEFAULT_COLOR ) { attr ^= BG_DEFAULT_COLOR ; }
	}
	else if ( typeof object.bgColor === 'number' && object.bgColor >= 0 && object.bgColor <= 255 ) {
		attr = attr - ( ( ( attr >>> 8 ) & 255 ) << 8 ) + ( object.bgColor << 8 ) ;
		if ( attr & BG_DEFAULT_COLOR ) { attr ^= BG_DEFAULT_COLOR ; }
	}

	// Style part
	if ( object.bold === true ) { attr |= BOLD ; }
	else if ( object.bold === false ) { attr &= ~ BOLD ; }

	if ( object.dim === true ) { attr |= DIM ; }
	else if ( object.dim === false ) { attr &= ~ DIM ; }

	if ( object.italic === true ) { attr |= ITALIC ; }
	else if ( object.italic === false ) { attr &= ~ ITALIC ; }

	if ( object.underline === true ) { attr |= UNDERLINE ; }
	else if ( object.underline === false ) { attr &= ~ UNDERLINE ; }

	if ( object.blink === true ) { attr |= BLINK ; }
	else if ( object.blink === false ) { attr &= ~ BLINK ; }

	if ( object.inverse === true ) { attr |= INVERSE ; }
	else if ( object.inverse === false ) { attr &= ~ INVERSE ; }

	if ( object.hidden === true ) { attr |= HIDDEN ; }
	else if ( object.hidden === false ) { attr &= ~ HIDDEN ; }

	if ( object.strike === true ) { attr |= STRIKE ; }
	else if ( object.strike === false ) { attr &= ~ STRIKE ; }

	// Blending part
	if ( object.transparency === true ) { attr |= TRANSPARENCY ; }
	else if ( object.transparency === false ) { attr &= ~ TRANSPARENCY ; }

	if ( object.fgTransparency === true ) { attr |= FG_TRANSPARENCY ; }
	else if ( object.fgTransparency === false ) { attr &= ~ FG_TRANSPARENCY ; }

	if ( object.bgTransparency === true ) { attr |= BG_TRANSPARENCY ; }
	else if ( object.bgTransparency === false ) { attr &= ~ BG_TRANSPARENCY ; }

	if ( object.styleTransparency === true ) { attr |= STYLE_TRANSPARENCY ; }
	else if ( object.styleTransparency === false ) { attr &= ~ STYLE_TRANSPARENCY ; }

	if ( object.charTransparency === true ) { attr |= CHAR_TRANSPARENCY ; }
	else if ( object.charTransparency === false ) { attr &= ~ CHAR_TRANSPARENCY ; }

	return attr ;
} ;

ScreenBuffer.prototype.attrAndObject = function( attr , object ) {
	return ScreenBuffer.attrAndObject( attr , object , this.palette && this.palette.colorNameToIndex ) ;
} ;

// Used by TextBuffer for selection
ScreenBuffer.attrInverse = ScreenBuffer.prototype.attrInverse = attr => attr ^ INVERSE ;





/* Constants */



// General purpose flags
const NONE = 0 ;	// Nothing

// Style mask and flags
const STYLE_MASK = 255 << 16 ;

const BOLD = 1 << 16 ;
const DIM = 2 << 16 ;
const ITALIC = 4 << 16 ;
const UNDERLINE = 8 << 16 ;
const BLINK = 16 << 16 ;
const INVERSE = 32 << 16 ;
const HIDDEN = 64 << 16 ;
const STRIKE = 128 << 16 ;

const BOLD_DIM = BOLD | DIM ;

// Blending flags, mask and misc flags
const FG_TRANSPARENCY = 1 << 24 ;
const BG_TRANSPARENCY = 2 << 24 ;
const STYLE_TRANSPARENCY = 4 << 24 ;
const CHAR_TRANSPARENCY = 8 << 24 ;
const TRANSPARENCY = FG_TRANSPARENCY | BG_TRANSPARENCY | STYLE_TRANSPARENCY | CHAR_TRANSPARENCY ;

// Special color: default terminal color
const FG_DEFAULT_COLOR = 16 << 24 ;
const BG_DEFAULT_COLOR = 32 << 24 ;

// Attribute mask: anything except fullwidth flags
const ATTR_MASK = 255 + ( 255 << 8 ) + ( 255 << 16 ) + TRANSPARENCY + FG_DEFAULT_COLOR + BG_DEFAULT_COLOR ;

// E.g.: if it needs redraw
// Was never implemented, could be replaced by a full-transparency check
//const VOID = 32 << 24 ;

const LEADING_FULLWIDTH = 64 << 24 ;
const TRAILING_FULLWIDTH = 128 << 24 ;
const FULLWIDTH = LEADING_FULLWIDTH | TRAILING_FULLWIDTH ;
const REMOVE_FULLWIDTH_FLAG = ~ FULLWIDTH ;

// Unused bits: none



// Tuning
const OUTPUT_THRESHOLD = 10000 ;	// minimum amount of data to retain before sending them to the terminal

// DEPRECATED version 1 of ScreenBuffer file format
const HEADER_SIZE = 40 ;	// Header consists of 40 bytes



// Data structure
ScreenBuffer.prototype.ATTR_SIZE = 4 ;	// do not edit, everything use Buffer.writeInt32BE()
ScreenBuffer.prototype.CHAR_SIZE = 4 ;
ScreenBuffer.prototype.ITEM_SIZE = ScreenBuffer.prototype.ATTR_SIZE + ScreenBuffer.prototype.CHAR_SIZE ;

ScreenBuffer.DEFAULT_ATTR =		// <- used by TextBuffer
ScreenBuffer.prototype.DEFAULT_ATTR = ScreenBuffer.object2attr( { defaultColor: true , bgDefaultColor: true } ) ;

ScreenBuffer.prototype.CLEAR_ATTR = ScreenBuffer.object2attr( { defaultColor: true , bgDefaultColor: true , transparency: true } ) ;
ScreenBuffer.prototype.CLEAR_BUFFER = Buffer.allocUnsafe( ScreenBuffer.prototype.ITEM_SIZE ) ;
ScreenBuffer.prototype.CLEAR_BUFFER.writeInt32BE( ScreenBuffer.prototype.CLEAR_ATTR , 0 ) ;
ScreenBuffer.prototype.CLEAR_BUFFER.write( ' \x00\x00\x00' , ScreenBuffer.prototype.ATTR_SIZE ) ;	// space

ScreenBuffer.prototype.LEADING_FULLWIDTH = LEADING_FULLWIDTH ;
ScreenBuffer.prototype.TRAILING_FULLWIDTH = TRAILING_FULLWIDTH ;



// Loader/Saver, mostly obsolete

ScreenBuffer.loadSyncV1 = function( filepath ) {
	var content , width , height , size , screenBuffer ;

	// Let it crash if nothing found
	content = fs.readFileSync( filepath ) ;

	// No header found?
	if ( content.length < HEADER_SIZE ) { throw new Error( 'No header found: this is not a ScreenBuffer file' ) ; }

	// See if we have got a 'SB' at the begining of the file
	if ( content[ 0 ] !== 83 || content[ 1 ] !== 66 ) { throw new Error( 'Magic number mismatch: this is not a ScreenBuffer file' ) ; }

	// Get the geometry
	width = content.readUInt16BE( 4 ) ;
	height = content.readUInt16BE( 6 ) ;

	// Guess the file size
	size = HEADER_SIZE + width * height * ScreenBuffer.prototype.ITEM_SIZE ;

	// Bad size?
	if ( content.length !== size ) { throw new Error( 'Bad file size: this is not a ScreenBuffer file' ) ; }

	// So the file exists, create a canvas based upon it
	screenBuffer = new ScreenBuffer( {
		width: width ,
		height: height
	} ) ;

	content.copy( screenBuffer.buffer , 0 , HEADER_SIZE ) ;

	return screenBuffer ;
} ;



ScreenBuffer.prototype.saveSyncV1 = function( filepath ) {
	var content ;

	content = Buffer.allocUnsafe( HEADER_SIZE + this.buffer.length ) ;

	// Clear the header area
	content.fill( 0 , 0 , HEADER_SIZE ) ;

	// Write the 'SB' magic number
	content[ 0 ] = 83 ;
	content[ 1 ] = 66 ;

	// Set the geometry
	content.writeUInt16BE( this.width , 4 ) ;
	content.writeUInt16BE( this.height , 6 ) ;

	this.buffer.copy( content , HEADER_SIZE ) ;

	// Let it crash if something bad happens
	fs.writeFileSync( filepath , content ) ;
} ;



ScreenBuffer.loadSyncV2 = function( filepath ) {
	var i , content , header , screenBuffer ;

	// Let it crash if nothing found
	content = fs.readFileSync( filepath ) ;

	// See if we have got a 'SB' at the begining of the file
	if ( content.length < 3 || content.toString( 'ascii' , 0 , 3 ) !== 'SB\n' ) {
		throw new Error( 'Magic number mismatch: this is not a ScreenBuffer file' ) ;
	}

	// search for the second \n
	for ( i = 3 ; i < content.length ; i ++ ) {
		if ( content[ i ] === 0x0a ) { break ; }
	}

	if ( i === content.length ) {
		throw new Error( 'No header found: this is not a ScreenBuffer file' ) ;
	}

	// Try to parse a JSON header
	try {
		header = JSON.parse( content.toString( 'utf8' , 3 , i ) ) ;
	}
	catch( error ) {
		throw new Error( 'No correct one-lined JSON header found: this is not a ScreenBuffer file' ) ;
	}

	// Mandatory header field
	if ( header.version === undefined || header.width === undefined || header.height === undefined ) {
		throw new Error( 'Missing mandatory header data, this is a corrupted or obsolete ScreenBuffer file' ) ;
	}

	// Check bitsPerColor
	if ( header.bitsPerColor && header.bitsPerColor !== ScreenBuffer.prototype.bitsPerColor ) {
		throw new Error( 'Bad Bits Per Color: ' + header.bitsPerColor + ' (should be ' + ScreenBuffer.prototype.bitsPerColor + ')' ) ;
	}

	// Bad size?
	if ( content.length !== i + 1 + header.width * header.height * ScreenBuffer.prototype.ITEM_SIZE ) {
		throw new Error( 'Bad file size: this is a corrupted ScreenBuffer file' ) ;
	}

	// So the file exists, create a canvas based upon it
	screenBuffer = new ScreenBuffer( {
		width: header.width ,
		height: header.height
	} ) ;

	content.copy( screenBuffer.buffer , 0 , i + 1 ) ;

	return screenBuffer ;
} ;



// This new format use JSON header for a maximal flexibility rather than a fixed binary header.
// The header start with a magic number SB\n then a compact single-line JSON that end with an \n.
// So the data part start after the second \n, providing a variable header size.
// This will allow adding meta data without actually changing the file format.
ScreenBuffer.prototype.saveSyncV2 = function( filepath ) {
	var content , header ;

	header = {
		version: 2 ,
		width: this.width ,
		height: this.height ,
		bpp: this.bpp
	} ;

	header = 'SB\n' + JSON.stringify( header ) + '\n' ;

	content = Buffer.allocUnsafe( header.length + this.buffer.length ) ;
	content.write( header ) ;

	this.buffer.copy( content , header.length ) ;

	// Let it crash if something bad happens
	fs.writeFileSync( filepath , content ) ;
} ;



ScreenBuffer.loadSync = ScreenBuffer.loadSyncV2 ;
ScreenBuffer.prototype.saveSync = ScreenBuffer.prototype.saveSyncV2 ;


}).call(this)}).call(this,require("buffer").Buffer)
},{"./misc.js":48,"./termkit.js":56,"buffer":159,"fs":148,"nextgen-events":78,"string-kit":133}],4:[function(require,module,exports){
(function (Buffer){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const ScreenBuffer = require( './ScreenBuffer.js' ) ;
const misc = require( './misc.js' ) ;

const fs = require( 'fs' ) ;
const string = require( 'string-kit' ) ;



/*
	options:
		* width: buffer width (default to dst.width)
		* height: buffer height (default to dst.height)
		* dst: writting destination
		* inline: for terminal dst only, draw inline instead of at some position (do not moveTo)
		* x: default position in the dst
		* y: default position in the dst
		* wrap: default wrapping behavior of .put()
		* noFill: do not call .fill() with default values at ScreenBuffer creation
		* blending: false/null or true or object (blending options): default blending params (can be overriden by .draw())
*/
function ScreenBufferHD( options = {} ) {
	ScreenBuffer.call( this , options ) ;
}

module.exports = ScreenBufferHD ;



const termkit = require( './termkit.js' ) ;



ScreenBufferHD.prototype = Object.create( ScreenBuffer.prototype ) ;
ScreenBufferHD.prototype.constructor = ScreenBufferHD ;
ScreenBufferHD.prototype.bitsPerColor = 24 ;

// Backward compatibility
ScreenBufferHD.create = ( ... args ) => new ScreenBufferHD( ... args ) ;



/*
	options:
		* attr: attributes passed to .put()
		* transparencyChar: a char that is transparent
		* transparencyType: bit flags for the transparency char
*/
ScreenBufferHD.createFromString = function( options , data ) {
	var x , y , length , attr , attrTrans , width , height , lineWidth , screenBuffer ;

	// Manage options
	if ( ! options ) { options = {} ; }

	if ( typeof data !== 'string' ) {
		if ( ! data.toString ) { throw new Error( '[terminal] ScreenBufferHD.createFromDataString(): argument #1 should be a string or provide a .toString() method.' ) ; }
		data = data.toString() ;
	}

	// Transform the data into an array of lines
	data = termkit.stripControlChars( data , true ).split( '\n' ) ;

	// Compute the buffer size
	width = 0 ;
	height = data.length ;

	attr = options.attr !== undefined ? options.attr : ScreenBufferHD.prototype.DEFAULT_ATTR ;
	if ( attr && typeof attr === 'object' && ! attr.BYTES_PER_ELEMENT ) { attr = ScreenBufferHD.object2attr( attr ) ; }

	attrTrans = attr ;

	if ( options.transparencyChar ) {
		if ( ! options.transparencyType ) { attrTrans |= ScreenBufferHD.prototype.TRANSPARENCY ; }
		else { attrTrans |= options.transparencyType & ScreenBufferHD.prototype.TRANSPARENCY ; }
	}

	// Compute the width of the screenBuffer
	for ( y = 0 ; y < data.length ; y ++ ) {
		lineWidth = string.unicode.width( data[ y ] ) ;
		if ( lineWidth > width ) { width = lineWidth ; }
	}

	// Create the buffer with the right width & height
	screenBuffer = new ScreenBufferHD( { width: width , height: height } ) ;

	// Fill the buffer with data
	for ( y = 0 ; y < data.length ; y ++ ) {
		if ( ! options.transparencyChar ) {
			screenBuffer.put( { x: 0 , y: y , attr: attr } , data[ y ] ) ;
		}
		else {
			length = data[ y ].length ;

			for ( x = 0 ; x < length ; x ++ ) {
				if ( data[ y ][ x ] === options.transparencyChar ) {
					screenBuffer.put( { x: x , y: y , attr: attrTrans } , data[ y ][ x ] ) ;
				}
				else {
					screenBuffer.put( { x: x , y: y , attr: attr } , data[ y ][ x ] ) ;
				}
			}
		}
	}

	return screenBuffer ;
} ;



// Backward compatibility
ScreenBufferHD.createFromChars = ScreenBufferHD.createFromString ;



var colorScheme = require( './colorScheme/gnome.json' ).map( o => ( { color: { r: o.r , g: o.g , b: o.b } } ) ) ;
var bgColorScheme = colorScheme.map( o => ( { bgColor: { r: o.r , g: o.g , b: o.b } } ) ) ;

ScreenBufferHD.prototype.markupToAttrObject = {
	normal: {
		'-': { dim: true } ,
		'+': { bold: true } ,
		'_': { underline: true } ,
		'/': { italic: true } ,
		'!': { inverse: true } ,

		'k': colorScheme[ 0 ] ,
		'r': colorScheme[ 1 ] ,
		'g': colorScheme[ 2 ] ,
		'y': colorScheme[ 3 ] ,
		'b': colorScheme[ 4 ] ,
		'm': colorScheme[ 5 ] ,
		'c': colorScheme[ 6 ] ,
		'w': colorScheme[ 7 ] ,
		'K': colorScheme[ 8 ] ,
		'R': colorScheme[ 9 ] ,
		'G': colorScheme[ 10 ] ,
		'Y': colorScheme[ 11 ] ,
		'B': colorScheme[ 12 ] ,
		'M': colorScheme[ 13 ] ,
		'C': colorScheme[ 14 ] ,
		'W': colorScheme[ 15 ]
	} ,
	background: {
		'k': bgColorScheme[ 0 ] ,
		'r': bgColorScheme[ 1 ] ,
		'g': bgColorScheme[ 2 ] ,
		'y': bgColorScheme[ 3 ] ,
		'b': bgColorScheme[ 4 ] ,
		'm': bgColorScheme[ 5 ] ,
		'c': bgColorScheme[ 6 ] ,
		'w': bgColorScheme[ 7 ] ,
		'K': bgColorScheme[ 8 ] ,
		'R': bgColorScheme[ 9 ] ,
		'G': bgColorScheme[ 10 ] ,
		'Y': bgColorScheme[ 11 ] ,
		'B': bgColorScheme[ 12 ] ,
		'M': bgColorScheme[ 13 ] ,
		'C': bgColorScheme[ 14 ] ,
		'W': bgColorScheme[ 15 ]
	}
} ;



ScreenBufferHD.prototype.blitterCellBlendingIterator = function( p ) {
	var attr = this.readAttr( p.context.srcBuffer , p.srcStart ) ;

	var blendFn = ScreenBufferHD.blendFn.normal ;
	var opacity = 1 ;
	var blendSrcFgWithDstBg = false ;

	if ( typeof p.context.blending === 'object' ) {
		if ( p.context.blending.fn ) { blendFn = p.context.blending.fn ; }
		if ( p.context.blending.opacity !== undefined ) { opacity = p.context.blending.opacity ; }
		if ( p.context.blending.blendSrcFgWithDstBg ) { blendSrcFgWithDstBg = true ; }
	}

	if (
		( attr[ BPOS_MISC ] & STYLE_TRANSPARENCY ) &&
		( attr[ BPOS_MISC ] & CHAR_TRANSPARENCY ) &&
		( ! opacity || ( attr[ BPOS_A ] === 0 && attr[ BPOS_BG_A ] === 0 ) )
	) {
		// Fully transparent, do nothing
		return ;
	}

	// First, manage fullwidth chars
	if ( p.startOfBlitLine && p.dstStart >= this.ITEM_SIZE ) {
		// Remove overlapping dst fullwidth at the begining
		this.removeLeadingFullWidth( p.context.dstBuffer , p.dstStart - this.ITEM_SIZE ) ;
	}

	if ( p.endOfBlitLine && p.dstEnd < p.context.dstBuffer.length ) {
		// Remove overlapping dst fullwidth at the end
		this.removeTrailingFullWidth( p.context.dstBuffer , p.dstEnd ) ;
	}

	if (
		! ( attr[ BPOS_MISC ] & STYLE_TRANSPARENCY ) &&
		! ( attr[ BPOS_MISC ] & CHAR_TRANSPARENCY ) &&
		attr[ BPOS_A ] === 255 && attr[ BPOS_BG_A ] === 255 && opacity === 1 &&
		blendFn === ScreenBufferHD.blendFn.normal &&
		! ( p.endOfBlitLine || ! ( attr[ BPOS_MISC ] & LEADING_FULLWIDTH ) )
	) {
		// Fully opaque, copy it
		p.context.srcBuffer.copy( p.context.dstBuffer , p.dstStart , p.srcStart , p.srcEnd ) ;
		return ;
	}

	// Blending part...

	var alpha ;	// Normalized alpha

	if ( attr[ BPOS_A ] ) {
		alpha = opacity * attr[ BPOS_A ] / 255 ;

		if ( blendSrcFgWithDstBg ) {
			p.context.dstBuffer[ p.dstStart + BPOS_R ] = alphaBlend(
				p.context.srcBuffer[ p.srcStart + BPOS_R ] ,
				p.context.dstBuffer[ p.dstStart + BPOS_BG_R ] ,
				alpha ,
				blendFn
			) ;
			p.context.dstBuffer[ p.dstStart + BPOS_G ] = alphaBlend(
				p.context.srcBuffer[ p.srcStart + BPOS_G ] ,
				p.context.dstBuffer[ p.dstStart + BPOS_BG_G ] ,
				alpha ,
				blendFn
			) ;
			p.context.dstBuffer[ p.dstStart + BPOS_B ] = alphaBlend(
				p.context.srcBuffer[ p.srcStart + BPOS_B ] ,
				p.context.dstBuffer[ p.dstStart + BPOS_BG_B ] ,
				alpha ,
				blendFn
			) ;
			// Blending alpha is special
			p.context.dstBuffer[ p.dstStart + BPOS_A ] = alphaBlend(
				p.context.srcBuffer[ p.srcStart + BPOS_A ] ,
				p.context.dstBuffer[ p.dstStart + BPOS_BG_A ] ,
				opacity ,
				ScreenBufferHD.blendFn.screen
			) ;
		}
		else {
			p.context.dstBuffer[ p.dstStart + BPOS_R ] = alphaBlend(
				p.context.srcBuffer[ p.srcStart + BPOS_R ] ,
				p.context.dstBuffer[ p.dstStart + BPOS_R ] ,
				alpha ,
				blendFn
			) ;
			p.context.dstBuffer[ p.dstStart + BPOS_G ] = alphaBlend(
				p.context.srcBuffer[ p.srcStart + BPOS_G ] ,
				p.context.dstBuffer[ p.dstStart + BPOS_G ] ,
				alpha ,
				blendFn
			) ;
			p.context.dstBuffer[ p.dstStart + BPOS_B ] = alphaBlend(
				p.context.srcBuffer[ p.srcStart + BPOS_B ] ,
				p.context.dstBuffer[ p.dstStart + BPOS_B ] ,
				alpha ,
				blendFn
			) ;
			// Blending alpha is special
			p.context.dstBuffer[ p.dstStart + BPOS_A ] = alphaBlend(
				p.context.srcBuffer[ p.srcStart + BPOS_A ] ,
				p.context.dstBuffer[ p.dstStart + BPOS_A ] ,
				opacity ,
				ScreenBufferHD.blendFn.screen
			) ;
		}
	}

	if ( attr[ BPOS_BG_A ] ) {
		alpha = opacity * attr[ BPOS_BG_A ] / 255 ;

		p.context.dstBuffer[ p.dstStart + BPOS_BG_R ] = alphaBlend(
			p.context.srcBuffer[ p.srcStart + BPOS_BG_R ] ,
			p.context.dstBuffer[ p.dstStart + BPOS_BG_R ] ,
			alpha ,
			blendFn
		) ;
		p.context.dstBuffer[ p.dstStart + BPOS_BG_G ] = alphaBlend(
			p.context.srcBuffer[ p.srcStart + BPOS_BG_G ] ,
			p.context.dstBuffer[ p.dstStart + BPOS_BG_G ] ,
			alpha ,
			blendFn
		) ;
		p.context.dstBuffer[ p.dstStart + BPOS_BG_B ] = alphaBlend(
			p.context.srcBuffer[ p.srcStart + BPOS_BG_B ] ,
			p.context.dstBuffer[ p.dstStart + BPOS_BG_B ] ,
			alpha ,
			blendFn
		) ;
		// Blending alpha is special
		p.context.dstBuffer[ p.dstStart + BPOS_BG_A ] = alphaBlend(
			p.context.srcBuffer[ p.srcStart + BPOS_BG_A ] ,
			p.context.dstBuffer[ p.dstStart + BPOS_BG_A ] ,
			opacity ,
			ScreenBufferHD.blendFn.screen
		) ;
	}

	if ( ! ( attr[ BPOS_MISC ] & STYLE_TRANSPARENCY ) ) {
		p.context.dstBuffer[ p.dstStart + BPOS_STYLE ] =
			p.context.srcBuffer[ p.srcStart + BPOS_STYLE ] ;
	}

	if ( ! ( attr[ BPOS_MISC ] & CHAR_TRANSPARENCY ) ) {
		if ( p.endOfBlitLine && ( attr[ BPOS_MISC ] & LEADING_FULLWIDTH ) ) {
			// Leading fullwidth at the end of the blit line, output a space instead
			this.writeChar( p.context.dstBuffer , ' ' , p.dstStart ) ;
		}
		else {
			// Copy source character
			p.context.srcBuffer.copy(
				p.context.dstBuffer ,
				p.dstStart + this.ATTR_SIZE ,
				p.srcStart + this.ATTR_SIZE ,
				p.srcEnd
			) ;
		}
	}
} ;



function alphaBlend( src , dst , alpha , fn ) {
	return Math.round( fn( src , dst ) * alpha + dst * ( 1 - alpha ) ) ;
}



// https://en.wikipedia.org/wiki/Blend_modes
ScreenBufferHD.blendFn = {
	normal: src => src ,
	multiply: ( src , dst ) => 255 * ( ( src / 255 ) * ( dst / 255 ) ) ,
	screen: ( src , dst ) => 255 * ( 1 - ( 1 - src / 255 ) * ( 1 - dst / 255 ) ) ,
	overlay: ( src , dst ) => dst <= 127 ?
		255 * ( 2 * ( src / 255 ) * ( dst / 255 ) ) :
		255 * ( 1 - 2 * ( 1 - src / 255 ) * ( 1 - dst / 255 ) ) ,
	hardLight: ( src , dst ) => src <= 127 ?
		255 * ( 2 * ( src / 255 ) * ( dst / 255 ) ) :
		255 * ( 1 - 2 * ( 1 - src / 255 ) * ( 1 - dst / 255 ) ) ,
	softLight: ( src , dst ) => {
		src /= 255 ;
		dst /= 255 ;
		return 255 * (  ( 1 - 2 * src ) * dst * dst + 2 * src * dst  ) ;
	}
} ;



function attrEquals( attr1 , attr2 ) {
	if ( attr1.readUInt32BE( BPOS_FG ) !== attr2.readUInt32BE( BPOS_FG ) ) { return false ; }
	if ( attr1.readUInt32BE( BPOS_BG ) !== attr2.readUInt32BE( BPOS_BG ) ) { return false ; }
	if ( attr1[ BPOS_STYLE ] !== attr2[ BPOS_STYLE ] ) { return false ; }
	if ( ( attr1[ BPOS_MISC ] & MISC_ATTR_MASK ) !== ( attr2[ BPOS_MISC ] & MISC_ATTR_MASK ) ) { return false ; }

	return true ;
}



ScreenBufferHD.prototype.terminalBlitterLineIterator = function( p ) {
	var offset , attr ;

	if ( ! p.context.inline ) {
		p.context.sequence += p.context.term.optimized.moveTo( p.dstXmin , p.dstY ) ;
		p.context.moves ++ ;
	}

	for ( offset = p.srcStart ; offset < p.srcEnd ; offset += this.ITEM_SIZE ) {
		attr = this.readAttr( p.context.srcBuffer , offset ) ;

		if ( attr[ BPOS_MISC ] & TRAILING_FULLWIDTH ) {
			// Trailing fullwidth cell, the previous char already shifted the cursor to the right
			continue ;
		}

		if ( ! p.context.lastAttr || ! attrEquals( attr , p.context.lastAttr ) ) {
			p.context.sequence += ! p.context.lastAttr || ! p.context.deltaEscapeSequence ?
				this.generateEscapeSequence( p.context.term , attr ) :
				this.generateDeltaEscapeSequence( p.context.term , attr , p.context.lastAttr ) ;
			p.context.lastAttr = attr ;
			p.context.attrs ++ ;
		}

		p.context.sequence += this.readChar( p.context.srcBuffer , offset ) ;
		p.context.cells ++ ;
	}

	if ( p.context.inline ) {
		// When we are at the bottom of the screen, the terminal may create a new line
		// using the current attr for background color, just like .eraseLineAfter() would do...
		// So we have to reset *BEFORE* the new line.
		p.context.sequence += p.context.term.optimized.styleReset + '\n' ;
		p.context.attrs ++ ;
		p.context.lastAttr = null ;
	}

	// Output buffering saves a good amount of CPU usage both for the node's processus and the terminal processus
	if ( p.context.sequence.length > OUTPUT_THRESHOLD ) {
		p.context.rawTerm( p.context.sequence ) ;
		p.context.sequence = '' ;
		p.context.writes ++ ;
	}
} ;



ScreenBufferHD.prototype.terminalBlitterCellIterator = function( p ) {
	//var attr = p.context.srcBuffer.readUInt32BE( p.srcStart ) ;
	var attr = this.readAttr( p.context.srcBuffer , p.srcStart ) ;

	// If last buffer's cell === current buffer's cell, no need to refresh... skip that now
	if ( p.context.srcLastBuffer ) {
		if (
			attrEquals( attr , this.readAttr( p.context.srcLastBuffer , p.srcStart ) ) &&
			this.readChar( p.context.srcBuffer , p.srcStart ) === this.readChar( p.context.srcLastBuffer , p.srcStart ) ) {
			return ;
		}

		p.context.srcBuffer.copy( p.context.srcLastBuffer , p.srcStart , p.srcStart , p.srcEnd ) ;
	}

	if ( attr[ BPOS_MISC ] & TRAILING_FULLWIDTH ) {
		// Trailing fullwidth, do nothing
		// Check that after eventually updating lastBuffer
		return ;
	}

	p.context.cells ++ ;

	if ( p.dstX !== p.context.cx || p.dstY !== p.context.cy ) {
		p.context.sequence += p.context.term.optimized.moveTo( p.dstX , p.dstY ) ;
		p.context.moves ++ ;
	}

	if ( ! p.context.lastAttr || ! attrEquals( attr , p.context.lastAttr ) ) {
		p.context.sequence += ! p.context.lastAttr || ! p.context.deltaEscapeSequence ?
			this.generateEscapeSequence( p.context.term , attr ) :
			this.generateDeltaEscapeSequence( p.context.term , attr , p.context.lastAttr ) ;
		p.context.lastAttr = attr ;
		p.context.attrs ++ ;
	}

	p.context.sequence += this.readChar( p.context.srcBuffer , p.srcStart ) ;

	// Output buffering saves a good amount of CPU usage both for the node's processus and the terminal processus
	if ( p.context.sequence.length > OUTPUT_THRESHOLD ) {
		p.context.rawTerm( p.context.sequence ) ;
		p.context.sequence = '' ;
		p.context.writes ++ ;
	}

	// Next expected cursor position
	p.context.cy = p.dstY ;

	if ( attr & LEADING_FULLWIDTH ) {
		p.context.cx = p.dstX + 2 ;
		return true ;   // i.e.: tell the master iterator that this is a full-width char
	}

	p.context.cx = p.dstX + 1 ;
} ;



ScreenBufferHD.fromNdarrayImage = function( pixels /*, options */ ) {
	var x , xMax = pixels.shape[ 0 ] ,
		y , yMax = Math.ceil( pixels.shape[ 1 ] / 2 ) ,
		hasAlpha = pixels.shape[ 2 ] === 4 ;

	var image = new ScreenBufferHD( {
		width: xMax , height: yMax , blending: true , noFill: true
	} ) ;

	for ( x = 0 ; x < xMax ; x ++ ) {
		for ( y = 0 ; y < yMax ; y ++ ) {
			if ( y * 2 + 1 < pixels.shape[ 1 ] ) {
				image.put(
					{
						x: x ,
						y: y ,
						attr: {
							color: {
								r: pixels.get( x , y * 2 , 0 ) ,
								g: pixels.get( x , y * 2 , 1 ) ,
								b: pixels.get( x , y * 2 , 2 ) ,
								a: hasAlpha ? pixels.get( x , y * 2 , 3 ) : 255
							} ,
							bgColor: {
								r: pixels.get( x , y * 2 + 1 , 0 ) ,
								g: pixels.get( x , y * 2 + 1 , 1 ) ,
								b: pixels.get( x , y * 2 + 1 , 2 ) ,
								a: hasAlpha ? pixels.get( x , y * 2 + 1 , 3 ) : 255
							}
						}
					} ,
					'▀'
				) ;
			}
			else {
				image.put(
					{
						x: x ,
						y: y ,
						attr: {
							color: {
								r: pixels.get( x , y * 2 , 0 ) ,
								g: pixels.get( x , y * 2 , 1 ) ,
								b: pixels.get( x , y * 2 , 2 ) ,
								a: hasAlpha ? pixels.get( x , y * 2 , 3 ) : 255
							} ,
							bgColor: {
								r: 0 ,
								g: 0 ,
								b: 0 ,
								a: 0
							}
						}
					} ,
					'▀'
				) ;
			}
		}
	}

	return image ;
} ;



ScreenBufferHD.loadImage = termkit.image.load.bind( ScreenBufferHD , ScreenBufferHD.fromNdarrayImage ) ;



ScreenBufferHD.prototype.dump = function() {
	var y , x , offset , str = '' , char ;

	for ( y = 0 ; y < this.height ; y ++ ) {
		for ( x = 0 ; x < this.width ; x ++ ) {
			offset = ( y * this.width + x ) * this.ITEM_SIZE ;

			char = this.readChar( this.buffer , offset ) ;
			str += char + ( string.unicode.isFullWidth( char ) ? ' ' : '  ' ) ;

			str += string.format( '%x%x%x%x %x%x%x%x %x%x ' ,
				this.buffer.readUInt8( offset ) ,
				this.buffer.readUInt8( offset + 1 ) ,
				this.buffer.readUInt8( offset + 2 ) ,
				this.buffer.readUInt8( offset + 3 ) ,
				this.buffer.readUInt8( offset + 4 ) ,
				this.buffer.readUInt8( offset + 5 ) ,
				this.buffer.readUInt8( offset + 6 ) ,
				this.buffer.readUInt8( offset + 7 ) ,
				this.buffer.readUInt8( offset + 8 ) ,
				this.buffer.readUInt8( offset + 9 )
			) ;
		}

		str += '\n' ;
	}

	return str ;
} ;



ScreenBufferHD.prototype.readAttr = function( buffer , at ) {
	return buffer.slice( at , at + this.ATTR_SIZE ) ;
} ;



ScreenBufferHD.prototype.writeAttr = function( buffer , attr , at , fullWidth = 0 ) {
	if ( ! fullWidth ) { return attr.copy( buffer , at ) ; }
	attr.copy( buffer , at ) ;
	return buffer.writeUInt8( attr[ BPOS_MISC ] | fullWidth , at + BPOS_MISC ) ;
} ;



ScreenBufferHD.prototype.hasLeadingFullWidth = function( buffer , at ) {
	return !! ( buffer.readUInt8( at + BPOS_MISC ) & LEADING_FULLWIDTH ) ;
} ;



ScreenBufferHD.prototype.hasTrailingFullWidth = function( buffer , at ) {
	return !! ( buffer.readUInt8( at + BPOS_MISC ) & TRAILING_FULLWIDTH ) ;
} ;



ScreenBufferHD.prototype.removeLeadingFullWidth = function( buffer , at ) {
	var attr = buffer.readUInt8( at + BPOS_MISC ) ;
	if ( ! ( attr & LEADING_FULLWIDTH ) ) { return ; }
	attr ^= LEADING_FULLWIDTH ;
	buffer.writeUInt8( attr , at + BPOS_MISC ) ;
	buffer.write( ' ' , at + this.ATTR_SIZE , this.CHAR_SIZE ) ;
} ;



ScreenBufferHD.prototype.removeTrailingFullWidth = function( buffer , at ) {
	var attr = buffer.readUInt8( at + BPOS_MISC ) ;
	if ( ! ( attr & TRAILING_FULLWIDTH ) ) { return ; }
	attr ^= TRAILING_FULLWIDTH ;
	buffer.writeUInt8( attr , at + BPOS_MISC ) ;
	buffer.write( ' ' , at + this.ATTR_SIZE , this.CHAR_SIZE ) ;
} ;



ScreenBufferHD.prototype.removeFullWidth = function( buffer , at ) {
	var attr = buffer.readUInt8( at + BPOS_MISC ) ;
	if ( ! ( attr & FULLWIDTH ) ) { return ; }
	attr = attr & REMOVE_FULLWIDTH_FLAG ;
	buffer.writeUInt8( attr , at + BPOS_MISC ) ;
	buffer.write( ' ' , at + this.ATTR_SIZE , this.CHAR_SIZE ) ;
} ;



ScreenBufferHD.prototype.attrLeadingFullWidth = function( attr ) {
	attr.writeUInt8( attr[ BPOS_MISC ] | LEADING_FULLWIDTH , BPOS_MISC ) ;
	return attr ;
} ;



ScreenBufferHD.prototype.attrTrailingFullWidth = function( attr ) {
	attr.writeUInt8( attr[ BPOS_MISC ] | TRAILING_FULLWIDTH , BPOS_MISC ) ;
	return attr ;
} ;



ScreenBufferHD.prototype.readChar = function( buffer , at ) {
	var bytes ;

	at += this.ATTR_SIZE ;

	if ( buffer[ at ] < 0x80 ) { bytes = 1 ; }
	else if ( buffer[ at ] < 0xc0 ) { return '\x00' ; } // We are in a middle of an unicode multibyte sequence... something was wrong...
	else if ( buffer[ at ] < 0xe0 ) { bytes = 2 ; }
	else if ( buffer[ at ] < 0xf0 ) { bytes = 3 ; }
	else if ( buffer[ at ] < 0xf8 ) { bytes = 4 ; }
	else if ( buffer[ at ] < 0xfc ) { bytes = 5 ; }
	else { bytes = 6 ; }

	if ( bytes > this.CHAR_SIZE ) { return '\x00' ; }

	return buffer.toString( 'utf8' , at , at + bytes ) ;
} ;



ScreenBufferHD.prototype.writeChar = function( buffer , char , at ) {
	return buffer.write( char , at + this.ATTR_SIZE , this.CHAR_SIZE ) ;
} ;



ScreenBufferHD.prototype.generateEscapeSequence = function( term , attr ) {
	var esc = term.optimized.styleReset
		+ term.optimized.color24bits( attr[ BPOS_R ] , attr[ BPOS_G ] , attr[ BPOS_B ] )
		+ term.optimized.bgColor24bits( attr[ BPOS_BG_R ] , attr[ BPOS_BG_G ] , attr[ BPOS_BG_B ] ) ;

	var style = attr[ BPOS_STYLE ] ;

	// Style part
	if ( style & BOLD ) { esc += term.optimized.bold ; }
	if ( style & DIM ) { esc += term.optimized.dim ; }
	if ( style & ITALIC ) { esc += term.optimized.italic ; }
	if ( style & UNDERLINE ) { esc += term.optimized.underline ; }
	if ( style & BLINK ) { esc += term.optimized.blink ; }
	if ( style & INVERSE ) { esc += term.optimized.inverse ; }
	if ( style & HIDDEN ) { esc += term.optimized.hidden ; }
	if ( style & STRIKE ) { esc += term.optimized.strike ; }

	return esc ;
} ;



// Generate only the delta between the last and new attributes, may speed up things for the terminal process
// as well as consume less bandwidth, at the cost of small CPU increase in the application process
ScreenBufferHD.prototype.generateDeltaEscapeSequence = function( term , attr , lastAttr ) {
	var esc = '' ;

	// Color
	if (
		attr[ BPOS_R ] !== lastAttr[ BPOS_R ] ||
		attr[ BPOS_G ] !== lastAttr[ BPOS_G ] ||
		attr[ BPOS_B ] !== lastAttr[ BPOS_B ]
	) {
		esc += term.optimized.color24bits( attr[ BPOS_R ] , attr[ BPOS_G ] , attr[ BPOS_B ] ) ;
	}

	// Bg color
	if (
		attr[ BPOS_BG_R ] !== lastAttr[ BPOS_BG_R ] ||
		attr[ BPOS_BG_G ] !== lastAttr[ BPOS_BG_G ] ||
		attr[ BPOS_BG_B ] !== lastAttr[ BPOS_BG_B ]
	) {
		esc += term.optimized.bgColor24bits( attr[ BPOS_BG_R ] , attr[ BPOS_BG_G ] , attr[ BPOS_BG_B ] ) ;
	}


	var style = attr[ BPOS_STYLE ] ;
	var lastStyle = lastAttr[ BPOS_STYLE ] ;

	if ( style !== lastStyle ) {
		// Bold and dim style are particular: all terminal has noBold = noDim

		if ( ( style & BOLD_DIM ) !== ( lastStyle & BOLD_DIM ) ) {
			if ( ( ( lastStyle & BOLD ) && ! ( style & BOLD ) ) ||
				( ( lastStyle & DIM ) && ! ( style & DIM ) ) ) {
				esc += term.optimized.noBold ;
				if ( style & BOLD ) { esc += term.optimized.bold ; }
				if ( style & DIM ) { esc += term.optimized.dim ; }
			}
			else {
				if ( ( style & BOLD ) && ! ( lastStyle & BOLD ) ) { esc += term.optimized.bold ; }
				if ( ( style & DIM ) && ! ( lastStyle & DIM ) ) { esc += term.optimized.dim ; }
			}
		}

		if ( ( style & ITALIC ) !== ( lastStyle & ITALIC ) ) {
			esc += style & ITALIC ? term.optimized.italic : term.optimized.noItalic ;
		}

		if ( ( style & UNDERLINE ) !== ( lastStyle & UNDERLINE ) ) {
			esc += style & UNDERLINE ? term.optimized.underline : term.optimized.noUnderline ;
		}

		if ( ( style & BLINK ) !== ( lastStyle & BLINK ) ) {
			esc += style & BLINK ? term.optimized.blink : term.optimized.noBlink ;
		}

		if ( ( style & INVERSE ) !== ( lastStyle & INVERSE ) ) {
			esc += style & INVERSE ? term.optimized.inverse : term.optimized.noInverse ;
		}

		if ( ( style & HIDDEN ) !== ( lastStyle & HIDDEN ) ) {
			esc += style & HIDDEN ? term.optimized.hidden : term.optimized.noHidden ;
		}

		if ( ( style & STRIKE ) !== ( lastStyle & STRIKE ) ) {
			esc += style & STRIKE ? term.optimized.strike : term.optimized.noStrike ;
		}
	}

	return esc ;
} ;





/*
	Methods that are both static and instance member.
	It must be possible to call them without any instance AND invoke instance specific method.
*/



ScreenBufferHD.attr2object = function( attr ) {
	var object = { color: {} , bgColor: {} } ;

	// Color part
	object.color.r = attr[ BPOS_R ] ;
	object.color.g = attr[ BPOS_G ] ;
	object.color.b = attr[ BPOS_B ] ;
	object.color.a = attr[ BPOS_A ] ;

	// Background color part
	object.bgColor.r = attr[ BPOS_BG_R ] ;
	object.bgColor.g = attr[ BPOS_BG_G ] ;
	object.bgColor.b = attr[ BPOS_BG_B ] ;
	object.bgColor.a = attr[ BPOS_BG_A ] ;

	// Style part
	object.bold = !! ( attr[ BPOS_STYLE ] & BOLD ) ;
	object.dim = !! ( attr[ BPOS_STYLE ] & DIM ) ;
	object.italic = !! ( attr[ BPOS_STYLE ] & ITALIC ) ;
	object.underline = !! ( attr[ BPOS_STYLE ] & UNDERLINE ) ;
	object.blink = !! ( attr[ BPOS_STYLE ] & BLINK ) ;
	object.inverse = !! ( attr[ BPOS_STYLE ] & INVERSE ) ;
	object.hidden = !! ( attr[ BPOS_STYLE ] & HIDDEN ) ;
	object.strike = !! ( attr[ BPOS_STYLE ] & STRIKE ) ;

	// Misc part
	object.styleTransparency = !! ( attr[ BPOS_MISC ] & STYLE_TRANSPARENCY ) ;
	object.charTransparency = !! ( attr[ BPOS_MISC ] & CHAR_TRANSPARENCY ) ;

	return object ;
} ;

ScreenBufferHD.prototype.attr2object = ScreenBufferHD.attr2object ;



ScreenBufferHD.object2attr = function( object ) {
	var attr = Buffer.allocUnsafe( ScreenBufferHD.prototype.ATTR_SIZE ) ;

	if ( ! object || typeof object !== 'object' ) { object = {} ; }

	// Misc and color part
	attr[ BPOS_MISC ] = 0 ;

	// Color part
	if ( typeof object.color === 'string' ) {
		let color = misc.hexToRgba( object.color ) ;
		attr[ BPOS_R ] = color.r ;
		attr[ BPOS_G ] = color.g ;
		attr[ BPOS_B ] = color.b ;
		attr[ BPOS_A ] = color.a ;
	}
	else if ( object.color && typeof object.color === 'object' ) {
		attr[ BPOS_R ] = + object.color.r || 0 ;
		attr[ BPOS_G ] = + object.color.g || 0 ;
		attr[ BPOS_B ] = + object.color.b || 0 ;
		attr[ BPOS_A ] = object.color.a !== undefined ? + object.color.a || 0 : 255 ;
	}
	else {
		attr[ BPOS_R ] = 0 ;
		attr[ BPOS_G ] = 0 ;
		attr[ BPOS_B ] = 0 ;
		attr[ BPOS_A ] = 255 ;
	}

	// Background color part
	if ( typeof object.bgColor === 'string' ) {
		let color = misc.hexToRgba( object.bgColor ) ;
		attr[ BPOS_BG_R ] = color.r ;
		attr[ BPOS_BG_G ] = color.g ;
		attr[ BPOS_BG_B ] = color.b ;
		attr[ BPOS_BG_A ] = color.a ;
	}
	else if ( object.bgColor && typeof object.bgColor === 'object' ) {
		attr[ BPOS_BG_R ] = + object.bgColor.r || 0 ;
		attr[ BPOS_BG_G ] = + object.bgColor.g || 0 ;
		attr[ BPOS_BG_B ] = + object.bgColor.b || 0 ;
		attr[ BPOS_BG_A ] = object.bgColor.a !== undefined ? + object.bgColor.a || 0 : 255 ;
	}
	else {
		attr[ BPOS_BG_R ] = 0 ;
		attr[ BPOS_BG_G ] = 0 ;
		attr[ BPOS_BG_B ] = 0 ;
		attr[ BPOS_BG_A ] = 255 ;
	}

	if ( object.styleTransparency ) { attr[ BPOS_MISC ] |= STYLE_TRANSPARENCY ; }
	if ( object.charTransparency ) { attr[ BPOS_MISC ] |= CHAR_TRANSPARENCY ; }

	// Style part
	attr[ BPOS_STYLE ] = 0 ;

	if ( object.bold ) { attr[ BPOS_STYLE ] |= BOLD ; }
	if ( object.dim ) { attr[ BPOS_STYLE ] |= DIM ; }
	if ( object.italic ) { attr[ BPOS_STYLE ] |= ITALIC ; }
	if ( object.underline ) { attr[ BPOS_STYLE ] |= UNDERLINE ; }
	if ( object.blink ) { attr[ BPOS_STYLE ] |= BLINK ; }
	if ( object.inverse ) { attr[ BPOS_STYLE ] |= INVERSE ; }
	if ( object.hidden ) { attr[ BPOS_STYLE ] |= HIDDEN ; }
	if ( object.strike ) { attr[ BPOS_STYLE ] |= STRIKE ; }

	return attr ;
} ;

ScreenBufferHD.prototype.object2attr = ScreenBufferHD.object2attr ;



ScreenBufferHD.attrAndObject = function( attr , object ) {
	if ( ! object || typeof object !== 'object' ) { return attr ; }

	// Misc and color part

	if ( object.color && typeof object.color === 'object' ) {
		if ( object.color.r !== undefined ) { attr[ BPOS_R ] = + object.color.r || 0 ; }
		if ( object.color.g !== undefined ) { attr[ BPOS_G ] = + object.color.g || 0 ; }
		if ( object.color.b !== undefined ) { attr[ BPOS_B ] = + object.color.b || 0 ; }
		if ( object.color.a !== undefined ) { attr[ BPOS_A ] = + object.color.a || 0 ; }
	}

	if ( object.bgColor && typeof object.bgColor === 'object' ) {
		if ( object.bgColor.r !== undefined ) { attr[ BPOS_BG_R ] = + object.bgColor.r || 0 ; }
		if ( object.bgColor.g !== undefined ) { attr[ BPOS_BG_G ] = + object.bgColor.g || 0 ; }
		if ( object.bgColor.b !== undefined ) { attr[ BPOS_BG_B ] = + object.bgColor.b || 0 ; }
		if ( object.bgColor.a !== undefined ) { attr[ BPOS_BG_A ] = + object.bgColor.a || 0 ; }
	}

	if ( object.styleTransparency === true ) { attr[ BPOS_MISC ] |= STYLE_TRANSPARENCY ; }
	else if ( object.styleTransparency === false ) { attr[ BPOS_MISC ] &= ~ STYLE_TRANSPARENCY ; }

	if ( object.charTransparency === true ) { attr[ BPOS_MISC ] |= CHAR_TRANSPARENCY ; }
	else if ( object.charTransparency === false ) { attr[ BPOS_MISC ] &= ~ CHAR_TRANSPARENCY ; }

	// Style part
	if ( object.bold === true ) { attr[ BPOS_STYLE ] |= BOLD ; }
	else if ( object.bold === false ) { attr[ BPOS_STYLE ] &= ~ BOLD ; }

	if ( object.dim === true ) { attr[ BPOS_STYLE ] |= DIM ; }
	else if ( object.dim === false ) { attr[ BPOS_STYLE ] &= ~ DIM ; }

	if ( object.italic === true ) { attr[ BPOS_STYLE ] |= ITALIC ; }
	else if ( object.italic === false ) { attr[ BPOS_STYLE ] &= ~ ITALIC ; }

	if ( object.underline === true ) { attr[ BPOS_STYLE ] |= UNDERLINE ; }
	else if ( object.underline === false ) { attr[ BPOS_STYLE ] &= ~ UNDERLINE ; }

	if ( object.blink === true ) { attr[ BPOS_STYLE ] |= BLINK ; }
	else if ( object.blink === false ) { attr[ BPOS_STYLE ] &= ~ BLINK ; }

	if ( object.inverse === true ) { attr[ BPOS_STYLE ] |= INVERSE ; }
	else if ( object.inverse === false ) { attr[ BPOS_STYLE ] &= ~ INVERSE ; }

	if ( object.hidden === true ) { attr[ BPOS_STYLE ] |= HIDDEN ; }
	else if ( object.hidden === false ) { attr[ BPOS_STYLE ] &= ~ HIDDEN ; }

	if ( object.strike === true ) { attr[ BPOS_STYLE ] |= STRIKE ; }
	else if ( object.strike === false ) { attr[ BPOS_STYLE ] &= ~ STRIKE ; }

	return attr ;
} ;

ScreenBufferHD.prototype.attrAndObject = ScreenBufferHD.attrAndObject ;

// Used by TextBuffer for selection
ScreenBufferHD.attrInverse = ScreenBufferHD.prototype.attrInverse = attr => { attr[ BPOS_STYLE ] ^= INVERSE ; return attr ; } ;





/* Constants */



// General purpose flags
const NONE = 0 ;	// Nothing

// Attr byte positions
const BPOS_R = 0 ;
const BPOS_G = 1 ;
const BPOS_B = 2 ;
const BPOS_A = 3 ;
const BPOS_BG_R = 4 ;
const BPOS_BG_G = 5 ;
const BPOS_BG_B = 6 ;
const BPOS_BG_A = 7 ;
const BPOS_STYLE = 8 ;
const BPOS_MISC = 9 ;

const BPOS_FG = 0 ;
const BPOS_BG = 4 ;


// Style flags
const BOLD = 1 ;
const DIM = 2 ;
const ITALIC = 4 ;
const UNDERLINE = 8 ;
const BLINK = 16 ;
const INVERSE = 32 ;
const HIDDEN = 64 ;
const STRIKE = 128 ;

const BOLD_DIM = BOLD | DIM ;

// Misc flags
const STYLE_TRANSPARENCY = 4 ;
const CHAR_TRANSPARENCY = 8 ;

// Special color: default terminal color
//const FG_DEFAULT_COLOR = 16 ;
//const BG_DEFAULT_COLOR = 32 ;

const MISC_ATTR_MASK = STYLE_TRANSPARENCY | CHAR_TRANSPARENCY ;		// | FG_DEFAULT_COLOR | BG_DEFAULT_COLOR ;

// E.g.: if it needs redraw
// Was never implemented, could be replaced by a full-transparency check
//const VOID = 32 ;

const LEADING_FULLWIDTH = 64 ;
const TRAILING_FULLWIDTH = 128 ;
const FULLWIDTH = LEADING_FULLWIDTH | TRAILING_FULLWIDTH ;
const REMOVE_FULLWIDTH_FLAG = 255 ^ FULLWIDTH ;

// Unused bits: 1, 2, 16, 32



// Tuning
const OUTPUT_THRESHOLD = 10000 ;	// minimum amount of data to retain before sending them to the terminal



/*
	Cell structure:
	- 4 bytes: fg rgba
	- 4 bytes: bg rgba
	- 1 byte: style
	- 1 byte: misc/blending flags
*/

// Data structure
ScreenBufferHD.prototype.ATTR_SIZE = 10 ;
ScreenBufferHD.prototype.CHAR_SIZE = 4 ;
ScreenBufferHD.prototype.ITEM_SIZE = ScreenBufferHD.prototype.ATTR_SIZE + ScreenBufferHD.prototype.CHAR_SIZE ;

ScreenBufferHD.DEFAULT_ATTR =	// <- used by TextBuffer
ScreenBufferHD.prototype.DEFAULT_ATTR = ScreenBufferHD.object2attr( {
	color: {
		r: 255 , g: 255 , b: 255 , a: 255
	} ,
	bgColor: {
		r: 0 , g: 0 , b: 0 , a: 255
	}
} ) ;

ScreenBufferHD.prototype.CLEAR_ATTR = ScreenBufferHD.object2attr( {
	color: {
		r: 255 , g: 255 , b: 255 , a: 0
	} ,
	bgColor: {
		r: 0 , g: 0 , b: 0 , a: 0
	} ,
	charTransparency: true ,
	styleTransparency: true
} ) ;

ScreenBufferHD.prototype.CLEAR_BUFFER = Buffer.allocUnsafe( ScreenBufferHD.prototype.ITEM_SIZE ) ;
ScreenBufferHD.prototype.CLEAR_ATTR.copy( ScreenBufferHD.prototype.CLEAR_BUFFER ) ;
ScreenBufferHD.prototype.CLEAR_BUFFER.write( ' \x00\x00\x00' , ScreenBufferHD.prototype.ATTR_SIZE ) ;	// space

ScreenBufferHD.prototype.LEADING_FULLWIDTH = LEADING_FULLWIDTH ;
ScreenBufferHD.prototype.TRAILING_FULLWIDTH = TRAILING_FULLWIDTH ;



// Loader/Saver, mostly obsolete

ScreenBufferHD.loadSyncV2 = function( filepath ) {
	var i , content , header , screenBuffer ;

	// Let it crash if nothing found
	content = fs.readFileSync( filepath ) ;

	// See if we have got a 'SB' at the begining of the file
	if ( content.length < 3 || content.toString( 'ascii' , 0 , 3 ) !== 'SB\n' ) {
		throw new Error( 'Magic number mismatch: this is not a ScreenBufferHD file' ) ;
	}

	// search for the second \n
	for ( i = 3 ; i < content.length ; i ++ ) {
		if ( content[ i ] === 0x0a ) { break ; }
	}

	if ( i === content.length ) {
		throw new Error( 'No header found: this is not a ScreenBufferHD file' ) ;
	}

	// Try to parse a JSON header
	try {
		header = JSON.parse( content.toString( 'utf8' , 3 , i ) ) ;
	}
	catch( error ) {
		throw new Error( 'No correct one-lined JSON header found: this is not a ScreenBufferHD file' ) ;
	}

	// Mandatory header field
	if ( header.version === undefined || header.width === undefined || header.height === undefined ) {
		throw new Error( 'Missing mandatory header data, this is a corrupted or obsolete ScreenBufferHD file' ) ;
	}

	// Check bitsPerColor
	if ( header.bitsPerColor && header.bitsPerColor !== ScreenBufferHD.prototype.bitsPerColor ) {
		throw new Error( 'Bad Bits Per Color: ' + header.bitsPerColor + ' (should be ' + ScreenBufferHD.prototype.bitsPerColor + ')' ) ;
	}

	// Bad size?
	if ( content.length !== i + 1 + header.width * header.height * ScreenBufferHD.prototype.ITEM_SIZE ) {
		throw new Error( 'Bad file size: this is a corrupted ScreenBufferHD file' ) ;
	}

	// So the file exists, create a canvas based upon it
	screenBuffer = new ScreenBufferHD( {
		width: header.width ,
		height: header.height
	} ) ;

	content.copy( screenBuffer.buffer , 0 , i + 1 ) ;

	return screenBuffer ;
} ;



// This new format use JSON header for a maximal flexibility rather than a fixed binary header.
// The header start with a magic number SB\n then a compact single-line JSON that end with an \n.
// So the data part start after the second \n, providing a variable header size.
// This will allow adding meta data without actually changing the file format.
ScreenBufferHD.prototype.saveSyncV2 = function( filepath ) {
	var content , header ;

	header = {
		version: 2 ,
		width: this.width ,
		height: this.height
	} ;

	header = 'SB\n' + JSON.stringify( header ) + '\n' ;

	content = Buffer.allocUnsafe( header.length + this.buffer.length ) ;
	content.write( header ) ;

	this.buffer.copy( content , header.length ) ;

	// Let it crash if something bad happens
	fs.writeFileSync( filepath , content ) ;
} ;



ScreenBufferHD.loadSync = ScreenBufferHD.loadSyncV2 ;
ScreenBufferHD.prototype.saveSync = ScreenBufferHD.prototype.saveSyncV2 ;


}).call(this)}).call(this,require("buffer").Buffer)
},{"./ScreenBuffer.js":3,"./colorScheme/gnome.json":11,"./misc.js":48,"./termkit.js":56,"buffer":159,"fs":148,"string-kit":133}],5:[function(require,module,exports){
(function (process,Buffer){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const tree = require( 'tree-kit' ) ;
const string = require( 'string-kit' ) ;
const NextGenEvents = require( 'nextgen-events' ) ;
const Promise = require( 'seventh' ) ;

const termkit = require( './termkit.js' ) ;
//function noop() {}



/*
	Since there is a lot of hacks with the Terminal instance creation, we can't use the 'new' operator at all...
*/
function Terminal( ... args ) { return Terminal.create( ... args ) ; }

Terminal.prototype = Object.create( NextGenEvents.prototype ) ;
Terminal.prototype.constructor = Terminal ;
module.exports = Terminal ;



Terminal.create = function( createOptions ) {
	// Default options...
	if ( ! createOptions || typeof createOptions !== 'object' ) { createOptions = {} ; }
	if ( ! createOptions.stdin ) { createOptions.stdin = process.stdin ; }
	if ( ! createOptions.stdout ) { createOptions.stdout = process.stdout ; }
	if ( ! createOptions.stderr ) { createOptions.stderr = process.stderr ; }
	if ( typeof createOptions.generic !== 'string' ) { createOptions.generic = 'xterm' ; }

	var k ;

	var termconfig ;
	var chainable = Object.create( notChainable ) ;
	var options = {
		on: '' , off: '' , params: 0 , out: createOptions.stdout
	} ;

	var term = applyEscape.bind( undefined , options ) ;

	// Yay, this is a nasty hack...
	Object.setPrototypeOf( term , chainable ) ;
	term.apply = Function.prototype.apply ;
	term.call = Function.prototype.call ;

	// Fix the root
	options.root = term ;
	term.root = term ;

	term.options = options ;
	term.stdin = createOptions.stdin ;
	term.stdout = createOptions.stdout ;
	term.stderr = createOptions.stderr ;
	term.generic = createOptions.generic ;
	term.appId = createOptions.appId ;
	term.appName = createOptions.appName ;
	term.isTTY = createOptions.isTTY === undefined ? true : !! createOptions.isTTY ;
	term.isSSH = !! createOptions.isSSH ;
	term.pid = createOptions.pid ;
	term.grabbing = false ;
	term.mouseGrabbing = false ;
	term.focusGrabbing = false ;
	term.timeout = term.isSSH ? 500 : 200 ;		// The value is a bit high, to prevent lag due to huge terminal load
	term.shutdown = false ;
	term.raw = term.stdout.write.bind( term.stdout ) ;	// Used by ScreenBuffer, for optimization

	term.onStdin = onStdin.bind( term ) ;	// bindings...
	term.prependStdinChunk = null ;
	term.metaKeyPrefix = null ;
	term.metaKeyRemove = null ;

	term.lock = {} ;

	term.wrapOptions = {
		x: 1 ,
		width: null ,
		continue: false ,
		offset: 0
	} ;

	// Screen size
	term.width = undefined ;
	term.height = undefined ;
	onResize.call( term ) ;

	// Resizing event, by order of preference
	if ( createOptions.preferProcessSigwinch ) { process.on( 'SIGWINCH' , onResize.bind( term ) ) ; }
	else if ( term.stdout.isTTY ) { term.stdout.on( 'resize' , onResize.bind( term ) ) ; }
	else if ( createOptions.processSigwinch ) { process.on( 'SIGWINCH' , onResize.bind( term ) ) ; }

	// States
	term.state = {
		fullscreen: false ,
		button: {
			left: null ,
			middle: null ,
			right: null ,
			other: null
		}
	} ;

	if ( term.appId ) {
		// We have got the real terminal app
		try {
			term.termconfigFile = term.appId + '.js' ;
			termconfig = require( './termconfig/' + term.termconfigFile ) ;
		}
		catch ( error ) {} // Do nothing, let the next if block handle the case
	}

	if ( ! termconfig ) {
		// The real terminal app is not known, or we fail to load it...
		// Fallback to the terminal generic (most of time, got from the $TERM env variable).
		try {
			// If a .generic.js file exists, this is a widely used terminal generic, 'xterm' for example.
			// We should use this generic files because despite advertising them as 'xterm',
			// most terminal sucks at being truly 'xterm' compatible (only 33% to 50% of xterm capabilities
			// are supported, even gnome-terminal and Konsole are bad).
			// So we will try to maintain a fail-safe xterm generic config.
			term.termconfigFile = term.generic + '.generic.js' ;
			termconfig = require( './termconfig/' + term.termconfigFile ) ;
		}
		catch ( error ) {
			try {
				// No generic config exists, try a specific config
				term.termconfigFile = term.generic + '.js' ;
				termconfig = require( './termconfig/' + term.termconfigFile ) ;
			}
			catch ( error_ ) {
				// Nothing found, fallback to the most common terminal generic
				term.termconfigFile = 'xterm.generic.js' ;
				termconfig = require( './termconfig/' + term.termconfigFile ) ;
			}
		}
	}

	//console.log( term.termconfigFile ) ;

	// if needed, this should be replaced by some tput commands?

	term.esc = tree.extend( { deep: true } , {} , termconfig.esc ) ;
	term.support = tree.extend( { deep: true } , {} , termconfig.support ) ;

	tree.extend(
		null ,		// Do not use deep:true here
		term.esc ,
		pseudoEsc ,
		{
			// Aliases
			gray: term.esc.brightBlack ,
			grey: term.esc.brightBlack ,
			bgGray: term.esc.bgBrightBlack ,
			bgGrey: term.esc.bgBrightBlack
		}
	) ;

	term.handler = tree.extend( null , {} , termconfig.handler ) ;
	term.keymap = tree.extend( { deep: true } , {} , termconfig.keymap ) ;
	term.colorRegister = tree.extend( { deep: true } , [] , defaultColorRegister , termconfig.colorRegister ) ;

	term.escHandler = { root: term } ;
	term.escOffHandler = { root: term } ;

	// reverse keymap
	term.rKeymap = [] ;
	term.rKeymapMaxSize = -1 ;
	term.rKeymapStarter = [] ;
	term.rKeymapStarterMaxSize = -1 ;

	Object.keys( term.keymap ).forEach( ( key ) => {

		var i , j , keymapObject , code , codeList = term.keymap[ key ] ;

		if ( ! Array.isArray( codeList ) ) { codeList = [ codeList ] ; term.keymap[ key ] = codeList ; }

		for ( j = 0 ; j < codeList.length ; j ++ ) {
			code = codeList[ j ] ;

			if ( typeof code === 'object' ) {
				keymapObject = code ;
				keymapObject.name = key ;
				code = keymapObject.code ;
			}
			else {
				keymapObject = {
					code: code ,
					name: key ,
					matches: [ key ]
				} ;

				term.keymap[ key ][ j ] = { code: code } ;
			}

			// keymap handler
			if ( keymapObject.handler && typeof keymapObject.handler !== 'function' ) {
				term.keymap[ key ][ j ].handler = term.handler[ keymapObject.handler ] ;
			}

			if ( code ) {
				if ( code.length > term.rKeymapMaxSize ) {
					for ( i = term.rKeymapMaxSize + 1 ; i <= code.length ; i ++ ) { term.rKeymap[ i ] = {} ; }
					term.rKeymapMaxSize = code.length ;
				}

				if ( term.rKeymap[ code.length ][ code ] ) {
					term.rKeymap[ code.length ][ code ].matches.push( key ) ;
				}
				else {
					term.rKeymap[ code.length ][ code ] = keymapObject ;
					term.rKeymap[ code.length ][ code ].matches = [ key ] ;
				}
			}
			else {
				if ( ! keymapObject.starter || ! keymapObject.ender || ! keymapObject.handler ) { continue ; }

				if ( keymapObject.starter.length > term.rKeymapStarterMaxSize ) {
					for ( i = term.rKeymapStarterMaxSize + 1 ; i <= keymapObject.starter.length ; i ++ ) { term.rKeymapStarter[ i ] = {} ; }
					term.rKeymapStarterMaxSize = keymapObject.starter.length ;
				}

				if ( term.rKeymapStarter[ keymapObject.starter.length ][ keymapObject.starter ] ) {
					term.rKeymapStarter[ keymapObject.starter.length ][ keymapObject.starter ].push( key ) ;
				}
				else {
					term.rKeymapStarter[ keymapObject.starter.length ][ keymapObject.starter ] = [ keymapObject ] ;
				}
			}
		}
	} ) ;


	// Create methods for the 'chainable' prototype

	Object.keys( term.esc ).forEach( ( key ) => {
		if ( ! term.esc[ key ] || typeof term.esc[ key ] !== 'object' ) {
			console.error( "Bad escape sequence entry '" + key + "' using termconfig: '" + term.termconfigFile + "'." ) ;
			return ;
		}

		// build-time resolution
		if ( typeof term.esc[ key ].on === 'function' ) { term.esc[ key ].on = term.esc[ key ].on.call( term ) ; }
		if ( typeof term.esc[ key ].off === 'function' ) { term.esc[ key ].off = term.esc[ key ].off.call( term ) ; }

		// dynamic handler
		if ( term.esc[ key ].handler ) {
			if ( typeof term.esc[ key ].handler === 'function' ) { term.escHandler[ key ] = term.esc[ key ].handler.bind( term ) ; }
			else { term.escHandler[ key ] = term.handler[ term.esc[ key ].handler ] ; }
		}

		// dynamic off handler
		if ( term.esc[ key ].offHandler ) {
			if ( typeof term.esc[ key ].offHandler === 'function' ) { term.escOffHandler[ key ] = term.esc[ key ].offHandler.bind( term ) ; }
			else { term.escOffHandler[ key ] = term.handler[ term.esc[ key ].offHandler ] ; }
		}

		Object.defineProperty( chainable , key , {
			configurable: true ,
			get: function() {
				var fn , chainOptions ;

				chainOptions = Object.assign( {} , this.options ) ;

				chainOptions.on += this.root.esc[ key ].on || '' ;
				chainOptions.off = ( this.root.esc[ key ].off || '' ) + chainOptions.off ;
				chainOptions.params += string.format.count( this.root.esc[ key ].on ) ;

				if ( ! chainOptions.onHasFormatting &&
					( chainOptions.params ||
						( typeof this.root.esc[ key ].on === 'string' &&
							string.format.hasFormatting( this.root.esc[ key ].on ) ) ) ) {
					chainOptions.onHasFormatting = true ;
				}

				if ( ! chainOptions.offHasFormatting &&
					( typeof this.root.esc[ key ].off === 'string' &&
						string.format.hasFormatting( this.root.esc[ key ].off ) ) ) {
					chainOptions.offHasFormatting = true ;
				}

				if ( this.root.esc[ key ].err ) { chainOptions.err = true ; chainOptions.out = this.root.stderr ; }
				if ( this.root.esc[ key ].str ) { chainOptions.str = true ; }
				if ( this.root.esc[ key ].bind ) { chainOptions.bind = true ; }
				if ( this.root.esc[ key ].forceStyleOnReset ) { chainOptions.forceStyleOnReset = true ; }
				if ( this.root.esc[ key ].noFormat ) { chainOptions.noFormat = true ; }
				if ( this.root.esc[ key ].markupOnly ) { chainOptions.markupOnly = true ; }
				if ( this.root.esc[ key ].wrap ) { chainOptions.wrap = true ; }

				fn = applyEscape.bind( undefined , chainOptions ) ;

				// Yay, this is a nasty hack...
				Object.setPrototypeOf( fn , chainable ) ;
				fn.apply = Function.prototype.apply ;
				fn.root = this.root || this ;
				fn.options = chainOptions ;

				// Replace the getter by the newly created function, to speed up further call
				Object.defineProperty( this , key , { value: fn , configurable: true } ) ;

				//console.log( 'Create function:' , key ) ;

				return fn ;
			}
		} ) ;
	} ) ;

	// Format and markup config
	term.resetString = '' ;
	term.setResetString = function( str ) {
		term.resetString = string.markupMethod.call( term.formatConfig.rawMarkupConfig , str ) ;
	} ;

	var resetFn = ( extra ) => term.str.styleReset() + term.resetString + extra ;

	term.formatConfig = {
		fn: {} ,
		endingMarkupReset: true ,
		markupReset: resetFn.bind( undefined , '' ) ,
		//markupReset: term.str.styleReset() ,
		shiftMarkup: {
			'#': 'background'
		} ,
		markup: {
			":": resetFn.bind( undefined , '' ) ,
			" ": resetFn.bind( undefined , ' ' ) ,
			//":": term.str.styleReset() ,
			//" ": term.str.styleReset() + ' ' ,

			"-": term.str.dim() ,
			"+": term.str.bold() ,
			"_": term.str.underline() ,
			"/": term.str.italic() ,
			"!": term.str.inverse() ,

			"b": term.str.blue() ,
			"B": term.str.brightBlue() ,
			"c": term.str.cyan() ,
			"C": term.str.brightCyan() ,
			"g": term.str.green() ,
			"G": term.str.brightGreen() ,
			"k": term.str.black() ,
			"K": term.str.brightBlack() ,
			"m": term.str.magenta() ,
			"M": term.str.brightMagenta() ,
			"r": term.str.red() ,
			"R": term.str.brightRed() ,
			"w": term.str.white() ,
			"W": term.str.brightWhite() ,
			"y": term.str.yellow() ,
			"Y": term.str.brightYellow()
		} ,
		shiftedMarkup: {
			background: {
				":": resetFn.bind( undefined , '' ) ,
				" ": resetFn.bind( undefined , ' ' ) ,
				//":": term.str.styleReset() ,
				//" ": term.str.styleReset() + ' ' ,

				"b": term.str.bgBlue() ,
				"B": term.str.bgBrightBlue() ,
				"c": term.str.bgCyan() ,
				"C": term.str.bgBrightCyan() ,
				"g": term.str.bgGreen() ,
				"G": term.str.bgBrightGreen() ,
				"k": term.str.bgBlack() ,
				"K": term.str.bgBrightBlack() ,
				"m": term.str.bgMagenta() ,
				"M": term.str.bgBrightMagenta() ,
				"r": term.str.bgRed() ,
				"R": term.str.bgBrightRed() ,
				"w": term.str.bgWhite() ,
				"W": term.str.bgBrightWhite() ,
				"y": term.str.bgYellow() ,
				"Y": term.str.bgBrightYellow()
			}
		} ,
		dataMarkup: {
			fg: ( markupStack , key , value ) => {
				var str ;

				if ( typeof value === 'string' && value[ 0 ] === '#' ) {
					let hex = value.slice( 1 ) ;
					if ( hex.length === 3 ) { hex = hex[ 0 ] + hex[ 0 ] + hex[ 1 ] + hex[ 1 ] + hex[ 2 ] + hex[ 2 ] ; }
					let r = parseInt( hex.slice( 0 , 2 ) , 16 ) || 0 ;
					let g = parseInt( hex.slice( 2 , 4 ) , 16 ) || 0 ;
					let b = parseInt( hex.slice( 4 , 6 ) , 16 ) || 0 ;
					str = term.optimized.color24bits( r , g , b ) ;
				}
				else {
					str = term.str.color( value ) ;
				}

				markupStack.push( str ) ;
				return str ;
			} ,
			bg: ( markupStack , key , value ) => {
				var str ;

				if ( typeof value === 'string' && value[ 0 ] === '#' ) {
					let hex = value.slice( 1 ) ;
					if ( hex.length === 3 ) { hex = hex[ 0 ] + hex[ 0 ] + hex[ 1 ] + hex[ 1 ] + hex[ 2 ] + hex[ 2 ] ; }
					let r = parseInt( hex.slice( 0 , 2 ) , 16 ) || 0 ;
					let g = parseInt( hex.slice( 2 , 4 ) , 16 ) || 0 ;
					let b = parseInt( hex.slice( 4 , 6 ) , 16 ) || 0 ;
					str = term.optimized.bgColor24bits( r , g , b ) ;
				}
				else {
					str = term.str.bgColor( value ) ;
				}

				markupStack.push( str ) ;
				return str ;
			}
		} ,
		markupCatchAll: ( markupStack , key , value ) => {
			term.optimized.bgColor24bits ;
			var str = '' ;

			if ( value === undefined ) {
				if ( key[ 0 ] === '#' ) {
					return term.formatConfig.dataMarkup.fg( markupStack , 'fg' , key ) ;
				}
				else if ( termkit.markupCatchAllKeywords[ key ] ) {
					switch ( termkit.markupCatchAllKeywords[ key ][ 0 ] ) {
						case 'color' :
							return term.formatConfig.dataMarkup.fg( markupStack , 'fg' , termkit.markupCatchAllKeywords[ key ][ 1 ] ) ;
						case 'bgColor' :
							return term.formatConfig.dataMarkup.bg( markupStack , 'bg' , termkit.markupCatchAllKeywords[ key ][ 1 ] ) ;
						case 'dim' :
							str = term.str.dim() ;
							break ;
						case 'bold' :
							str = term.str.bold() ;
							break ;
						case 'underline' :
							str = term.str.underline() ;
							break ;
						case 'italic' :
							str = term.str.italic() ;
							break ;
						case 'inverse' :
							str = term.str.inverse() ;
							break ;
					}
				}
			}

			markupStack.push( str ) ;
			return str ;
		}
	} ;

	// Aliases...
	term.formatConfig.dataMarkup.color = term.formatConfig.dataMarkup.fgColor = term.formatConfig.dataMarkup.c = term.formatConfig.dataMarkup.fg ;
	term.formatConfig.dataMarkup.bgColor = term.formatConfig.dataMarkup.bg ;

	term.formatConfig.rawMarkupConfig = Object.create( term.formatConfig ) ;
	term.formatConfig.rawMarkupConfig.startingMarkupReset = false ;
	term.formatConfig.rawMarkupConfig.endingMarkupReset = false ;

	for ( k in term.escHandler ) { term.formatConfig.fn[ k ] = term.escHandler[ k ] ; }
	for ( k in term.escOffHandler ) { term.formatConfig.fn[ k + '_off' ] = term.escOffHandler[ k ] ; }

	term.format = string.createFormatter( term.formatConfig ) ;
	term.markup = string.createMarkup( term.formatConfig ) ;
	term.options = options ;

	// Should come after any escape sequence definitions
	createOptimized( term ) ;

	// Register various exit handler
	// Fix the issue #3, turn grabInput off on exit
	// Disable input grabbing at exit.
	// Note: the terminal can still send some garbage if it was about to do it when exit kickin.

	process.on( 'exit' , () => {
		//console.log( '>>> exit' ) ;
		// Cleanup was done from elsewhere, probably by .asyncCleanup()
		if ( term.shutdown ) { return ; }
		term.shutdown = true ;
		term.styleReset() ;
		term.grabInput( false ) ;
	} ) ;

	// Promise.asyncExit() produce this:
	process.on( 'asyncExit' , ( code , timeout , done ) => {
		//console.log( '>>> asyncExit' ) ;
		term.asyncCleanup().then( done ) ;
	} ) ;

	// The event loop is empty, we have more time to clean up things:
	// We keep the process running for a little bit of time, to prevent the terminal from displaying garbage.
	process.once( 'beforeExit' , () => {
		//console.log( '>>> beforeExit' ) ;
		term.asyncCleanup() ;
	} ) ;

	// Should be done at the end, once everything is working: it needs a configured terminal to generate escape sequences
	term.palette = new termkit.Palette( { system: true , term: term } ) ;

	return term ;
} ;





/* Optimized */



function createOptimized( term ) {
	// This is a subset of the terminal capability, mainly used to speed up ScreenBuffer and ScreenBufferHD
	var i ;

	term.optimized = {} ;

	// reset
	term.optimized.styleReset = term.str.styleReset() ;

	// Styles
	term.optimized.bold = term.str.bold() ;
	term.optimized.dim = term.str.dim() ;
	term.optimized.italic = term.str.italic() ;
	term.optimized.underline = term.str.underline() ;
	term.optimized.blink = term.str.blink() ;
	term.optimized.inverse = term.str.inverse() ;
	term.optimized.hidden = term.str.hidden() ;
	term.optimized.strike = term.str.strike() ;

	term.optimized.noBold = term.str.bold( false ) ;
	term.optimized.noDim = term.str.dim( false ) ;
	term.optimized.noItalic = term.str.italic( false ) ;
	term.optimized.noUnderline = term.str.underline( false ) ;
	term.optimized.noBlink = term.str.blink( false ) ;
	term.optimized.noInverse = term.str.inverse( false ) ;
	term.optimized.noHidden = term.str.hidden( false ) ;
	term.optimized.noStrike = term.str.strike( false ) ;

	// Colors
	term.optimized.color256 = [] ;
	term.optimized.bgColor256 = [] ;

	for ( i = 0 ; i <= 255 ; i ++ ) {
		term.optimized.color256[ i ] = term.str.color256( i ) ;
		term.optimized.bgColor256[ i ] = term.str.bgColor256( i ) ;
	}

	term.optimized.defaultColor = term.str.defaultColor() ;
	term.optimized.bgDefaultColor = term.str.bgDefaultColor() ;

	// Move To
	term.optimized.moveTo = term.esc.moveTo.optimized || term.str.moveTo ;

	// Move 1 char to the right
	term.optimized.right = term.str.right( 1 ) ;

	// 24 bits colors
	term.optimized.color24bits = term.esc.color24bits.optimized || term.str.color24bits ;
	term.optimized.bgColor24bits = term.esc.bgColor24bits.optimized || term.str.bgColor24bits ;
}





/* Apply */



// CAUTION: 'options' MUST NOT BE OVERWRITTEN!
// It is bound at the function creation and contains function specificities!
function applyEscape( options , ... args ) {
	var fn , newOptions , wrapOptions ;

	// Cause trouble because the shutting down process itself needs to send escape sequences asynchronously
	//if ( options.root.shutdown && ! options.str ) { return options.root ; }

	if ( options.bounded ) { args = options.bounded.concat( args ) ; }

	//console.error( args ) ;
	if ( options.bind ) {
		newOptions = Object.assign( {} , options , { bind: false , bounded: args } ) ;
		fn = applyEscape.bind( this , newOptions ) ;

		// Still a nasty hack...
		Object.setPrototypeOf( fn , Object.getPrototypeOf( options.root ) ) ;
		fn.apply = Function.prototype.apply ;
		fn.root = options.root ;
		fn.options = newOptions ;

		return fn ;
	}

	var onFormat = [ options.on ] , output , on , off ;
	var action = args[ options.params ] ;

	// If not enough arguments, return right now
	// Well... what about term.up(), term.previousLine(), and so on?
	//if ( arguments.length < 1 + options.params && ( action === null || action === false ) ) { return options.root ; }

	if ( options.params ) {
		onFormat = onFormat.concat( args.slice( 0 , options.params ) ) ;
	}

	//console.log( '\n>>> Action:' , action , '<<<\n' ) ;
	//console.log( 'Attributes:' , attributes ) ;
	if ( action === undefined || action === true ) {
		on = options.onHasFormatting ? options.root.format( ... onFormat ) : options.on ;
		if ( options.str ) { return on ; }
		options.out.write( on ) ;
		return options.root ;
	}

	if ( action === null || action === false ) {
		off = options.offHasFormatting ? options.root.format( options.off ) : options.off ;
		if ( options.str ) { return off ; }
		options.out.write( off ) ;
		return options.root ;
	}

	if ( typeof action !== 'string' ) {
		if ( typeof action.toString === 'function' ) { action = action.toString() ; }
		else { action = '' ; }
	}

	// So we have got a string

	on = options.onHasFormatting ? options.root.format( ... onFormat ) : options.on ;

	if ( options.markupOnly ) {
		action = options.root.markup( ... args.slice( options.params ) ) ;
	}
	else if ( ! options.noFormat ) {
		action = options.root.format( ... args.slice( options.params ) ) ;
	}

	if ( options.wrap ) {
		if ( options.root.wrapOptions.x && options.root.wrapOptions.x > 1 ) {
			wrapOptions = {
				width: options.root.wrapOptions.width || options.root.width - options.root.wrapOptions.x + 1 ,
				glue: '\n' + options.root.str.column( options.root.wrapOptions.x ) ,
				offset: options.root.wrapOptions.offset ,
				updateOffset: true ,
				skipFn: termkit.escapeSequenceSkipFn
			} ;

			action = string.wordwrap( action , wrapOptions ) ;

			if ( ! options.root.wrapOptions.continue ) {
				action = options.root.str.column( options.root.wrapOptions.x ) + action ;
			}

			options.root.wrapOptions.continue = true ;
			options.root.wrapOptions.offset = wrapOptions.offset ;
		}
		else {
			wrapOptions = {
				width: options.root.wrapOptions.width || options.root.width ,
				glue: '\n' ,
				offset: options.root.wrapOptions.offset ,
				updateOffset: true ,
				skipFn: termkit.escapeSequenceSkipFn
			} ;

			action = string.wordwrap( action , wrapOptions ) ;
			options.root.wrapOptions.continue = true ;
			options.root.wrapOptions.offset = wrapOptions.offset ;
		}
	}
	else {
		// All non-wrapped string display reset the offset
		options.root.wrapOptions.continue = false ;
		options.root.wrapOptions.offset = 0 ;
	}

	off = options.offHasFormatting ? options.root.format( options.off ) : options.off ;

	if ( options.forceStyleOnReset ) {
		action = action.replace( new RegExp( string.escape.regExp( options.root.optimized.styleReset ) , 'g' ) , options.root.optimized.styleReset + on ) ;
	}

	if ( options.root.resetString ) {
		output = options.root.resetString + on + action + off + options.root.resetString ;
	}
	else {
		output = on + action + off ;
	}

	// tmp hack?
	if ( options.crlf ) { output = output.replace( /\n/g , '\r\n' ) ; }

	if ( options.str ) { return output ; }

	options.out.write( output ) ;

	return options.root ;
}





/* Pseudo esc */



var pseudoEsc = {
	// It just set error:true so it will write to STDERR instead of STDOUT
	error: { err: true } ,

	// It just set str:true so it will not write anything, but return the value in a string
	str: { str: true } ,

	// It just set attr:true so it will not write anything, but return an attribute object
	attr: { attr: true } ,

	// It just set bind to an empty array so it will not do anything except returning a wrapper
	bindArgs: { bind: true } ,

	// It just set forceStyleOnReset:true so it will find style reset and recall the full chain
	forceStyleOnReset: { forceStyleOnReset: true } ,

	// It just set noFormat:true so it will not call string.format() on user input,
	// only useful for ScreenBuffer, so blit-like redraw() can perform slightly faster
	noFormat: { noFormat: true } ,

	// It just set markupOnly:true so it will not use format string but allow caret ^ markup
	markupOnly: { markupOnly: true } ,

	// It just set wrap:true so it will wrap words on different lines
	wrap: { wrap: true } ,

	move: {
		on: '%[move:%a%a]F' ,
		handler: function move( x , y ) {

			var sequence = '' ;

			if ( x ) {
				if ( x > 0 ) { sequence += this.root.format( this.root.esc.right.on , x ) ; }
				else { sequence += this.root.format( this.root.esc.left.on , -x ) ; }
			}

			if ( y ) {
				if ( y > 0 ) { sequence += this.root.format( this.root.esc.down.on , y ) ; }
				else { sequence += this.root.format( this.root.esc.up.on , -y ) ; }
			}

			return sequence ;
		}
	} ,

	color: {
		on: '%[color:%a]F' ,
		off: function() { return this.root.esc.defaultColor.on ; } ,
		handler: function color( c ) {
			if ( typeof c === 'string' ) { c = termkit.colorNameToIndex( c ) ; }
			if ( typeof c !== 'number' ) { return '' ; }

			c = Math.floor( c ) ;

			if ( c < 0 || c > 15 ) { return '' ; }

			if ( c <= 7 ) { return this.root.format( this.root.esc.darkColor.on , c ) ; }
			return this.root.format( this.root.esc.brightColor.on , c - 8 ) ;
		}
	} ,

	bgColor: {
		on: '%[bgColor:%a]F' ,
		off: function() { return this.root.esc.bgDefaultColor.on ; } ,
		handler: function bgColor( c ) {
			if ( typeof c === 'string' ) { c = termkit.colorNameToIndex( c ) ; }
			if ( typeof c !== 'number' ) { return '' ; }

			c = Math.floor( c ) ;

			if ( c < 0 || c > 15 ) { return '' ; }

			if ( c <= 7 ) { return this.root.format( this.root.esc.bgDarkColor.on , c ) ; }
			return this.root.format( this.root.esc.bgBrightColor.on , c - 8 ) ;
		}
	} ,

	colorRgb: {
		on: '%[colorRgb:%a%a%a]F' ,
		off: function() { return this.root.esc.defaultColor.on ; } ,
		handler: colorRgbHandler

	} ,

	bgColorRgb: {
		on: '%[bgColorRgb:%a%a%a]F' ,
		off: function() { return this.root.esc.bgDefaultColor.on ; } ,
		handler: bgColorRgbHandler
	} ,

	colorRgbHex: {
		on: '%[colorRgbHex:%a]F' ,
		off: function() { return this.root.esc.defaultColor.on ; } ,
		handler: colorRgbHandler

	} ,

	bgColorRgbHex: {
		on: '%[bgColorRgbHex:%a]F' ,
		off: function() { return this.root.esc.bgDefaultColor.on ; } ,
		handler: bgColorRgbHandler
	} ,

	colorGrayscale: {
		on: '%[colorGrayscale:%a]F' ,
		off: function() { return this.root.esc.defaultColor.on ; } ,
		handler: function colorGrayscale( g ) {
			var c ;

			if ( typeof g !== 'number' ) { return '' ; }
			if ( g < 0 || g > 255 ) { return '' ; }

			if ( ! this.root.esc.color24bits.na && ! this.root.esc.color24bits.fb ) {
				// The terminal supports 24bits! Yeah!
				return this.root.format( this.root.esc.color24bits.on , g , g , g ) ;
			}

			if ( ! this.root.esc.color256.na && ! this.root.esc.color256.fb ) {
				// The terminal supports 256 colors

				// Convert to 0..25 range
				g = Math.round( g * 25 / 255 ) ;

				if ( g < 0 || g > 25 ) { return '' ; }

				if ( g === 0 ) { c = 16 ; }
				else if ( g === 25 ) { c = 231 ; }
				else { c = g + 231 ; }

				return this.root.format( this.root.esc.color256.on , c ) ;
			}

			// The terminal does not support 256 colors, fallback
			c = this.root.registerForRgb( g , g , g , 0 , 15 ) ;
			return this.root.format( this.root.esc.color.on , c ) ;
		}
	} ,

	bgColorGrayscale: {
		on: '%[bgColorGrayscale:%a]F' ,
		off: function() { return this.root.esc.bgDefaultColor.on ; } ,
		handler: function bgColorGrayscale( g ) {
			var c ;

			if ( typeof g !== 'number' ) { return '' ; }
			if ( g < 0 || g > 255 ) { return '' ; }

			if ( ! this.root.esc.bgColor24bits.na && ! this.root.esc.bgColor24bits.fb ) {
				// The terminal supports 24bits! Yeah!
				return this.root.format( this.root.esc.bgColor24bits.on , g , g , g ) ;
			}

			if ( ! this.root.esc.bgColor256.na && ! this.root.esc.bgColor256.fb ) {
				// Convert to 0..25 range
				g = Math.round( g * 25 / 255 ) ;

				if ( g < 0 || g > 25 ) { return '' ; }

				if ( g === 0 ) { c = 16 ; }
				else if ( g === 25 ) { c = 231 ; }
				else { c = g + 231 ; }

				return this.root.format( this.root.esc.bgColor256.on , c ) ;
			}

			// The terminal does not support 256 colors, fallback
			c = this.root.registerForRgb( g , g , g , 0 , 15 ) ;
			return this.root.format( this.root.esc.bgColor.on , c ) ;
		}
	}
} ;





/* Internal/private functions */



function colorRgbHandler( r , g , b ) {
	var c , rgb ;

	if ( typeof r === 'string' ) {
		rgb = termkit.hexToRgba( r ) ;
		r = rgb.r ; g = rgb.g ; b = rgb.b ;
	}

	if (
		typeof r !== 'number' || isNaN( r ) ||
		typeof g !== 'number' || isNaN( g ) ||
		typeof b !== 'number' || isNaN( b ) ||
		r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255
	) {
		return '' ;
	}

	if ( ! this.root.esc.color24bits.na && ! this.root.esc.color24bits.fb ) {
		// The terminal supports 24bits! Yeah!
		return this.root.format( this.root.esc.color24bits.on , r , g , b ) ;
	}

	if ( ! this.root.esc.color256.na && ! this.root.esc.color256.fb ) {
		// The terminal supports 256 colors

		// Convert to 0..5 range
		r = Math.round( r * 5 / 255 ) ;
		g = Math.round( g * 5 / 255 ) ;
		b = Math.round( b * 5 / 255 ) ;

		c = 16 + r * 36 + g * 6 + b ;

		// min:16 max:231
		//if ( c < 16 || c > 231 ) { return '' ; }

		return this.root.format( this.root.esc.color256.on , c ) ;
	}

	// The terminal does not support 256 colors, fallback
	c = this.root.registerForRgb( r , g , b , 0 , 15 ) ;
	return this.root.format( this.root.esc.color.on , c ) ;
}



function bgColorRgbHandler( r , g , b ) {
	var c , rgb ;

	if ( typeof r === 'string' ) {
		rgb = termkit.hexToRgba( r ) ;
		r = rgb.r ; g = rgb.g ; b = rgb.b ;
	}

	if (
		typeof r !== 'number' || isNaN( r ) ||
		typeof g !== 'number' || isNaN( g ) ||
		typeof b !== 'number' || isNaN( b ) ||
		r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255
	) {
		return '' ;
	}

	if ( ! this.root.esc.bgColor24bits.na && ! this.root.esc.bgColor24bits.fb ) {
		// The terminal supports 24bits! Yeah!
		return this.root.format( this.root.esc.bgColor24bits.on , r , g , b ) ;
	}

	if ( ! this.root.esc.bgColor256.na && ! this.root.esc.bgColor256.fb ) {
		// The terminal supports 256 colors

		// Convert to 0..5 range
		r = Math.round( r * 5 / 255 ) ;
		g = Math.round( g * 5 / 255 ) ;
		b = Math.round( b * 5 / 255 ) ;

		c = 16 + r * 36 + g * 6 + b ;

		// min:16 max:231
		//if ( c < 16 || c > 231 ) { return '' ; }

		return this.root.format( this.root.esc.bgColor256.on , c ) ;
	}

	// The terminal does not support 256 colors, fallback
	c = this.root.registerForRgb( r , g , b , 0 , 15 ) ;
	return this.root.format( this.root.esc.bgColor.on , c ) ;
}



// Called by either SIGWINCH signal or stdout's 'resize' event.
// It is not meant to be used by end-user.
function onResize() {
	if ( this.stdout.columns && this.stdout.rows ) {
		this.width = this.stdout.columns ;
		this.height = this.stdout.rows ;
	}
	else if ( ! this.isTTY ) {
		// If it's not a TTY, we are probably piping to a file, in that case the size is virtually infinite
		this.width = this.height = Infinity ;
	}

	this.emit( 'resize' , this.width , this.height ) ;
}





/* Advanced methods */



// Complexes functions that cannot be chained.
// It is the ancestors of the terminal object, so it should inherit from async.EventEmitter.
var notChainable = Object.create( Terminal.prototype ) ;



// Complexes high-level features have their own file
notChainable.yesOrNo = require( './yesOrNo.js' ) ;
notChainable.inputField = require( './inputField.js' ) ;
notChainable.fileInput = require( './fileInput.js' ) ;
notChainable.singleRowMenu = notChainable.singleLineMenu = require( './singleLineMenu.js' ) ;
notChainable.singleColumnMenu = require( './singleColumnMenu.js' ) ;
notChainable.gridMenu = require( './gridMenu.js' ) ;
notChainable.progressBar = require( './progressBar.js' ) ;
notChainable.bar = require( './bar.js' ) ;
notChainable.slowTyping = require( './slowTyping.js' ) ;



notChainable.createDocument = function( options ) {
	if ( ! options || typeof options !== 'object' ) { options = {} ; }
	options.outputDst = this ;
	options.eventSource = this ;
	return new termkit.Document( options ) ;
} ;



notChainable.createInlineElement = function( Type , options ) {
	return termkit.Element.createInline( this , Type , options ) ;
} ;



notChainable.table = function( table , options = {} ) {
	return termkit.Element.createInline( this , termkit.TextTable ,
		Object.assign( {} , options , {
			cellContents: table ,
			fit: options.fit !== undefined ? !! options.fit : true
		} )
	) ;
} ;



notChainable.spinner = function( options = {} ) {
	if ( typeof options === 'string' ) { options = { animation: options } ; }
	return termkit.Element.createInline( this , termkit.AnimatedText , options ) ;
} ;



/*
	.wrapColumn()
	.wrapColumn( width )
	.wrapColumn( x , width )
	.wrapColumn( namedParameters )

	width: the width for word-wrapping, null for terminal width
	x: x position (for column wrapping)
	continue: true if we are continuing
	offset: offset of the first line (default: 0)
*/
notChainable.wrapColumn = function( ... args ) {
	this.wrapOptions.continue = false ;
	this.wrapOptions.offset = 0 ;

	if ( ! args.length ) { return ; }

	if ( args[ 0 ] && typeof args[ 0 ] === 'object' ) {
		Object.assign( this.wrapOptions , args[ 0 ] ) ;
		return this.wrap ;
	}

	if ( args.length === 1 ) {
		this.wrapOptions.x = 1 ;
		this.wrapOptions.width = args[ 0 ] ;
		return this.wrap ;
	}

	this.wrapOptions.x = args[ 0 ] ;
	this.wrapOptions.width = args[ 1 ] ;
	return this.wrap ;
} ;



// Fail-safe alternate screen buffer
notChainable.fullscreen = function( options ) {
	if ( options === false ) {
		if ( ! this.state.fullscreen ) { return this ; }

		// Disable fullscreen mode
		this.state.fullscreen = false ;
		this.moveTo( 1 , this.height , '\n' ) ;
		this.alternateScreenBuffer( false ) ;
		return this ;
	}

	if ( ! options ) { options = {} ; }

	this.state.fullscreen = true ;
	if ( ! options.noAlternate ) { this.alternateScreenBuffer( true ) ; }
	this.clear() ;
} ;





/* Input management */



function onStdin( chunk ) {
	var i , j , buffer , startBuffer , char , codepoint ,
		keymapCode , keymapStartCode , keymap , keymapList ,
		regexp , matches , bytes , found , handlerResult ,
		accumulate = false ,
		index = 0 , length = chunk.length ;

	if ( this.shutdown ) { return ; }

	if ( this.prependStdinChunk ) {
		chunk = Buffer.concat( [ this.prependStdinChunk , chunk ] ) ;
	}

	while ( index < length ) {
		found = false ;
		bytes = 1 ;

		if ( chunk[ index ] <= 0x1f || chunk[ index ] === 0x7f ) {
			// Those are ASCII control character and DEL key

			for ( i = Math.min( length , Math.max( this.rKeymapMaxSize , this.rKeymapStarterMaxSize ) ) ; i > 0 ; i -- ) {
				buffer = chunk.slice( index ) ;
				keymapCode = buffer.toString() ;
				startBuffer = chunk.slice( index , index + i ) ;
				keymapStartCode = startBuffer.toString() ;


				if ( this.rKeymap[ i ] && this.rKeymap[ i ][ keymapStartCode ] ) {
					// First test fixed sequences

					keymap = this.rKeymap[ i ][ keymapStartCode ] ;
					found = true ;

					if ( keymap.handler ) {
						handlerResult = keymap.handler.call( this , keymap.name , chunk.slice( index + i ) ) ;
						bytes = i + handlerResult.eaten ;

						if ( ! handlerResult.disable ) {
							this.emit( keymap.event , handlerResult.name , handlerResult.data ) ;
						}
					}
					else if ( keymap.event ) {
						bytes = i ;
						this.emit( keymap.event , keymap.name , keymap.data , { code: startBuffer } ) ;
					}
					else {
						bytes = i ;
						this.emitKey( keymap.name , keymap.matches , { isCharacter: false , code: startBuffer } ) ;
					}

					break ;
				}
				else if ( this.rKeymapStarter[ i ] && this.rKeymapStarter[ i ][ keymapStartCode ] ) {
					// Then test pattern sequences

					keymapList = this.rKeymapStarter[ i ][ keymapStartCode ] ;

					//console.log( 'for i:' , keymapList ) ;

					for ( j = 0 ; j < keymapList.length ; j ++ ) {
						keymap = keymapList[ j ] ;

						if ( keymap.altEnder ) {
							regexp = '^' +
								string.escape.regExp( keymap.starter ) +
								'([ -~]*?)' +	// [ -~] match only all ASCII non-control character
								'(' + string.escape.regExp( keymap.ender ) + '|' + string.escape.regExp( keymap.altEnder ) + ')' ;
						}
						else {
							regexp = '^' +
								string.escape.regExp( keymap.starter ) +
								'([ -~]*?)' +	// [ -~] match only all ASCII non-control character
								string.escape.regExp( keymap.ender ) ;
						}

						matches = keymapCode.match( new RegExp( regexp ) , 'g' ) ;

						//console.log( 'for j:' , keymap , regexp , matches ) ;

						if ( matches ) {
							found = true ;

							handlerResult = keymap.handler.call( this , keymap.name , matches[ 1 ] ) ;
							bytes = matches[ 0 ].length ;
							this.emit( keymap.event , handlerResult.name , handlerResult.data ) ;

							break ;
						}
						else if ( keymap.accumulate ) {
							found = true ;
							accumulate = true ;
							break ;
						}
					}

					if ( found ) { break ; }
				}
			}

			// Nothing was found, so to not emit trash, we just abort the current buffer processing
			if ( ! found ) { this.emit( 'unknown' , chunk ) ; break ; }
		}
		else if ( chunk[ index ] >= 0x80 ) {
			// Unicode bytes per char guessing
			if ( chunk[ index ] < 0xc0 ) { continue ; }	// We are in a middle of an unicode multibyte sequence... Something fails somewhere, we will just continue for now...
			else if ( chunk[ index ] < 0xe0 ) { bytes = 2 ; }
			else if ( chunk[ index ] < 0xf0 ) { bytes = 3 ; }
			else if ( chunk[ index ] < 0xf8 ) { bytes = 4 ; }
			else if ( chunk[ index ] < 0xfc ) { bytes = 5 ; }
			else { bytes = 6 ; }

			buffer = chunk.slice( index , index + bytes ) ;
			char = buffer.toString( 'utf8' ) ;

			//if ( bytes > 2 ) { codepoint = punycode.ucs2.decode( char )[ 0 ] ; }
			if ( bytes > 2 ) { codepoint = string.unicode.firstCodePoint( char ) ; }
			else { codepoint = char.charCodeAt( 0 ) ; }

			this.emitKey( char , [ char ] , { isCharacter: true , codepoint: codepoint , code: buffer } ) ;
		}
		else {
			// Standard ASCII
			char = String.fromCharCode( chunk[ index ] ) ;
			this.emitKey( char , [ char ] , { isCharacter: true , codepoint: chunk[ index ] , code: chunk[ index ] } ) ;
		}

		index += bytes ;
	}

	if ( accumulate ) {
		this.prependStdinChunk = chunk ;
	}
	else {
		this.prependStdinChunk = null ;
	}
}



// Internal?
notChainable.emitKey = function( name , matches , data ) {
	if ( this.metaKeyPrefix ) {
		data.meta = this.metaKeyPrefix ;
		data.isCharacter = false ;

		if ( this.metaKeyRemove ) {
			let regexp = new RegExp( '(^|_)' + this.metaKeyRemove + '_' ) ;

			// We force using a match that contains the key to remove,
			// e.g.: META + RETURN/CTRL_M would produce META_M instead of META_RETURN
			if ( matches.length > 1 ) {
				let index = matches.findIndex( oneMatch => oneMatch.match( regexp ) ) ;
				regexp.lastIndex = 0 ;
				if ( index >= 0 ) { name = matches[ index ] ; }
			}

			name = name.replace( regexp , '$1' ) ;
		}

		name = this.metaKeyPrefix + '_' + termkit.characterName( name ) ;

		this.metaKeyPrefix = null ;
		this.metaKeyRemove = null ;
	}

	this.emit( 'key' , name , matches , data ) ;
} ;



// TODOC
notChainable.setMetaKeyPrefix = function( prefix , remove ) {
	this.metaKeyPrefix = prefix && typeof prefix === 'string' ? prefix : null ;
	this.metaKeyRemove = remove && typeof remove === 'string' ? remove : null ;
} ;



/*
	* options `false` or `Object` where:
		* mouse `string` one of:
			* 'button'
			* 'drag'
			* 'motion'
		* focus `boolean`
	* safe `boolean` (optional), when set and when *options* is set to `false`, it turns *.grabInput()*
	  it returns a promise that resolve when input grabbing is safely turned off, avoiding extra junks to
	  be echoed when the terminal left the raw mode. It is mostly useful after grabbing mouse motion.
*/
notChainable.grabInput = function( options , safe ) {
	// RESET
	this.mouseButton( false ) ;
	this.mouseDrag( false ) ;
	this.mouseMotion( false ) ;
	//this.mouseSGR( false ) ;
	this.focusEvent( false ) ;
	this.stdin.removeListener( 'data' , this.onStdin ) ;

	// Start disabling everything
	this.grabbing = false ;
	this.mouseGrabbing = false ;
	this.focusGrabbing = false ;

	// Disable grabInput mode
	var disable = () => {
		// Very important: removing all listeners doesn't switch back to pause mode.
		// This is some nasty Node.js quirks (the documentation pleads for backward compatibility).
		this.stdin.pause() ;

		try {
			this.stdin.setRawMode( false ) ;
		}
		catch ( error ) {
			// That's not critical in any way and thus can be ignored: we are probably reading from a non-TTY
		}
	} ;

	if ( options === false ) {
		if ( safe ) {
			return Promise.resolveSafeTimeout( this.timeout / 2 ).then( disable ) ;
		}

		disable() ;
		return Promise.resolved ;
	}

	// Should not be moved before, because shutdown typically needs .grabInput( false )
	if ( this.shutdown ) { return Promise.resolved ; }

	this.grabbing = true ;

	if ( ! options ) { options = {} ; }

	// SET
	try {
		this.stdin.setRawMode( true ) ;
	}
	catch ( error ) {
		// Same here, that's not critical in any way and thus can be ignored: we are probably reading from a non-TTY
	}

	this.stdin.on( 'data' , this.onStdin ) ;

	// Very important: after the first this.stdin.pause(), listening for data seems to not switch back to flowing mode.
	// Again, a nasty Node.js quirk.
	this.stdin.resume() ;

	if ( options.mouse ) {
		this.mouseGrabbing = true ;

		switch ( options.mouse ) {
			case 'button' : this.mouseButton.mouseSGR() ; break ;
			case 'drag' : this.mouseDrag.mouseSGR() ; break ;
			case 'motion' : this.mouseMotion.mouseSGR() ; break ;
		}
	}

	if ( options.focus ) {
		this.focusEvent() ;
		this.focusGrabbing = true ;
	}

	return Promise.resolved ;
} ;



// Like process.exit(), but perform cleanup of the terminal first.
// It is asynchronous, so it should be followed by a 'return' if needed.
// A better way to handle that is to use Promise.asyncExit(), that is detected by the Terminal instance.
notChainable.processExit = function( code ) {
	this( '\n' ) ;
	this.asyncCleanup().then( () => process.exit( code ) ) ;
} ;



notChainable.asyncCleanup = async function() {
	if ( this.shutdown ) { return ; }
	this.shutdown = true ;

	this.styleReset() ;
	this.hideCursor( false ) ;

	var wasGrabbing = this.grabbing ;

	await this.waitStreamDone( this.stdout ) ;

	if ( ! this.isTTY || ! wasGrabbing ) { return ; }

	await Promise.resolveSafeTimeout( this.timeout / 4 ) ;
	return this.grabInput( false , true ) ;
} ;



notChainable.waitStreamDone = function( stream ) {
	if ( ! stream._writableState.needDrain ) { return Promise.resolved ; }
	return Promise.onceEvent( stream , 'drain' ) ;
} ;



notChainable.object2attr = function( object ) {
	var attr = this.esc.styleReset.on ;

	if ( ! object || typeof object !== 'object' ) { object = {} ; }

	// Color part
	if ( typeof object.color === 'string' ) { object.color = termkit.colorNameToIndex( object.color ) ; }
	if ( typeof object.color !== 'number' || object.color < 0 || object.color > 255 ) { object.color = 7 ; }
	else { object.color = Math.floor( object.color ) ; }

	attr += this.str.color( object.color ) ;

	// Background color part
	if ( typeof object.bgColor === 'string' ) { object.bgColor = termkit.colorNameToIndex( object.bgColor ) ; }
	if ( typeof object.bgColor !== 'number' || object.bgColor < 0 || object.bgColor > 255 ) { object.bgColor = 0 ; }
	else { object.bgColor = Math.floor( object.bgColor ) ; }

	attr += this.str.bgColor( object.bgColor ) ;

	// Style part
	if ( object.bold ) { attr += this.esc.bold.on ; }
	if ( object.dim ) { attr += this.esc.dim.on ; }
	if ( object.italic ) { attr += this.esc.italic.on ; }
	if ( object.underline ) { attr += this.esc.underline.on ; }
	if ( object.blink ) { attr += this.esc.blink.on ; }
	if ( object.inverse ) { attr += this.esc.inverse.on ; }
	if ( object.hidden ) { attr += this.esc.hidden.on ; }
	if ( object.strike ) { attr += this.esc.strike.on ; }

	return attr ;
} ;



// Erase a whole rectangular area
// .eraseArea( x , y , [width] , [height] )
notChainable.eraseArea = function( xMin , yMin , width = 1 , height = 1 ) {
	xMin = Math.min( xMin , this.width ) ;
	yMin = Math.min( yMin , this.height ) ;

	var y ,
		xMax = Math.min( xMin + width , this.width + 1 ) ,
		yMax = Math.min( yMin + height , this.height + 1 ) ,
		str = ' '.repeat( xMax - xMin ) ;

	for ( y = yMin ; y < yMax ; y ++ ) {
		this.moveTo( xMin , y , str ) ;
	}
} ;



// A facility for those who don't want to deal with requestCursorLocation() and events...
notChainable.getCursorLocation = function( callback ) {
	var wasGrabbing = this.grabbing , alreadyCleanedUp = false , error ;

	if ( this.shutdown ) { return Promise.resolved ; }

	// First, check capabilities:
	if ( this.esc.requestCursorLocation.na ) {
		error = new Error( 'Terminal is not capable' ) ;
		if ( callback ) {
			callback( error ) ;
			return Promise.resolved ;
		}

		return Promise.reject( error ) ;
	}

	var promise = new Promise() ;

	// Now .getCursorLocation() cannot run in concurrency anymore
	if ( this.lock.getCursorLocation ) {
		this.once( 'unlock_getCursorLocation' , () => {
			this.getCursorLocation().then(
				data => {
					if ( callback ) { callback( undefined , data.x , data.y ) ; }
					else { promise.resolve( data ) ; }
				} ,
				error_ => {
					if ( callback ) { callback( error_ ) ; }
					else { promise.reject( error_ ) ; }
				}
			) ;
		} ) ;

		return promise ;
	}

	this.lock.getCursorLocation = true ;

	var cleanup = ( error_ , x , y ) => {
		if ( alreadyCleanedUp ) { return ; }
		alreadyCleanedUp = true ;

		this.removeListener( 'terminal' , onTerminal ) ;
		if ( ! wasGrabbing ) { this.grabInput( false ) ; }

		if ( error_ ) {
			if ( this.shutdown ) { error_.code = 'shutdown' ; }

			if ( callback ) { callback( error_ ) ; }
			else { promise.reject( error_ ) ; }
			return ;
		}

		if ( callback ) { callback( undefined , x , y ) ; }
		else { promise.resolve( { x , y } ) ; }
	} ;

	var onTerminal = ( name , data ) => {
		if ( name !== 'CURSOR_LOCATION' ) { return ; }
		this.lock.getCursorLocation = false ;
		this.emit( 'unlock_getCursorLocation' ) ;
		cleanup( undefined , data.x , data.y ) ;
	} ;

	if ( ! wasGrabbing ) { this.grabInput() ; }

	this.on( 'terminal' , onTerminal ) ;
	this.requestCursorLocation() ;

	Promise.resolveSafeTimeout( this.timeout ).then( () => {
		if ( alreadyCleanedUp ) { return ; }
		var error_ = new Error( '.getCursorLocation() timed out' ) ;
		error_.code = 'timeout' ;
		cleanup( error_ ) ;
	} ) ;

	return promise ;
} ;



// Get the RGB value for a color register
notChainable.getColor = function( register , callback ) {
	var wasGrabbing = this.grabbing , alreadyCleanedUp = false , error ;

	if ( this.shutdown ) { return Promise.resolved ; }

	// First, check capabilities:
	if ( this.esc.requestColor.na ) {
		error = new Error( 'Terminal is not capable' ) ;

		if ( callback ) {
			callback( error ) ;
			return Promise.resolved ;
		}

		return Promise.reject( error ) ;
	}

	var promise = new Promise() ;

	var cleanup = ( error_ , data ) => {
		if ( alreadyCleanedUp ) { return ; }
		alreadyCleanedUp = true ;

		this.removeListener( 'terminal' , onTerminal ) ;
		if ( ! wasGrabbing ) { this.grabInput( false ) ; }

		if ( error_ ) {
			if ( this.shutdown ) { error_.code = 'shutdown' ; }

			if ( callback ) { callback( error_ ) ; }
			else { promise.reject( error_ ) ; }
			return ;
		}

		if ( callback ) { callback( undefined , data ) ; }
		else { promise.resolve( data ) ; }
	} ;

	var onTerminal = ( name , data ) => {

		if ( name !== 'COLOR_REGISTER' ) { return ; }

		// We have got a color definition, but this is not for our register, so this is not our response
		if ( data.register !== register ) { return ; }

		// This is a good opportunity to update the color register
		if ( register < 16 ) { this.colorRegister[ register ] = { r: data.r , g: data.g , b: data.b } ; }

		// Everything is fine...
		cleanup( undefined , data ) ;
	} ;

	if ( ! wasGrabbing ) { this.grabInput() ; }

	this.requestColor( register ) ;
	this.on( 'terminal' , onTerminal ) ;

	Promise.resolveSafeTimeout( this.timeout ).then( () => {
		if ( alreadyCleanedUp ) { return ; }
		var error_ = new Error( '.getColor() timed out' ) ;
		error_.code = 'timeout' ;
		cleanup( error_ ) ;
	} ) ;

	return promise ;
} ;



// Get the current 16 colors palette of the terminal, if possible
notChainable.getPalette = function( callback ) {
	var defaultPalette ,
		wasGrabbing = this.grabbing ;

	if ( this.shutdown ) { return Promise.resolved ; }

	if ( ! wasGrabbing ) { this.grabInput() ; }

	// First, check capabilities, if not capable, return the default palette
	if ( this.esc.requestColor.na ) {
		defaultPalette = this.colorRegister.slice( 0 , 16 ) ;

		if ( callback ) {
			callback( undefined , defaultPalette ) ;
			return Promise.resolved ;
		}

		return Promise.resolve( defaultPalette ) ;
	}

	return Promise.concurrent(
		4 ,
		[ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ] ,
		register => this.getColor( register )
	)
		.then(
			palette => {
				if ( ! wasGrabbing ) { this.grabInput( false ) ; }
				if ( callback ) { callback( undefined , palette ) ; }
				else { return palette ; }
			} ,
			error => {
				if ( ! wasGrabbing ) { this.grabInput( false ) ; }

				if ( callback ) { callback( error ) ; }
				else { throw error ; }	// re-throw if not using callback
			}
		) ;
} ;



// Set the color for a register
notChainable.setColor = function( register , r , g , b , names ) {
	if ( r && typeof r === 'object' ) {
		b = r.b ;
		g = r.g ;
		r = r.r ;
		names = g ;
	}

	// Allow modification of register > 15 ? It looks like terminals don't allow it... (at least not gnome)
	if ( typeof register !== 'number' || register < 0 || register > 15 ) { throw new Error( 'Bad register value' ) ; }

	if ( ! Array.isArray( names ) ) { names = [] ; }

	if (
		typeof r !== 'number' || r < 0 || r > 255 ||
		typeof g !== 'number' || g < 0 || g > 255 ||
		typeof b !== 'number' || b < 0 || b > 255
	) {
		throw new Error( 'Bad RGB value' ) ;
	}

	// Issue an error, or not?
	if ( this.setColorLL.na ) { return ; }

	// This is a good opportunity to update the color register
	this.colorRegister[ register ] = {
		r: r , g: g , b: b , names: names
	} ;

	// Call the Low Level set color
	this.setColorLL( register , r , g , b ) ;
} ;



// Set the current 16 colors palette of the terminal, if possible
notChainable.setPalette = function( palette ) {
	var i ;

	if ( typeof palette === 'string' ) {
		try {
			palette = require( './colorScheme/' + palette + '.json' ) ;
		}
		catch( error ) {
			throw new Error( '[terminal] .setPalette(): color scheme not found: ' + palette ) ;
		}
	}

	if ( ! Array.isArray( palette ) ) { throw new Error( '[terminal] .setPalette(): argument #0 should be an Array of RGB Object or a built-in color scheme' ) ; }

	// Issue an error, or not?
	if ( this.setColorLL.na ) { return ; }

	for ( i = 0 ; i <= 15 ; i ++ ) {
		if ( ! palette[ i ] || typeof palette[ i ] !== 'object' ) { continue ; }
		this.setColor( i , palette[ i ] ) ;
	}
} ;



notChainable.getClipboard = function( source = 'c' ) {
	var wasGrabbing = this.grabbing , alreadyCleanedUp = false , extClipboard ;

	if ( this.shutdown ) { return Promise.resolved ; }

	// First, check capabilities:
	if ( this.esc.requestClipboard.na ) {
		extClipboard = require( './extClipboard.js' ) ;
		return extClipboard.getClipboard( source ).catch( () => '' ) ;
	}

	var promise = new Promise() ;

	var cleanup = ( error_ , data ) => {
		if ( alreadyCleanedUp ) { return ; }
		alreadyCleanedUp = true ;

		this.removeListener( 'terminal' , onTerminal ) ;
		if ( ! wasGrabbing ) { this.grabInput( false ) ; }

		if ( error_ ) {
			if ( this.shutdown ) { error_.code = 'shutdown' ; }
			promise.reject( error_ ) ;
			return ;
		}

		promise.resolve( data ) ;
	} ;

	var onTerminal = ( name , data ) => {
		//console.log( "EVENT: " , name , data ) ;
		if ( name !== 'CLIPBOARD' ) { return ; }

		// We have got some content, but this is not for our source, so this is not our response
		//if ( data.source !== source ) { return ; }

		// Everything is fine...
		cleanup( undefined , data.content ) ;
	} ;

	if ( ! wasGrabbing ) { this.grabInput() ; }

	this.requestClipboard( source[ 0 ] ) ;
	this.on( 'terminal' , onTerminal ) ;

	Promise.resolveSafeTimeout( this.timeout ).then( () => {
		if ( alreadyCleanedUp ) { return ; }
		var error_ = new Error( '.getClipboard() timed out' ) ;
		error_.code = 'timeout' ;
		cleanup( error_ ) ;
	} ) ;

	return promise ;
} ;



// Set the color for a register
notChainable.setClipboard = async function( str , source = 'c' ) {
	var extClipboard ;

	if ( this.esc.setClipboardLL.na ) {
		extClipboard = require( './extClipboard.js' ) ;
		return extClipboard.setClipboard( str , source ).catch( () => undefined ) ;
	}

	var base64 = Buffer.from( str ).toString( 'base64' ) ;

	//console.log( "base64:" , base64 , "retro:" , Buffer.from( base64 , 'base64' ).toString() ) ;

	// Call the Low Level set clipboard
	this.setClipboardLL( source[ 0 ] , base64 ) ;
	return Promise.resolved ;
} ;



// A facility for those who don't want to deal with requestCursorLocation() and events...
notChainable.getTerminfo = function( key ) {
	var wasGrabbing = this.grabbing , alreadyCleanedUp = false ;

	if ( this.shutdown ) { return Promise.resolved ; }

	// First, check capabilities:
	if ( this.esc.xtgettcapLL.na ) {
		return Promise.reject( new Error( 'Terminal is not capable' ) ) ;
	}

	var promise = new Promise() ;

	// Avoid concurrency
	if ( this.lock.getTerminfo ) {
		this.once( 'unlock_getTerminfo' , () => this.getTerminfo( key ).propagate( promise ) ) ;
		return promise ;
	}

	this.lock.getTerminfo = true ;

	var cleanup = ( error , data ) => {
		if ( alreadyCleanedUp ) { return ; }
		alreadyCleanedUp = true ;

		this.removeListener( 'terminal' , onTerminal ) ;
		if ( ! wasGrabbing ) { this.grabInput( false ) ; }

		if ( error ) {
			if ( this.shutdown ) { error.code = 'shutdown' ; }
			promise.reject( error ) ;
			return ;
		}

		if ( ! data.valid || data.key !== key ) {
			promise.resolve() ;
			return ;
		}

		promise.resolve( data.value ) ;
	} ;

	var onTerminal = ( name , data ) => {
		if ( name !== 'TERMINFO' ) { return ; }
		this.lock.getTerminfo = false ;
		this.emit( 'unlock_getTerminfo' ) ;
		cleanup( undefined , data ) ;
	} ;

	if ( ! wasGrabbing ) { this.grabInput() ; }

	this.on( 'terminal' , onTerminal ) ;
	//this.xtgettcapLL( keys.map( key => Buffer.from( key ).toString( 'hex' ) ).join( ';' ) ) ;
	this.xtgettcapLL( Buffer.from( key ).toString( 'hex' ) ) ;

	Promise.resolveSafeTimeout( this.timeout ).then( () => {
		if ( alreadyCleanedUp ) { return ; }
		var error = new Error( '.getTerminfo() timed out' ) ;
		error.code = 'timeout' ;
		cleanup( error ) ;
	} ) ;

	return promise ;
} ;





/* Utilities */



// Default colors, used for guessing
var defaultColorRegister = require( './colorScheme/default.json' ) ;

( function buildDefaultColorRegister() {
	var register , offset , factor , l ;

	for ( register = 16 ; register < 232 ; register ++ ) {
		// RGB 6x6x6
		offset = register - 16 ;
		factor = 255 / 5 ;
		defaultColorRegister[ register ] = {
			r: Math.round( ( Math.floor( offset / 36 ) % 6 ) * factor ) ,
			g: Math.round( ( Math.floor( offset / 6 ) % 6 ) * factor ) ,
			b: Math.round( ( offset % 6 ) * factor ) ,
			names: []
		} ;
	}

	for ( register = 232 ; register <= 255 ; register ++ ) {
		// Grayscale 0..23
		offset = register - 231 ;	// not 232, because the first of them is not a #000000 black
		factor = 255 / 25 ;	// not 23, because the last is not a #ffffff white
		l = Math.round( offset * factor ) ;
		defaultColorRegister[ register ] = {
			r: l , g: l , b: l , names: []
		} ;
	}
} )() ;



// If register hasn't changed, this is used to get the RGB value for them
notChainable.rgbForRegister = function( register ) {
	if ( register < 0 || register > 255 ) { throw new Error( 'Bad register value' ) ; }

	// Simply clone it
	return {
		r: this.colorRegister[ register ].r ,
		g: this.colorRegister[ register ].g ,
		b: this.colorRegister[ register ].b
	} ;
} ;



// If register hasn't changed, this is used to get it for an RGB
// .registerForRgb( r , g , b , [minRegister] , [maxRegister] )
// .registerForRgb( rgbObject , [minRegister] , [maxRegister] )

// Lab HCL cylinder coordinate distance
notChainable.registerForRgb = function( r , g , b , minRegister , maxRegister ) {
	// Manage function arguments
	if ( r && typeof r === 'object' ) {
		// Manage the .registerForRgb( rgbObject , [minRegister] , [maxRegister] ) variante
		maxRegister = b ;
		minRegister = g ;
		b = r.b ;
		g = r.g ;
		r = r.r ;
	}

	if (
		typeof r !== 'number' || r < 0 || r > 255 ||
		typeof g !== 'number' || g < 0 || g > 255 ||
		typeof b !== 'number' || b < 0 || b > 255
	) {
		throw new Error( 'Bad RGB value' ) ;
	}

	if ( typeof maxRegister !== 'number' || maxRegister < 0 || maxRegister > 255 ) { maxRegister = 15 ; }
	if ( typeof minRegister !== 'number' || minRegister < 0 || minRegister > 255 ) { minRegister = 0 ; }

	if ( minRegister > maxRegister ) {
		var tmp ;
		tmp = maxRegister ;
		maxRegister = minRegister ;
		minRegister = tmp ;
	}

	return this._registerForRgb( r , g , b , minRegister , maxRegister ) ;
} ;



notChainable._registerForRgb = function( r , g , b , minRegister , maxRegister ) {
	// Search for the best match
	var register , delta ,
		minDelta = Infinity ,
		rgb = [ r , g , b ]  ;

	for ( register = minRegister ; register <= maxRegister ; register ++ ) {
		delta = termkit.chroma.distance( rgb , this.colorRegister[ register ] , 'hcl' ) ;
		if ( delta < minDelta ) {
			minDelta = delta ;
			minRegister = register ;
		}
	}

	return minRegister ;
} ;



notChainable.colorNameForRgb = function( r , g , b ) {
	return termkit.indexToColorName( this.registerForRgb( r , g , b , 0 , 15 ) ) ;
} ;



notChainable.colorNameForHex = function( hex ) {
	var rgba = termkit.hexToRgba( hex ) ;
	return this.colorNameForRgb( rgba.r , rgba.g , rgba.b ) ;
} ;



notChainable.registerForRgbCache = function( cache , r , g , b , minRegister , maxRegister ) {
	var key = r + '-' + g + '-' + b ;
	if ( cache[ key ] ) { return cache[ key ] ; }
	return ( cache[ key ] = this._registerForRgb( r , g , b , minRegister , maxRegister ) ) ;
} ;





/* ScreenBuffer compatible methods */



// Cursor is always drawn so there is nothing to do here
notChainable.drawCursor = function() {} ;

// /!\ Missing: markup, attr return
notChainable.put = function( options , str , ... args ) {
	var i , x , y , dx , dy , attr , wrap , characters , len , moveToNeeded , inline ;

	// Manage options
	if ( ! options ) { options = {} ; }

	wrap = options.wrap === undefined ? true : options.wrap ;

	x = options.x || 0 ;
	y = options.y || 0 ;

	if ( typeof x !== 'number' || x < 1 ) { x = 1 ; }
	else if ( x > this.width ) { x = this.width ; }
	else { x = Math.floor( x ) ; }

	if ( typeof y !== 'number' || y < 1 ) { y = 1 ; }
	else if ( y > this.height ) { y = this.height ; }
	else { y = Math.floor( y ) ; }


	// Process directions/increments
	dx = 1 ;
	dy = 0 ;

	switch ( options.direction ) {
		//case 'right' : // not needed, use the default dx & dy
		case 'left' :
			dx = -1 ;
			break ;
		case 'up' :
			dx = 0 ;
			dy = -1 ;
			break ;
		case 'down' :
			dx = 0 ;
			dy = 1 ;
			break ;
		case null :
		case 'none' :
			dx = 0 ;
			dy = 0 ;
			break ;
	}

	if ( typeof options.dx === 'number' ) { dx = options.dx ; }
	if ( typeof options.dy === 'number' ) { dy = options.dy ; }

	inline = ( dx === 1 && dy === 0 ) ;


	// Process attributes
	attr = options.attr || this.esc.styleReset.on ;

	if ( attr && typeof attr === 'object' ) { attr = this.object2attr( attr ) ; }
	if ( typeof attr !== 'string' ) { attr = this.esc.styleReset.on ; }


	// Process the input string
	if ( typeof str !== 'string' ) {
		if ( str.toString ) { str = str.toString() ; }
		else { return ; }
	}

	if ( args.length ) { str = string.format( str , ... args ) ; }
	str = termkit.stripControlChars( str ) ;

	//characters = punycode.ucs2.decode( str ) ;
	characters = string.unicode.toArray( str ) ;
	len = characters.length ;

	moveToNeeded = true ;
	this.stdout.write( attr ) ;

	for ( i = 0 ; i < len ; i ++ ) {
		if ( moveToNeeded ) { this.moveTo( x , y ) ; }
		this( characters[ i ] ) ;

		x += dx ;
		y += dy ;

		moveToNeeded = ! inline ;

		if ( x < 0 ) {
			if ( ! wrap ) { break ; }
			x = this.width - 1 ;
			y -- ;
			moveToNeeded = true ;
		}
		else if ( x >= this.width ) {
			if ( ! wrap ) { break ; }
			x = 0 ;
			y ++ ;
			moveToNeeded = true ;
		}

		if ( y < 0 ) { break ; }
		else if ( y >= this.height ) { break ; }
	}
} ;



notChainable.drawNdarrayImage = function( pixels /* , options */ ) {
	var x , xMax = Math.min( pixels.shape[ 0 ] , this.width ) ,
		y , yMax = Math.ceil( pixels.shape[ 1 ] / 2 ) ,
		hasAlpha = pixels.shape[ 2 ] === 4 ,
		maxRegister = this.support['256colors'] ? 255 : 15 ,
		fgColor , bgColor , fgAlpha , bgAlpha , cache = {} ;


	for ( y = 0 ; y < yMax ; y ++ ) {
		for ( x = 0 ; x < xMax ; x ++ ) {
			if ( this.support.trueColor ) {
				fgAlpha = hasAlpha ? pixels.get( x , y * 2 , 3 ) / 255 : 1 ;

				if ( y * 2 + 1 < pixels.shape[ 1 ] ) {
					bgAlpha = hasAlpha ? pixels.get( x , y * 2 + 1 , 3 ) / 255 : 1 ;

					this.noFormat(
						this.optimized.color24bits(
							Math.round( fgAlpha * pixels.get( x , y * 2 , 0 ) ) ,
							Math.round( fgAlpha * pixels.get( x , y * 2 , 1 ) ) ,
							Math.round( fgAlpha * pixels.get( x , y * 2 , 2 ) )
						) +
						this.optimized.bgColor24bits(
							Math.round( bgAlpha * pixels.get( x , y * 2 + 1 , 0 ) ) ,
							Math.round( bgAlpha * pixels.get( x , y * 2 + 1 , 1 ) ) ,
							Math.round( bgAlpha * pixels.get( x , y * 2 + 1 , 2 ) )
						) +
						'▀'
					) ;
				}
				else {
					this.noFormat(
						this.optimized.color24bits(
							Math.round( fgAlpha * pixels.get( x , y * 2 , 0 ) ) ,
							Math.round( fgAlpha * pixels.get( x , y * 2 , 1 ) ) ,
							Math.round( fgAlpha * pixels.get( x , y * 2 , 2 ) )
						) +
						this.optimized.bgColor24bits( 0 , 0 , 0 ) +
						'▀'
					) ;
				}
			}
			else {
				fgColor = hasAlpha && pixels.get( x , y * 2 , 3 ) < 127 ?
					0 :
					this.registerForRgbCache(
						cache ,
						pixels.get( x , y * 2 , 0 ) ,
						pixels.get( x , y * 2 , 1 ) ,
						pixels.get( x , y * 2 , 2 ) ,
						0 , maxRegister
					) ;

				if ( y * 2 + 1 < pixels.shape[ 1 ] ) {
					bgColor = hasAlpha && pixels.get( x , y * 2 + 1 , 3 ) < 127 ?
						0 :
						this.registerForRgbCache(
							cache ,
							pixels.get( x , y * 2 + 1 , 0 ) ,
							pixels.get( x , y * 2 + 1 , 1 ) ,
							pixels.get( x , y * 2 + 1 , 2 ) ,
							0 , maxRegister
						) ;

					this.noFormat( this.optimized.color256[ fgColor ] + this.optimized.bgColor256[ bgColor ] + '▀' ) ;
				}
				else {
					this.noFormat( this.optimized.color256[ fgColor ] + this.optimized.bgColor256[ 0 ] + '▀' ) ;
				}
			}
		}

		this.styleReset()( '\n' ) ;
	}
} ;



notChainable.drawImage = function( filepath , options , callback ) {
	return termkit.image.load.call( this , notChainable.drawNdarrayImage.bind( this ) , filepath , options , callback ) ;
} ;


}).call(this)}).call(this,require('_process'),require("buffer").Buffer)
},{"./bar.js":8,"./colorScheme/default.json":10,"./extClipboard.js":42,"./fileInput.js":44,"./gridMenu.js":45,"./inputField.js":47,"./progressBar.js":50,"./singleColumnMenu.js":51,"./singleLineMenu.js":52,"./slowTyping.js":53,"./termkit.js":56,"./yesOrNo.js":63,"_process":191,"buffer":159,"nextgen-events":78,"seventh":114,"string-kit":133,"tree-kit":145}],6:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const misc = require( './misc.js' ) ;

const fs = require( 'fs' ) ;
const string = require( 'string-kit' ) ;



// A buffer suitable for text editor



function TextBuffer( options = {} ) {
	this.ScreenBuffer = options.ScreenBuffer || ( options.dst && options.dst.constructor ) || termkit.ScreenBuffer ;

	// a screenBuffer
	this.dst = options.dst ;
	this.palette = options.palette || ( this.dst && this.dst.palette ) ;

	// virtually infinity by default
	this.width = options.width || Infinity ;	// not used except by the blitter
	this.height = options.height || Infinity ;	// not used except by the blitter
	this.dstClipRect = options.dstClipRect ? new termkit.Rect( options.dstClipRect ) : null ;

	this.x = options.x || 0 ;
	this.y = options.y || 0 ;

	this.firstLineRightShift = options.firstLineRightShift || 0 ;

	this.cx = 0 ;
	this.cy = 0 ;
	this.ch = false ;	// cursor hidden

	this.voidTextBuffer = null ;	// Another TextBuffer used as fallback for empty cells, usefull for placeholder/hint/etc

	this.defaultAttr = this.ScreenBuffer.prototype.DEFAULT_ATTR ;
	this.voidAttr = this.ScreenBuffer.prototype.DEFAULT_ATTR ;
	this.preserveMarkupFormat = this.ScreenBuffer.prototype.preserveMarkupFormat ;
	this.markupToAttrObject = this.ScreenBuffer.prototype.markupToAttrObject ;

	this.hidden = false ;

	this.tabWidth = + options.tabWidth || 4 ;
	this.forceInBound = !! options.forceInBound ;

	// If set to a number, force line-splitting when exceeding that width
	this.lineWrapWidth = options.lineWrapWidth || null ;

	// If true, force word-aware line-splitting
	this.wordWrap = !! options.wordWrap ;

	// DEPRECATED but kept for backward compatibility.
	if ( options.wordWrapWidth ) {
		this.lineWrapWidth = options.wordWrapWidth ;
		this.wordWrap = true ;
	}

	this.selectionRegion = null ;

	this.buffer = [ [] ] ;

	this.stateMachine = options.stateMachine || null ;

	if ( options.hidden ) { this.setHidden( options.hidden ) ; }
}

module.exports = TextBuffer ;



// Backward compatibility
TextBuffer.create = ( ... args ) => new TextBuffer( ... args ) ;

TextBuffer.prototype.parseMarkup = string.markupMethod.bind( misc.markupOptions ) ;



// Special: if positive or 0, it's the width of the char, if -1 it's an anti-filler, if -2 it's a filler
function Cell( char = ' ' , special = 1 , attr = null , misc_ = null ) {
	this.char = char ;
	this.width = special >= 0 ? special : -special - 1 ;
	this.filler = special < 0 ;		// note: antiFiller ARE filler
	this.attr = attr ;
	this.misc = misc_ ;
}

TextBuffer.Cell = Cell ;



const termkit = require( './termkit.js' ) ;



TextBuffer.prototype.getText = function() {
	return this.buffer.map( line => string.unicode.fromCells( line ) ).join( '' ) ;
} ;



// TODOC
TextBuffer.prototype.getLineText = function( y = this.cy ) {
	if ( y >= this.buffer.length ) { return null ; }
	if ( ! this.buffer[ y ] ) { this.buffer[ y ] = [] ; }
	return string.unicode.fromCells( this.buffer[ y ] ) ;
} ;



// TODOC
// Get the indentation part of the line, return null if the line is empty (no char or no non-space char)
TextBuffer.prototype.getLineIndent = function( y = this.cy ) {
	if ( ! this.buffer[ y ] ) { return null ; }

	var x , xmin , xmax , cell ,
		indent = '' ;

	for ( x = 0 , xmax = this.buffer[ y ].length - 1 ; x <= xmax ; x ++ ) {
		cell = this.buffer[ y ][ x ] ;
		if ( ! cell.filler ) {
			if ( cell.char === '\t' || cell.char === ' ' ) {
				indent += cell.char ;
			}
			else if ( cell.char === '\n' ) {
				return null ;
			}
			else {
				return indent ;
			}
		}
	}

	return null ;
} ;



// TODOC
// Count characters in this line, excluding fillers
TextBuffer.prototype.getLineCharCount = function( y = this.cy ) {
	if ( y >= this.buffer.length ) { return null ; }
	if ( ! this.buffer[ y ] ) { this.buffer[ y ] = [] ; }
	return this.getCellsCharCount( this.buffer[ y ] ) ;
} ;



// internal
TextBuffer.prototype.getCellsCharCount = function( cells ) {
	var count = 0 ;

	for ( let cell of cells ) {
		if ( ! cell.filler ) { count ++ ; }
	}

	return count ;
} ;



// TODOC
// Remove spaces and tabs at the end of the line
TextBuffer.prototype.removeTrailingSpaces = function( y = this.cy , x = null , dry = false ) {
	if ( y >= this.buffer.length ) { return '' ; }
	if ( ! this.buffer[ y ] ) { this.buffer[ y ] = [] ; }

	var line = this.buffer[ y ] ;

	x = x ?? line.length - 1 ;

	if ( x < 0 || x >= line.length ) { return '' ; }

	var deletedStr = '' ,
		hasNL = line[ x ].char === '\n' ;

	if ( hasNL ) {
		x -- ;
	}

	for ( ; x >= 0 ; x -- ) {
		if ( line[ x ].filler ) { continue ; }

		let char = line[ x ].char ;

		if ( char === ' ' || char === '\t' ) {
			deletedStr = char + deletedStr ;
		}
		else {
			break ;
		}
	}

	if ( deletedStr && ! dry ) {
		line.splice( x + 1 , deletedStr.length ) ;
	}

	return deletedStr ;
} ;



// TODOC
// Get the text, but separate before the cursor and after the cursor
TextBuffer.prototype.getCursorSplittedText = function() {
	var y , line , before = '' , after = '' ;

	for ( y = 0 ; y < this.buffer.length ; y ++ ) {
		line = this.buffer[ y ] ;
		if ( y < this.cy ) {
			before += string.unicode.fromCells( line ) ;
		}
		else if ( y > this.cy ) {
			after += string.unicode.fromCells( line ) ;
		}
		else {
			before += string.unicode.fromCells( line.slice( 0 , this.cx ) ) ;
			after += string.unicode.fromCells( line.slice( this.cx ) ) ;
		}
	}

	return [ before , after ] ;
} ;



// .setText( text , [[hasMarkup] , baseAttr ] )
TextBuffer.prototype.setText = function( text , hasMarkup , baseAttr ) {
	// Argument management
	if ( typeof hasMarkup !== 'boolean' && typeof hasMarkup !== 'string' ) {
		baseAttr = hasMarkup ;
		hasMarkup = false ;
	}

	var legacyColor = false , parser = null ;

	switch ( hasMarkup ) {
		case 'ansi' : parser = string.ansi.parse ; break ;
		case 'legacyAnsi' : parser = string.ansi.parse ; legacyColor = true ; break ;
		case true : parser = this.parseMarkup ; break ;
	}

	if ( baseAttr === undefined ) { baseAttr = this.defaultAttr ; }
	if ( typeof baseAttr === 'object' ) { baseAttr = this.object2attr( baseAttr ) ; }

	// It must be reset now, because word-wrapping will be faster (always splice at the end of the array)
	this.buffer.length = 0 ;

	text.split( /(?<=\n)/g ).forEach( line => {
		var index = this.buffer.length ;
		this.buffer[ index ] = this.lineToCells( line , parser , baseAttr , 0 , legacyColor ) ;

		// /!\ Warning /!\ string.unicode.toCells() strips '\n', so we need to restore it at the end of the line
		if ( line[ line.length - 1 ] === '\n' ) {
			this.buffer[ index ].push( new Cell( '\n' , 1 , baseAttr ) ) ;
		}

		// word-wrap the current line, which is always the last line of the array (=faster)
		if ( this.lineWrapWidth ) { this.wrapLine( index ) ; }
	} ) ;

	this.selectionRegion = null ;
} ;



// Internal, transform a line of text, with or without markup to cells...
TextBuffer.prototype.lineToCells = function( line , parser , baseAttr , offset = 0 , legacyColor = false ) {
	if ( ! parser ) {
		return string.unicode.toCells( Cell , line , this.tabWidth , offset , baseAttr ) ;
	}

	var attr , attrObject ,
		cells = [] ;

	const defaultAttrObject = this.ScreenBuffer.attr2object( this.ScreenBuffer.DEFAULT_ATTR ) ;
	const baseAttrObject = this.ScreenBuffer.attr2object( baseAttr ) ;

	parser( line ).forEach( part => {
		attrObject = Object.assign( {} , part.specialReset ? defaultAttrObject : baseAttrObject , part ) ;
		delete attrObject.text ;

		// Remove incompatible flags
		if ( attrObject.defaultColor && attrObject.color ) { delete attrObject.defaultColor ; }
		if ( attrObject.bgDefaultColor && attrObject.bgColor ) { delete attrObject.bgDefaultColor ; }

		attr = this.object2attr( attrObject , undefined , legacyColor ) ;

		if ( part.text ) {
			cells.push( ... string.unicode.toCells( Cell , part.text , this.tabWidth , offset + cells.length , attr ) ) ;
		}
	} ) ;

	return cells ;
} ;



TextBuffer.prototype.setHidden = function( value ) {
	this.hidden =
		typeof value === 'string' && value.length ? value[ 0 ] :
		value ? termkit.spChars.password :
		false ;
} ;

TextBuffer.prototype.getHidden = function() { return this.hidden ; } ;



TextBuffer.prototype.setVoidTextBuffer = function( textBuffer = null ) {
	this.voidTextBuffer = textBuffer ;
} ;

TextBuffer.prototype.getVoidTextBuffer = function() { return this.voidTextBuffer ; } ;



TextBuffer.prototype.getContentSize = function() {
	return {
		width: Math.max( 1 , ... this.buffer.map( line => line.length ) ) ,
		height: this.buffer.length
	} ;
} ;



// TODOC
TextBuffer.prototype.coordinateToOffset = function( px , py ) {
	var x , y , line , offset = 0 ;

	for ( y = 0 ; y < py ; y ++ ) {
		line = this.buffer[ y ] ;
		if ( ! line ) { continue ; }
		for ( x = 0 ; x < line.length ; x ++ ) {
			if ( ! line[ x ].filler ) { offset ++ ; }
		}
	}

	line = this.buffer[ py ] ;
	if ( line ) {
		for ( x = 0 ; x < px && x < line.length ; x ++ ) {
			if ( ! line[ x ].filler ) { offset ++ ; }
		}
	}

	return offset ;
} ;



// Cursor offset in the text-content (excluding fillers)
TextBuffer.prototype.getCursorOffset = function() {
	return this.coordinateToOffset( this.cx , this.cy ) ;
} ;



// TODOC
TextBuffer.prototype.offsetToCoordinate = function( offset ) {
	var line ,
		x = 0 ,
		y = 0 ;

	if ( offset < 0 ) { return ; }

	while ( y < this.buffer.length ) {
		x = 0 ;
		line = this.buffer[ y ] ;
		//console.error( "  iter cy" , offset , y , x , "---" , line.length ) ;
		if ( ! line ) { continue ; }

		while ( x < line.length ) {
			//console.error( "    iter cx" , offset , y , x ) ;
			if ( ! line[ x ].filler ) {
				if ( offset <= 0 ) {
					if ( x === line.length && line[ line.length - 1 ].char === '\n' ) {
						//console.error( "    Exit with \\n" ) ;
						x = 0 ;
						y ++ ;
					}
					//console.error( "Exit" , y , x ) ;
					return { x , y } ;
				}

				offset -- ;
			}

			x ++ ;
		}

		y ++ ;
	}

	//console.error( "End of input" , offset , y , x ) ;
} ;



// Set the cursor position (cx,cy) depending on the offset in the text-content (excluding fillers)
TextBuffer.prototype.setCursorOffset = function( offset ) {
	var coord = this.offsetToCoordinate() ;
	if ( ! coord ) { return ; }
	this.cx = coord.x ;
	this.cy = coord.y ;
} ;



// TODOC
TextBuffer.prototype.setTabWidth = function( tabWidth ) {
	this.tabWidth = + tabWidth || 4 ;
	this.reTab() ;
} ;



// TODOC
TextBuffer.prototype.reTab = function() {
	for ( let y = 0 ; y < this.buffer.length ; y ++ ) {
		this.reTabLine( 0 , y ) ;
	}
} ;



// Recompute tabs
TextBuffer.prototype.reTabLine = function( startAt = 0 , y = this.cy ) {
	var length , cell , index , fillSize , input , output ,
		linePosition = startAt ;

	if ( this.buffer[ y ] === undefined ) { this.buffer[ y ] = [] ; }

	input = this.buffer[ y ] ;
	output = input.slice( 0 , startAt ) ;
	length = input.length ;

	for ( index = startAt ; index < length ; index ++ ) {
		cell = input[ index ] ;

		if ( cell.char === '\t' ) {
			fillSize = this.tabWidth - ( linePosition % this.tabWidth ) - 1 ;
			output.push( cell ) ;
			linePosition += 1 + fillSize ;

			while ( fillSize -- ) {
				output.push( new Cell( ' ' , -2 , cell.attr , cell.misc ) ) ;
			}

			// Skip input filler
			while ( index + 1 < length && input[ index + 1 ].filler ) { index ++ ; }
		}
		else {
			output.push( cell ) ;
			linePosition ++ ;
		}
	}

	this.buffer[ y ] = output ;
} ;



// Forbidden split for word-wrap, only if there is only one space before
const FORBIDDEN_SPLIT = new Set( [
	// French typo double graph punctuation,
	'!' , '?' , ':' , ';' , '«' , '»' ,
	// Other common punctuation that are often misused, should not be splitted anyway
	',' , '.' , '…'
] ) ;



// Wrap/word-wrap the current line, stop on the next explicit '\n' or at the end of the buffer.
// Return the next line to scan.
// /!\ Should probably .reTabLine()
TextBuffer.prototype.wrapLine = function( startY = this.cy , width = this.lineWrapWidth , wordWrap = this.wordWrap ) {
	var x , y , rightShift , endY , line , lineWidth , previousLine , lastChar , found , cursorInlineOffset ,
		checkCursor = this.cy === startY ;

	if ( startY >= this.buffer.length ) { return startY ; }

	// First check early exit conditions
	line = this.buffer[ startY ] ;
	previousLine = this.buffer[ startY - 1 ] ;
	rightShift = startY ? 0 : this.firstLineRightShift ;
	lineWidth = width - rightShift ;
	//console.error( "startY:" , startY ) ;
	if ( ! width || (
		line.length && line.length <= lineWidth && line[ line.length - 1 ].char === '\n'
		&& ( ! previousLine || ! previousLine.length || previousLine[ previousLine.length - 1 ].char === '\n' )
	) ) {
		//console.error( "exit" , previousLine);
		// There is nothing to do: we only have one line and it is not even longer than the lineWidth
		return startY + 1 ;
	}

	// Avoid creating arrays if early exit triggers
	var unifiedLine = [] , replacementLines = [] ;

	// First, search BACKWARD for the previous \n or start of buffer, to adjust startY value
	for ( y = startY - 1 ; y >= 0 ; y -- ) {
		line = this.buffer[ y ] ;

		if ( line.length && line[ line.length - 1 ].char === '\n' ) {
			startY = y + 1 ;
			break ;
		}
		else if ( ! y ) {
			startY = 0 ;
			break ;
		}
	}
	//console.error( "startY aft:" , startY ) ;

	// Then, search for the next \n and concat everything in a single line
	for ( y = startY ; y < this.buffer.length ; y ++ ) {
		//console.error( "  iter" , y , this.buffer.length) ;
		line = this.buffer[ y ] ;
		unifiedLine.push( ... line ) ;

		if ( line.length && line[ line.length - 1 ].char === '\n' ) {
			//console.error( "has \\n" ) ;
			// If we found the next \n, we don't go any further, but we still increment y because of endY
			y ++ ;
			break ;
		}
	}

	// Save the last line index
	endY = y ;
	rightShift = startY ? 0 : this.firstLineRightShift ;
	//console.error( "endY:" , endY ) ;

	if ( checkCursor ) {
		// Compute the cursor "inline" position
		cursorInlineOffset = 0 ;
		for ( y = startY ; y < this.cy ; y ++ ) {
			// +1 because the cursor is allowed to be ahead by one cell
			cursorInlineOffset += this.buffer[ y ].length ;
		}
		cursorInlineOffset += this.cx ;
	}

	while ( unifiedLine.length ) {
		lineWidth = width - rightShift ;
		rightShift = 0 ;	// Next time rightShift will be 0

		if ( unifiedLine.length <= lineWidth ) {
			// No more than the allowed lineWidth: add it and finish
			replacementLines.push( unifiedLine ) ;

			// If the length is EXACTLY the line-width and it's the last lines, create a new empty line
			if ( unifiedLine.length === lineWidth ) {
				replacementLines.push( [] ) ;
			}
			break ;
		}

		if ( ! wordWrap ) {
			replacementLines.push( unifiedLine.splice( 0 , lineWidth ) ) ;
			continue ;
		}

		found = false ;
		x = lineWidth ;

		if ( unifiedLine[ x ].char === ' ' ) {
			// Search forward for the first non-space
			while ( x < unifiedLine.length && unifiedLine[ x ].char === ' ' ) { x ++ ; }

			if ( x >= unifiedLine.length ) {
				// No non-space found: feed every remaining cells
				replacementLines.push( unifiedLine ) ;
				break ;
			}

			if ( x === lineWidth + 1 && FORBIDDEN_SPLIT.has( unifiedLine[ x ].char ) && unifiedLine[ lineWidth - 1 ].char !== ' ' ) {
				// Dang! We can't split here! We will search backward starting from lineWidth - 1
				x = lineWidth - 1 ;
			}
			else {
				// Else, cut at that non-space
				found = true ;
			}
		}

		if ( ! found ) {
			// Search backward for the first space
			lastChar = null ;

			while ( x >= 0 && ( unifiedLine[ x ].char !== ' ' || ( FORBIDDEN_SPLIT.has( lastChar ) && x > 0 && unifiedLine[ x - 1 ].char !== ' ' ) ) ) {
				lastChar = unifiedLine[ x ].char ;
				x -- ;
			}

			if ( x < 0 ) { x = lineWidth ; }	// No space found, cut at the lineWidth
			else { x ++ ; } // Cut just after the space
		}

		replacementLines.push( unifiedLine.splice( 0 , x ) ) ;
	}

	this.buffer.splice( startY , endY - startY , ... replacementLines ) ;


	// New endY to be returned, and used for cursor computing
	endY = startY + replacementLines.length ;

	if ( checkCursor ) {
		//console.error( "cursorInlineOffset:" , cursorInlineOffset , "endY:" , endY ) ;
		for ( y = startY ; ; y ++ ) {
			//console.error( "  iter" , y , "-- cursorInlineOffset:" , cursorInlineOffset , "this.buffer[ y ].length:" , this.buffer[ y ] && this.buffer[ y ].length ) ;
			if ( y >= endY ) {
				//console.error( "  exit #1" ) ;
				if ( y > 0 ) { y -- ; }
				this.cy = y ;
				this.cx = this.buffer[ y ] ? this.buffer[ y ].length : 0 ;
				break ;
			}

			if ( ! this.buffer[ y ] ) {
				//console.error( "  exit #2" ) ;
				this.cy = y ;
				this.cx = 0 ;
				break ;
			}

			if ( cursorInlineOffset < this.buffer[ y ].length ) {
				//console.error( "  exit #3" ) ;
				this.cy = y ;
				this.cx = cursorInlineOffset ;
				break ;
			}

			cursorInlineOffset -= this.buffer[ y ].length ;
		}

		//*
		// If we are after a true line breaker, go to the next line
		if ( this.cx && this.buffer[ this.cy ] && this.buffer[ this.cy ][ this.cx - 1 ] && this.buffer[ this.cy ][ this.cx - 1 ].char === '\n' ) {
			this.cy ++ ;
			this.cx = 0 ;
			if ( ! this.buffer[ this.cy ] ) { this.buffer[ this.cy ] = [] ; }
		}
		//*/
	}

	return endY ;
} ;



TextBuffer.prototype.wrapAllLines = function( width = this.lineWrapWidth , wordWrap = this.wordWrap ) {
	var y = 0 ;

	while ( y < this.buffer.length ) {
		y = this.wrapLine( y , width , wordWrap ) ;
	}
} ;



// Probably DEPRECATED
TextBuffer.prototype.wordWrapLine = function( startY = this.cy , width = this.lineWrapWidth ) {
	return this.wrapLine( startY , width , true ) ;
} ;



// Probably DEPRECATED
TextBuffer.prototype.wordWrapAllLines = function( width = this.lineWrapWidth ) {
	var y = 0 ;

	while ( y < this.buffer.length ) {
		y = this.wrapLine( y , width , true ) ;
	}
} ;



TextBuffer.prototype.setDefaultAttr = function( attr ) {
	if ( attr && typeof attr === 'object' ) { attr = this.object2attr( attr ) ; }
	else if ( typeof attr !== 'number' ) { return ; }

	this.defaultAttr = attr ;
} ;



TextBuffer.prototype.setEmptyCellAttr =		// DEPRECATED
TextBuffer.prototype.setVoidAttr = function( attr ) {
	if ( attr === null ) { this.voidAttr = null ; }		// null: don't draw
	else if ( attr && typeof attr === 'object' ) { attr = this.object2attr( attr ) ; }
	else if ( typeof attr !== 'number' ) { return ; }

	this.voidAttr = attr ;
} ;



TextBuffer.prototype.setAttrAt = function( attr , x , y ) {
	if ( attr && typeof attr === 'object' ) { attr = this.object2attr( attr ) ; }
	else if ( typeof attr !== 'number' ) { return ; }

	this.setAttrCodeAt( attr , x , y ) ;
} ;



// Faster than setAttrAt(), do no check attr, assume an attr code (number)
TextBuffer.prototype.setAttrCodeAt = function( attr , x , y ) {
	if ( ! this.buffer[ y ] ) { this.buffer[ y ] = [] ; }

	if ( ! this.buffer[ y ][ x ] ) { this.buffer[ y ][ x ] = new Cell( ' ' , 1 , attr ) ; }
	else { this.buffer[ y ][ x ].attr = attr ; }
} ;



const WHOLE_BUFFER_REGION = {
	xmin: 0 , xmax: Infinity , ymin: 0 , ymax: Infinity
} ;

// Set a whole region
TextBuffer.prototype.setAttrRegion = function( attr , region ) {
	if ( attr && typeof attr === 'object' ) { attr = this.object2attr( attr ) ; }
	else if ( typeof attr !== 'number' ) { return ; }

	this.setAttrCodeRegion( attr , region ) ;
} ;



// Faster than setAttrRegion(), do no check attr, assume an attr code (number)
TextBuffer.prototype.setAttrCodeRegion = function( attr , region = WHOLE_BUFFER_REGION ) {
	var x , y , xmin , xmax , ymax ;

	ymax = Math.min( region.ymax , this.buffer.length - 1 ) ;

	for ( y = region.ymin ; y <= ymax ; y ++ ) {
		if ( ! this.buffer[ y ] ) { this.buffer[ y ] = [] ; }

		xmin = y === region.ymin ? region.xmin : 0 ;
		xmax = y === region.ymax ? Math.min( region.xmax , this.buffer[ y ].length - 1 ) : this.buffer[ y ].length - 1 ;

		for ( x = xmin ; x <= xmax ; x ++ ) {
			this.buffer[ y ][ x ].attr = attr ;
		}
	}
} ;



TextBuffer.prototype.isInSelection = function( x = this.cx , y = this.cy ) {
	if ( ! this.selectionRegion ) { return false ; }
	return this.isInRegion( this.selectionRegion , x , y ) ;
} ;



TextBuffer.prototype.isInRegion = function( region , x = this.cx , y = this.cy ) {
	return (
		y >= region.ymin && y <= region.ymax
		&& ( y !== region.ymin || x >= region.xmin )
		&& ( y !== region.ymax || x <= region.xmax )
	) ;
} ;



TextBuffer.prototype.setSelectionRegion = function( region ) {
	if ( ! this.selectionRegion ) {
		this.selectionRegion = {} ;
	}

	if ( region.xmin !== undefined && region.ymin !== undefined ) {
		if ( region.xmin < 0 ) { region.xmin = 0 ; }
		if ( region.ymin < 0 ) { region.ymin = 0 ; }

		this.selectionRegion.xmin = region.xmin ;
		this.selectionRegion.ymin = region.ymin ;
		this.selectionRegion.cellMin = this.buffer[ region.ymin ]?.[ region.xmin ] ?? null ;
	}

	if ( region.xmax !== undefined && region.ymax !== undefined ) {
		this.selectionRegion.xmax = region.xmax ;
		this.selectionRegion.ymax = region.ymax ;
		this.selectionRegion.cellMax = this.buffer[ region.ymax ]?.[ region.xmax ] ?? null ;
	}
} ;



// TODOC
TextBuffer.prototype.startOfSelection = function() {
	if ( ! this.selectionRegion ) {
		this.selectionRegion = {} ;
	}

	this.selectionRegion.xmin = this.cx ;
	this.selectionRegion.ymin = this.cy ;
	this.selectionRegion.cellMin = this.buffer[ this.cy ]?.[ this.cx ] ?? null ;
} ;



// TODOC
TextBuffer.prototype.endOfSelection = function() {
	var coord = this.oneStepBackward() ;

	if ( ! this.selectionRegion ) {
		this.selectionRegion = {} ;
	}

	if ( ! coord ) {
		// Start of the file
		this.selectionRegion = null ;
		return ;
	}

	this.selectionRegion.xmax = coord.x ;
	this.selectionRegion.ymax = coord.y ;
	this.selectionRegion.cellMax = this.buffer[ coord.y ]?.[ coord.x ] ?? null ;
} ;



// TODOC
// Reset the region by scanning for the starting and ending cell
// If cursorCell is set, set cursor position to this cell
TextBuffer.prototype.updateSelectionFromCells = function( cursorCell = null ) {
	if ( ! this.selectionRegion ) { return ; }
	if ( ! this.selectionRegion.cellMin || ! this.selectionRegion.cellMax ) {
		this.selectionRegion = null ;
		return ;
	}

	var xmin , xmax , ymin , ymax ;

	for ( let y = 0 ; y < this.buffer.length ; y ++ ) {
		let currentLine = this.buffer[ y ] ;
		if ( ! currentLine ) { continue ; }

		for ( let x = 0 ; x < currentLine.length ; x ++ ) {
			if ( currentLine[ x ] === this.selectionRegion.cellMin ) {
				xmin = x ;
				ymin = y ;
			}

			if ( currentLine[ x ] === this.selectionRegion.cellMax ) {
				xmax = x ;
				ymax = y ;
			}

			if ( cursorCell && currentLine[ x ] === cursorCell ) {
				this.cx = x ;
				this.cy = y ;
			}
		}
	}

	if ( ymin === undefined || ymax === undefined ) {
		this.selectionRegion = null ;
		return ;
	}

	this.selectionRegion.xmin = xmin ;
	this.selectionRegion.xmax = xmax ;
	this.selectionRegion.ymin = ymin ;
	this.selectionRegion.ymax = ymax ;
} ;



// TODOC
// Return a Cell instance that is at the cursor location, or null if none
TextBuffer.prototype.getCursorCell = function() {
	return this.buffer[ this.cy ]?.[ this.cx ] ?? null ;
} ;



// TODOC
// Return true if found, else return false
TextBuffer.prototype.updateCursorFromCell = function( cursorCell ) {
	if ( ! cursorCell ) { return false ; }

	for ( let y = 0 ; y < this.buffer.length ; y ++ ) {
		let currentLine = this.buffer[ y ] ;
		if ( ! currentLine ) { continue ; }

		for ( let x = 0 ; x < currentLine.length ; x ++ ) {
			if ( cursorCell && currentLine[ x ] === cursorCell ) {
				this.cx = x ;
				this.cy = y ;
				return true ;
			}
		}
	}

	return false ;
} ;



TextBuffer.prototype.resetSelectionRegion = function() {
	if ( ! this.selectionRegion ) { return ; }
	this.selectionRegion = null ;
} ;



TextBuffer.prototype.getSelectionText = function() {
	return this.getRegionText( this.selectionRegion ) ;
} ;



// TODOC
TextBuffer.prototype.getRegionText = function( region , structured = false ) {
	var x , y , xmin , xmax , ymax , cell ,
		count = 0 ,
		str = '' ;

	if ( ! region || region.xmin === undefined || region.ymin === undefined || region.xmax === undefined || region.ymax === undefined ) {
		return ;
	}

	ymax = Math.min( region.ymax , this.buffer.length - 1 ) ;

	for ( y = region.ymin ; y <= ymax ; y ++ ) {
		if ( ! this.buffer[ y ] ) { this.buffer[ y ] = [] ; }

		xmin = y === region.ymin ? region.xmin : 0 ;
		xmax = y === region.ymax ? Math.min( region.xmax , this.buffer[ y ].length - 1 ) : this.buffer[ y ].length - 1 ;

		for ( x = xmin ; x <= xmax ; x ++ ) {
			cell = this.buffer[ y ][ x ] ;
			if ( ! cell.filler ) {
				str += cell.char ;
				count ++ ;
			}
		}
	}

	if ( structured ) { return { string: str , count } ; }
	return str ;
} ;



// TODOC
TextBuffer.prototype.deleteSelection = function( getDeleted = false ) {
	if ( ! this.selectionRegion ) { return ; }
	var region = this.selectionRegion ;
	this.selectionRegion = null ;	// unselect now
	return this.deleteRegion( region , getDeleted ) ;
} ;



// TODOC
// Delete current line
TextBuffer.prototype.deleteRegion = function( region , getDeleted = false ) {
	var x , y , xmin , xmax , ymax , currentLine , tabIndex , deleted , cursorCell ;

	if ( ! region || region.xmin === undefined || region.ymin === undefined || region.xmax === undefined || region.ymax === undefined ) {
		return ;
	}

	cursorCell = this.buffer[ this.cy ]?.[ this.cx ] ?? null ;

	if ( getDeleted ) {
		deleted = this.getRegionText( region , true ) ;
	}

	ymax = Math.min( region.ymax , this.buffer.length - 1 ) ;
	y = region.ymin ;
	currentLine = this.buffer[ y ] ;

	if ( y === ymax ) {
		if ( ! this.buffer[ y ] ) { return deleted ; }

		xmin = region.xmin ;
		xmax = Math.min( region.xmax , currentLine.length - 1 ) ;
		currentLine.splice( xmin , xmax - xmin + 1 ) ;

	}
	else {
		let lastLine = this.buffer[ ymax ] ;

		// First, remove next lines
		this.buffer.splice( y + 1 , ymax - y ) ;

		xmin = region.xmin ;
		currentLine.splice( xmin , currentLine.length - xmin ) ;

		if ( lastLine && lastLine.length ) {
			xmax = Math.min( region.xmax , lastLine.length - 1 ) ;
			lastLine.splice( 0 , xmax + 1 ) ;
			if ( lastLine.length ) {
				currentLine.splice( currentLine.length , 0 , ... lastLine ) ;
			}
		}
	}

	if ( y < this.buffer.length - 1 && ( ! currentLine.length || currentLine[ currentLine.length - 1 ].char !== '\n' ) ) {
		this.joinLine( true , y ) ;
	}

	tabIndex = this.indexOfCharInLine( currentLine , '\t' , region.xmin ) ;
	if ( tabIndex !== -1 ) { this.reTabLine( tabIndex , y ) ; }

	if ( this.selectionRegion ) {
		this.updateSelectionFromCells( cursorCell ) ;
	}
	else if ( cursorCell ) {
		this.updateCursorFromCell( cursorCell ) ;
	}

	return deleted ;
} ;



// Misc data are lazily created
TextBuffer.prototype.getMisc = function() {
	if ( ! this.buffer[ this.cy ] || ! this.buffer[ this.cy ][ this.cx ] ) { return ; }
	if ( ! this.buffer[ this.cy ][ this.cx ].misc ) { this.buffer[ this.cy ][ this.cx ].misc = {} ; }
	return this.buffer[ this.cy ][ this.cx ].misc ;
} ;



TextBuffer.prototype.getMiscAt = function( x , y ) {
	if ( ! this.buffer[ y ] || ! this.buffer[ y ][ x ] ) { return ; }
	if ( ! this.buffer[ y ][ x ].misc ) { this.buffer[ y ][ x ].misc = {} ; }
	return this.buffer[ y ][ x ].misc ;
} ;



TextBuffer.prototype.iterate = function( options , callback ) {
	var x , y , yMax , cell , lastNonFillerCell , offset = 0 , length ;

	if ( typeof options === 'function' ) { callback = options ; options = {} ; }
	else if ( ! options || typeof options !== 'object' ) { options = {} ; }

	if ( ! this.buffer.length ) { return ; }

	for ( y = 0 , yMax = this.buffer.length ; y < yMax ; y ++ ) {
		if ( this.buffer[ y ] ) {
			length = this.buffer[ y ].length ;
			lastNonFillerCell = null ;

			for ( x = 0 ; x < length ; x ++ ) {
				cell = this.buffer[ y ][ x ] ;
				if ( cell.filler ) {
					if ( options.fillerCopyAttr && lastNonFillerCell ) {
						cell.attr = lastNonFillerCell.attr ;
					}
				}
				else {
					callback( {
						offset: offset ,
						x: x ,
						y: y ,
						text: cell.char ,
						attr: cell.attr ,
						misc: cell.misc
					} ) ;

					offset ++ ;
					lastNonFillerCell = cell ;
				}
			}
		}
	}

	// Call the callback one last time at the end of the buffer, with an empty string.
	// Useful for 'Ne' (Neon) state machine.
	if ( options.finalCall ) {
		callback( {
			offset: offset + 1 ,
			x: null ,
			y: y ,
			text: '' ,
			attr: null ,
			misc: null
		} ) ;
	}
} ;



// Move to the left to the leading cell of a full-width char
TextBuffer.prototype.moveToLeadingFullWidth = function() {
	var currentLine = this.buffer[ this.cy ] ;
	while ( this.cx && currentLine?.[ this.cx ]?.filler && currentLine?.[ this.cx ]?.width === 0 ) { this.cx -- ; }
} ;



TextBuffer.prototype.moveTo = function( x , y ) {
	this.cx = x >= 0 ? x : 0 ;
	this.cy = y >= 0 ? y : 0 ;
	if ( this.forceInBound ) { this.moveInBound( true ) ; }
	this.moveToLeadingFullWidth() ;
} ;



TextBuffer.prototype.move = function( x , y ) { this.moveTo( this.cx + x , this.cy + y ) ; } ;
TextBuffer.prototype.moveToColumn = function( x ) { this.moveTo( x , this.cy ) ; } ;
TextBuffer.prototype.moveToLine = TextBuffer.prototype.moveToRow = function( y ) { this.moveTo( this.cx , y ) ; } ;



TextBuffer.prototype.moveUp = function() {
	this.cy = this.cy > 0 ? this.cy - 1 : 0 ;
	if ( this.forceInBound ) { this.moveInBound( true ) ; }
	this.moveToLeadingFullWidth() ;
} ;



TextBuffer.prototype.moveDown = function() {
	this.cy ++ ;
	if ( this.forceInBound ) { this.moveInBound( true ) ; }
	this.moveToLeadingFullWidth() ;
} ;



TextBuffer.prototype.moveLeft = function() {
	this.cx = this.cx > 0 ? this.cx - 1 : 0 ;
	if ( this.forceInBound ) { this.moveInBound( true ) ; }
	this.moveToLeadingFullWidth() ;
} ;



TextBuffer.prototype.moveRight = function() {
	this.cx ++ ;

	var currentLine = this.buffer[ this.cy ] ;
	while ( currentLine?.[ this.cx ]?.filler && currentLine?.[ this.cx ]?.width === 0 ) { this.cx ++ ; }

	if ( this.forceInBound ) { this.moveInBound( true ) ; }
} ;



TextBuffer.prototype.moveForward = function( testFn , justSkipFiller ) {
	var oldCx = this.cx ,
		currentLine = this.buffer[ this.cy ] ;

	//if ( justSkipFiller && ( ! currentLine || ! currentLine[ this.cx ] || ! currentLine[ this.cx ].filler || currentLine[ this.cx ].char !== '\n' ) ) { return ; }
	if ( justSkipFiller && ( ! currentLine || ! currentLine[ this.cx ] || ! currentLine[ this.cx ].filler ) ) { return ; }

	for ( ;; ) {
		if ( ! currentLine || this.cx + 1 > currentLine.length || ( this.cx < currentLine.length && currentLine[ this.cx ].char === '\n' ) ) {
			if ( this.cy + 1 < this.buffer.length || ! this.forceInBound ) {
				this.cy ++ ;
				this.cx = 0 ;
			}
			else {
				this.cx = oldCx ;
			}

			break ;
		}

		this.cx ++ ;

		if (
			! currentLine[ this.cx ]
			|| (
				! currentLine[ this.cx ].filler
				&& ( ! testFn || testFn( currentLine[ this.cx ].char , this.cx , this.cy ) )
			)
		) {
			break ;
		}
	}

	if ( this.forceInBound ) { this.moveInBound() ; }
} ;



TextBuffer.prototype.moveBackward = function( testFn , justSkipFiller ) {
	var lineLength ,
		currentLine = this.buffer[ this.cy ] ;

	//if ( justSkipFiller && ( ! currentLine || ! currentLine[ this.cx ] || ! currentLine[ this.cx ].filler || currentLine[ this.cx ].char !== '\n' ) ) { return ; }
	if ( justSkipFiller && ( ! currentLine || ! currentLine[ this.cx ] || ! currentLine[ this.cx ].filler ) ) { return ; }

	for ( ;; ) {
		lineLength = currentLine ? currentLine.length : 0 ;

		if ( this.cx > lineLength ) { this.cx = lineLength ; }
		else { this.cx -- ; }

		if ( this.cx < 0 ) {
			this.cy -- ;

			if ( this.cy < 0 ) { this.cy = 0 ; this.cx = 0 ; break ; }

			this.moveToEndOfLine() ;
			break ;
		}

		if (
			! currentLine || ! currentLine[ this.cx ]
			|| (
				! currentLine[ this.cx ].filler
				&& ( ! testFn || testFn( currentLine[ this.cx ].char ) )
			)
		) {
			break ;
		}
	}

	if ( this.forceInBound ) { this.moveInBound() ; }
} ;



// Rough word boundary test
const WORD_BOUNDARY = new Set( [ ' ' , '\t' , '.' , ',' , ';' , ':' , '!' , '?' , '/' , '\\' , '(' , ')' , '[' , ']' , '{' , '}' , '<' , '>' , '=' , "'" , '"' ] ) ;

TextBuffer.prototype.wordBoundary_ = function( method , checkInitial ) {
	var initialChar , nonBoundarySeen = false ;

	if ( checkInitial && this.buffer[ this.cy ] && this.buffer[ this.cy ][ this.cx ] ) {
		initialChar = this.buffer[ this.cy ][ this.cx ].char ;
		if ( ! WORD_BOUNDARY.has( initialChar ) ) { nonBoundarySeen = true ; }
	}

	this[ method ]( char => {
		if ( WORD_BOUNDARY.has( char ) ) {
			if ( nonBoundarySeen ) { return true ; }
			return false ;
		}

		nonBoundarySeen = true ;
		return false ;
	} ) ;
} ;



TextBuffer.prototype.moveToEndOfWord = function() {
	return this.wordBoundary_( 'moveForward' , true ) ;
} ;



TextBuffer.prototype.moveToStartOfWord = function() {
	var char , oldCx = this.cx , oldCy = this.cy ;
	this.wordBoundary_( 'moveBackward' ) ;

	if ( this.cx < oldCx && this.cy === oldCy && this.buffer[ this.cy ] && this.buffer[ this.cy ][ this.cx ] ) {
		char = this.buffer[ this.cy ][ this.cx ].char ;
		if ( WORD_BOUNDARY.has( char ) ) { this.moveForward() ; }
	}
	else if ( this.cy < oldCy && oldCx !== 0 && this.buffer[ oldCy ] && this.buffer[ oldCy ][ 0 ] ) {
		char = this.buffer[ oldCy ][ 0 ].char ;
		if ( ! WORD_BOUNDARY.has( char ) ) {
			this.cx = 0 ;
			this.cy = oldCy ;
		}
	}
} ;



TextBuffer.prototype.moveToStartOfLine = function() { this.cx = 0 ; } ;



TextBuffer.prototype.moveToEndOfLine = function() {
	var currentLine = this.buffer[ this.cy ] ;

	if ( ! currentLine ) {
		this.cx = 0 ;
	}
	else if ( currentLine.length && currentLine[ currentLine.length - 1 ].char === '\n' ) {
		this.cx = currentLine.length - 1 ;
	}
	else {
		this.cx = currentLine.length ;
	}
} ;



// Move to the start of the buffer: 0,0
TextBuffer.prototype.moveToStartOfBuffer = function() { this.cx = this.cy = 0 ; } ;



// Move to the end of the buffer: end of line of the last line
TextBuffer.prototype.moveToEndOfBuffer = function() {
	this.cy = this.buffer.length ? this.buffer.length - 1 : 0 ;
	this.moveToEndOfLine() ;
} ;



TextBuffer.prototype.moveInBound = function( ignoreCx ) {
	var currentLine = this.buffer[ this.cy ] ;

	if ( this.cy > this.buffer.length ) { this.cy = this.buffer.length ; }

	if ( ignoreCx ) { return ; }

	if ( ! currentLine ) {
		this.cx = 0 ;
	}
	else if ( currentLine.length && currentLine[ currentLine.length - 1 ].char === '\n' ) {
		if ( this.cx > currentLine.length - 1 ) { this.cx = currentLine.length - 1 ; }
	}
	else if ( this.cx > currentLine.length ) {
		this.cx = currentLine.length ;
	}
} ;



// .insert( text , [[hasMarkup] , attr ] )
TextBuffer.prototype.insert = function( text , hasMarkup , attr ) {
	var lines , index , length ,
		count = 0 ;

	if ( ! text ) { return count ; }

	if ( typeof hasMarkup !== 'boolean' && typeof hasMarkup !== 'string' ) {
		attr = hasMarkup ;
		hasMarkup = false ;
	}

	var legacyColor = false , parser = null ;

	switch ( hasMarkup ) {
		case 'ansi' : parser = string.ansi.parse ; break ;
		case 'legacyAnsi' : parser = string.ansi.parse ; legacyColor = true ; break ;
		case true : parser = this.parseMarkup ; break ;
	}

	lines = text.split( '\n' ) ;
	length = lines.length ;

	if ( attr && typeof attr === 'object' ) { attr = this.object2attr( attr ) ; }
	else if ( typeof attr !== 'number' ) { attr = this.defaultAttr ; }

	if ( this.forceInBound ) { this.moveInBound() ; }

	count += this.inlineInsert( lines[ 0 ] , parser , attr ) ;

	for ( index = 1 ; index < length ; index ++ ) {
		this.newLine( true ) ;
		count ++ ;
		count += this.inlineInsert( lines[ index ] , parser , attr , legacyColor ) ;
	}

	if ( this.selectionRegion ) { this.updateSelectionFromCells() ; }

	return count ;
} ;



TextBuffer.prototype.prepend = function( text , hasMarkup , attr ) {
	this.moveToStartOfBuffer() ;
	this.insert( text , hasMarkup , attr ) ;
} ;



TextBuffer.prototype.append = function( text , hasMarkup , attr ) {
	this.moveToEndOfBuffer() ;
	this.insert( text , hasMarkup , attr ) ;
} ;



// Internal API:
// Insert inline chars (no control chars)
TextBuffer.prototype.inlineInsert = function( text , parser , attr , legacyColor = false ) {
	var currentLine , currentLineLength , hasNL , nlCell , tabIndex , fillSize , cells , cellsCharCount ,
		count = 0 ;

	this.moveForward( undefined , true ) ;	// just skip filler char

	// Should come after moving forward (rely on this.cx)
	//cells = string.unicode.toCells( Cell , text , this.tabWidth , this.cx , attr ) ;
	cells = this.lineToCells( text , parser , attr , this.cx , legacyColor ) ;
	cellsCharCount = this.getCellsCharCount( cells ) ;

	// Is this a new line?
	if ( this.cy >= this.buffer.length ) {
		// Create all missing lines, if any
		while ( this.buffer.length < this.cy ) {
			this.buffer.push( [ new Cell( '\n' , 1 , this.defaultAttr ) ] ) ;
			count ++ ;
		}

		// Add a '\n' to the last line, if it is missing
		if (
			this.cy && (
				! this.buffer[ this.cy - 1 ].length ||
				this.buffer[ this.cy - 1 ][ this.buffer[ this.cy - 1 ].length - 1 ].char !== '\n'
			)
		) {
			this.buffer[ this.cy - 1 ].push( new Cell( '\n' , 1 , this.defaultAttr ) ) ;
			count ++ ;
		}

		this.buffer[ this.cy ] = [] ;
	}

	currentLine = this.buffer[ this.cy ] ;
	currentLineLength = currentLine.length ;
	hasNL = currentLineLength && currentLine[ currentLineLength - 1 ].char === '\n' ;

	// Apply
	if ( this.cx === currentLineLength ) {
		if ( hasNL ) {
			currentLine.splice( currentLineLength - 1 , 0 , new Cell( ' ' , 1 , this.defaultAttr ) , ... cells ) ;
			count += 1 + cellsCharCount ;
		}
		else {
			currentLine.push( ... cells ) ;
			count += cellsCharCount ;
		}
	}
	else if ( this.cx < currentLineLength ) {
		currentLine.splice( this.cx , 0 , ... cells ) ;
		count += cellsCharCount ;
	}
	// this.cx > currentLineLength
	else if ( hasNL ) {
		fillSize = this.cx - currentLineLength + 1 ;
		nlCell = currentLine.pop() ;

		while ( fillSize -- ) {
			currentLine.push( new Cell( ' ' , 1 , this.defaultAttr ) ) ;
			count ++ ;
		}

		currentLine.push( ... cells , nlCell ) ;
		count += cellsCharCount ;
	}
	else {
		fillSize = this.cx - currentLineLength ;

		while ( fillSize -- ) {
			currentLine.push( new Cell( ' ' , 1 , this.defaultAttr ) ) ;
			count ++ ;
		}

		currentLine.push( ... cells ) ;
		count += cellsCharCount ;
	}

	// Patch tab if needed
	tabIndex = this.indexOfCharInLine( currentLine , '\t' , this.cx ) ;
	this.cx += cells.length ;

	// (AFTER cx++) word-wrap the current line, which is always the last line of the array (=faster)
	if ( this.lineWrapWidth ) { this.wrapLine() ; }

	if ( tabIndex !== -1 ) { this.reTabLine( tabIndex ) ; }

	return count ;
} ;



// Internal utility function
TextBuffer.prototype.indexOfCharInLine = function( line , char , index = 0 ) {
	var iMax = line.length ;

	for ( ; index < iMax ; index ++ ) {
		if ( line[ index ].char === char ) { return index ; }
	}

	// Like .indexOf() does...
	return -1 ;
} ;



// Delete chars
TextBuffer.prototype.delete = function( count , getDeleted = false ) {
	var currentLine , inlineCount , fillerCount , hasNL , removedCells ,
		deleted = getDeleted ? { string: '' , count: 0 } : undefined ;

	if ( count === undefined ) { count = 1 ; }

	if ( this.forceInBound ) { this.moveInBound() ; }

	if ( this.buffer[ this.cy ] && this.buffer[ this.cy ][ this.cx ] && this.buffer[ this.cy ][ this.cx ].filler ) {
		this.moveBackward( undefined , true ) ;	// just skip filler char
		count -- ;
	}


	while ( count > 0 ) {
		currentLine = this.buffer[ this.cy ] ;

		// If we are already at the end of the buffer...
		if (
			this.cy >= this.buffer.length ||
			( this.cy === this.buffer.length - 1 && this.cx >= currentLine.length )
		) {
			if ( this.selectionRegion ) { this.updateSelectionFromCells() ; }
			return deleted ;
		}

		if ( currentLine ) {
			// If the cursor is too far away, move it at the end of the line
			if ( this.cx > currentLine.length ) { this.cx = currentLine.length ; }

			if ( currentLine[ this.cx ] && currentLine[ this.cx ].char !== '\n' ) {
				// Compute inline delete
				hasNL = currentLine[ currentLine.length - 1 ]?.char === '\n' ;
				fillerCount = this.countInlineForwardFiller( count ) ;
				inlineCount = Math.min( count + fillerCount , currentLine.length - hasNL - this.cx ) ;

				// Apply inline delete
				if ( inlineCount > 0 ) {
					removedCells = currentLine.splice( this.cx , inlineCount ) ;
					if ( getDeleted ) {
						removedCells = removedCells.filter( cell => ! cell.filler ) ;
						deleted.string += removedCells.map( cell => cell.char ).join( '' ) ;
						deleted.count += removedCells.length ;
					}
				}

				count -= inlineCount - fillerCount ;
			}
		}

		if ( count > 0 ) {
			if ( this.joinLine( true ) ) {
				count -- ;
				if ( getDeleted ) {
					deleted.string += '\n' ;
					deleted.count ++ ;
				}
			}
		}
	}

	// word-wrap the current line, which is always the last line of the array (=faster)
	if ( this.lineWrapWidth ) { this.wrapLine() ; }

	// Patch tab if needed
	//tabIndex = currentLine.indexOf( '\t' , this.cx ) ;
	//if ( tabIndex !== -1 ) { this.reTabLine( tabIndex ) ; }
	this.reTabLine() ;	// Do it every time, before finding a better way to do it

	if ( this.selectionRegion ) { this.updateSelectionFromCells() ; }

	return deleted ;
} ;



// Delete backward chars
TextBuffer.prototype.backDelete = function( count , getDeleted = false ) {
	//console.error( ">>> backDelete:" , count ) ;
	var currentLine , inlineCount , fillerCount , tabIndex , removedCells ,
		deleted = getDeleted ? { string: '' , count: 0 } : undefined ;

	if ( count === undefined ) { count = 1 ; }

	if ( this.forceInBound ) { this.moveInBound() ; }

	if ( this.buffer[ this.cy ] && this.cx && this.buffer[ this.cy ][ this.cx - 1 ] && this.buffer[ this.cy ][ this.cx - 1 ].filler ) {
		this.moveBackward( undefined , true ) ;	// just skip filler char
		//count -- ;	// do not downcount: the cursor is always on a \x00 before deleting a \t
	}


	while ( count > 0 ) {
		currentLine = this.buffer[ this.cy ] ;

		// If we are already at the begining of the buffer...
		if ( this.cy === 0 && this.cx === 0 ) {
			if ( this.selectionRegion ) { this.updateSelectionFromCells() ; }
			return deleted ;
		}

		if ( currentLine ) {
			// If the cursor is to far away, move it at the end of the line, it will cost one 'count'
			if ( this.cx > currentLine.length ) {
				if ( currentLine.length && currentLine[ currentLine.length - 1 ].char === '\n' ) { this.cx = currentLine.length - 1 ; }
				else { this.cx = currentLine.length ; }

				count -- ;
			}
			else if ( this.cx && this.cx === currentLine.length && currentLine[ currentLine.length - 1 ].char === '\n' ) {
				this.cx = currentLine.length - 1 ;
			}

			// Compute inline delete
			fillerCount = this.countInlineBackwardFiller( count ) ;
			inlineCount = Math.min( count + fillerCount , this.cx ) ;
			//console.error( "inlineCount:" , inlineCount , fillerCount , this.cx , this.cx - inlineCount ) ;

			// Apply inline delete
			if ( inlineCount > 0 ) {
				removedCells = currentLine.splice( this.cx - inlineCount , inlineCount ) ;
				if ( getDeleted ) {
					removedCells = removedCells.filter( cell => ! cell.filler ) ;
					deleted.string = removedCells.map( cell => cell.char ).join( '' ) + deleted.string ;
					deleted.count += removedCells.length ;
				}
				this.cx -= inlineCount ;
			}

			count -= inlineCount - fillerCount ;
		}

		if ( count > 0 ) {
			this.cy -- ;
			this.cx = currentLine ? currentLine.length : 0 ;
			if ( this.joinLine( true ) ) {
				count -- ;
				if ( getDeleted ) {
					deleted.string = '\n' + deleted.string ;
					deleted.count ++ ;
				}
			}
		}
	}

	// word-wrap the current line, which is always the last line of the array (=faster)
	if ( this.lineWrapWidth ) { this.wrapLine() ; }

	// Patch tab if needed
	//tabIndex = currentLine.indexOf( '\t' , this.cx ) ;
	//if ( tabIndex !== -1 ) { this.reTabLine( tabIndex ) ; }
	this.reTabLine( tabIndex ) ;	// Do it every time, before finding a better way to do it

	if ( this.selectionRegion ) { this.updateSelectionFromCells() ; }

	return deleted ;
} ;



// Fix a backward counter, get an additional count for each null char encountered
TextBuffer.prototype.countInlineBackwardFiller = function( count ) {
	var x , cell ,
		filler = 0 ;

	for ( x = this.cx - 1 ; x >= 0 && count ; x -- ) {
		cell = this.buffer[ this.cy ][ x ] ;

		if ( cell && cell.filler ) {
			filler ++ ;
		}
		else {
			count -- ;
		}
	}

	return filler ;
} ;



// Fix a forward counter, get an additional count for each null char encountered
TextBuffer.prototype.countInlineForwardFiller = function( count ) {
	var x , cell ,
		xMax = this.buffer[ this.cy ].length ,
		filler = 0 ;

	for ( x = this.cx ; x < xMax && count ; x ++ ) {
		cell = this.buffer[ this.cy ][ x + 1 ] ;

		if ( cell && cell.filler ) {
			filler ++ ;
		}
		else {
			count -- ;
		}
	}

	return filler ;
} ;



TextBuffer.prototype.newLine = function( internalCall ) {
	var currentLine , currentLineLength , nextLine = [] , tabIndex ;

	if ( ! internalCall && this.forceInBound ) { this.moveInBound() ; }

	if ( this.buffer[ this.cy ] === undefined ) { this.buffer[ this.cy ] = [] ; }

	currentLine = this.buffer[ this.cy ] ;
	currentLineLength = currentLine.length ;

	// Apply
	if ( this.cx < currentLineLength ) {
		nextLine = currentLine.slice( this.cx ) ;
		currentLine.length = this.cx ;
	}

	currentLine.push( new Cell( '\n' , 1 , this.defaultAttr ) ) ;

	this.buffer.splice( this.cy + 1 , 0 , nextLine ) ;

	this.cx = 0 ;
	this.cy ++ ;

	// Patch tab if needed
	if ( ! internalCall ) {
		// word-wrap the current line, which is always the last line of the array (=faster)
		if ( this.lineWrapWidth ) { this.wrapLine() ; }

		tabIndex = this.indexOfCharInLine( currentLine , '\t' , this.cx ) ;
		if ( tabIndex !== -1 ) { this.reTabLine( tabIndex ) ; }

		if ( this.selectionRegion ) { this.updateSelectionFromCells() ; }
	}
} ;



// If y is specified, we are not joining on current cursor
TextBuffer.prototype.joinLine = function( internalCall , y ) {
	var tabIndex , currentLine , x ,
		updateCursor = false ,
		hasDeleted = false ;

	if ( y === undefined ) {
		y = this.cy ;
		updateCursor = true ;
	}

	if ( ! internalCall && this.forceInBound ) { this.moveInBound() ; }

	if ( this.buffer[ y ] === undefined ) { this.buffer[ y ] = [] ; }
	if ( this.buffer[ y + 1 ] === undefined ) { this.buffer[ y + 1 ] = [] ; }

	currentLine = this.buffer[ y ] ;

	if ( currentLine.length && currentLine[ currentLine.length - 1 ].char === '\n' ) {
		// Remove the last '\n' if any
		currentLine.length -- ;
		hasDeleted = true ;
	}

	x = currentLine.length ;
	if ( updateCursor ) { this.cx = x ; }

	currentLine.splice( currentLine.length , 0 , ... this.buffer[ y + 1 ] ) ;

	this.buffer.splice( y + 1 , 1 ) ;

	// Patch tab if needed
	if ( ! internalCall ) {
		// word-wrap the current line, which is always the last line of the array (=faster)
		if ( this.lineWrapWidth ) { this.wrapLine() ; }

		tabIndex = this.indexOfCharInLine( currentLine , '\t' , x ) ;
		if ( tabIndex !== -1 ) { this.reTabLine( tabIndex , y ) ; }

		if ( this.selectionRegion ) { this.updateSelectionFromCells() ; }
	}

	return hasDeleted ;
} ;



// TODOC
// Delete current line
TextBuffer.prototype.deleteLine = function( getDeleted = false ) {
	var currentLine , inlineCount , fillerCount , hasNL , removedCells , deleted ;

	if ( this.forceInBound ) { this.moveInBound() ; }
	if ( this.cy >= this.buffer.length ) { return ; }

	if ( getDeleted ) {
		deleted = {
			count: this.getLineCharCount() ,
			string: this.getLineText()
		} ;
	}
	this.buffer.splice( this.cy , 1 ) ;

	if ( this.selectionRegion ) { this.updateSelectionFromCells() ; }

	return deleted ;
} ;



// TODOC
// Return a region where the searchString is found
TextBuffer.prototype.findNext = function( searchString , startPosition , reverse ) {
	var index , startAt , endAt ,
		text = this.getText() ,
		// /!\ another function MUST BE used once unicode composition will be supported
		// It is meant to produce the exact same cell size
		size = string.unicode.toArray( searchString ).length ;

	reverse = !! reverse ;

	if ( reverse ) {
		startPosition = startPosition ? startPosition - size : text.length - size ;
	}
	else {
		startPosition = startPosition ?? 0 ;
	}

	index = reverse ? text.lastIndexOf( searchString , startPosition ) :
		text.indexOf( searchString , startPosition ) ;

	if ( index === -1 ) { return ; }

	startAt = this.offsetToCoordinate( index ) ;
	endAt = this.offsetToCoordinate( index + size - 1 ) ;

	return {
		xmin: startAt.x ,
		ymin: startAt.y ,
		xmax: endAt.x ,
		ymax: endAt.y
	} ;
} ;



// TODOC
TextBuffer.prototype.findPrevious = function( searchString , startPosition ) {
	return this.findNext( searchString , startPosition , true ) ;
} ;



// TODOC
// Return a region where the regexp match, the region also have a 'match' property with the result of the regexp#exec().
// Can't be reversed due to how regexp works, except by searching for all match beforehand
TextBuffer.prototype.regexpFindNext = function( regexp , startPosition = 0 ) {
	var index , startAt , endAt , size , match ,
		text = this.getText() ;

	if ( typeof regexp === 'string' ) {
		regexp = new RegExp( regexp , 'gu' ) ;
	}
	else {
		// Force global and unicode
		regexp.global = true ;
		regexp.unicode = true ;
	}

	regexp.lastIndex = startPosition ;

	match = regexp.exec( text ) ;

	if ( ! match ) { return ; }

	// /!\ another function MUST BE used once unicode composition will be supported
	// It is meant to produce the exact same cell size
	size = string.unicode.toArray( match[ 0 ] ).length ;

	startAt = this.offsetToCoordinate( match.index ) ;
	endAt = this.offsetToCoordinate( match.index + size - 1 ) ;

	return {
		xmin: startAt.x ,
		ymin: startAt.y ,
		xmax: endAt.x ,
		ymax: endAt.y ,
		match
	} ;
} ;



/*
	A TextBuffer can only draw to a ScreenBuffer.
	To display it, you need to:
		- draw the TextBuffer to a ScreenBuffer
		- then draw that ScreenBuffer to the terminal
*/
TextBuffer.prototype.draw = function( options = {} ) {
	// Transmitted options (do not edit the user provided options, clone them)
	var tr = {
		dst: options.dst || this.dst ,
		offsetX: options.x !== undefined ? Math.floor( options.x ) : Math.floor( this.x ) ,
		offsetY: options.y !== undefined ? Math.floor( options.y ) : Math.floor( this.y ) ,
		dstClipRect: options.dstClipRect ? new termkit.Rect( options.dstClipRect ) : this.dstClipRect ,
		srcClipRect: options.srcClipRect ? new termkit.Rect( options.srcClipRect ) : undefined ,
		blending: options.blending ,
		wrap: options.wrap ,
		tile: options.tile
	} ;

	if ( tr.dst instanceof this.ScreenBuffer ) {
		this.blitter( tr ) ;

		if ( options.cursor ) {
			tr.dst.cx = this.cx + tr.offsetX ;
			tr.dst.cy = this.cy + tr.offsetY ;
		}
	}
} ;



TextBuffer.prototype.drawCursor = function( options ) {
	if ( ! options || typeof options !== 'object' ) { options = {} ; }

	var cx ,
		dst = options.dst || this.dst ;

	if ( dst instanceof this.ScreenBuffer ) {
		cx = this.cy ? this.cx : this.cx + this.firstLineRightShift ;

		if ( ! this.ch && ( this.dstClipRect || new termkit.Rect( this.dst ) ).isInside( cx + this.x , this.cy + this.y ) ) {
			dst.cx = cx + this.x ;
			dst.cy = this.cy + this.y ;
			dst.ch = false ;
		}
		else {
			dst.ch = true ;
		}
	}
} ;



TextBuffer.prototype.blitter = function( p ) {
	var tr , srcRect , srcClipRect , srcAltBuffer , iterator , iteratorCallback ;

	srcRect = new termkit.Rect( this ) ;

	if ( this.voidTextBuffer ) {
		srcAltBuffer = this.voidTextBuffer.buffer ;
		srcRect.merge( new termkit.Rect( this.voidTextBuffer ) ) ;
	}

	srcClipRect = p.srcClipRect || new termkit.Rect( srcRect ) ;

	// Default options & iterator
	tr = {
		type: 'line' ,
		context: {
			srcFirstLineRightShift: this.firstLineRightShift ,
			srcBuffer: this.buffer ,
			srcAltBuffer ,
			dstBuffer: p.dst.buffer ,
			forceChar: this.hidden ,
			voidAttr: this.voidAttr ,
			inverseRegion: this.selectionRegion ,
			writeAttr:
				this.ScreenBuffer === termkit.ScreenBuffer ?
					( dst , attr , offset ) => { dst.writeInt32BE( attr , offset ) ; } :
					( dst , attr , offset ) => { attr.copy( dst , offset ) ; }
		} ,
		dstRect: new termkit.Rect( p.dst ) ,
		srcRect ,
		dstClipRect: p.dstClipRect || new termkit.Rect( p.dst ) ,
		srcClipRect ,
		offsetX: p.offsetX ,
		offsetY: p.offsetY ,
		wrap: p.wrap ,
		tile: p.tile ,
		multiply: this.ScreenBuffer.prototype.ITEM_SIZE
	} ;

	iteratorCallback = this.blitterLineIterator.bind( this ) ;

	if ( p.wrap ) { iterator = 'wrapIterator' ; }
	else if ( p.tile ) { iterator = 'tileIterator' ; }
	else { iterator = 'regionIterator' ; }

	termkit.Rect[ iterator ]( tr , iteratorCallback ) ;
} ;



TextBuffer.prototype.blitterLineIterator = function( p ) {
	//console.error( "blitter line" , p.srcY ) ;
	var srcRShift , srcX , srcXmax , srcExistingXmax , dstOffset , cells , cell , attr , char , charCode ,
		invRegion = p.context.inverseRegion ,
		invXmin = Infinity ,
		invXmax = -Infinity ;

	//if ( ! global.deb ) { global.deb = [] ; }
	//global.deb.push( p ) ;

	srcRShift = p.srcY ? 0 : p.context.srcFirstLineRightShift ;
	srcX = p.srcXmin - srcRShift ;
	srcXmax = p.srcXmax - srcRShift ;
	dstOffset = p.dstStart ;

	cells = p.context.srcBuffer[ p.srcY ] ;

	if ( cells ) {
		//console.error( "  C1" ) ;
		srcExistingXmax = srcXmax ;

		if ( srcExistingXmax >= cells.length ) { srcExistingXmax = cells.length - 1 ; }

		if ( invRegion && p.srcY >= invRegion.ymin && p.srcY <= invRegion.ymax ) {
			invXmin = p.srcY === invRegion.ymin ? invRegion.xmin : -Infinity ;
			invXmax = p.srcY === invRegion.ymax ? invRegion.xmax : Infinity ;
		}

		// Write existing cells
		for ( ; srcX <= srcExistingXmax ; srcX ++ , dstOffset += this.ScreenBuffer.prototype.ITEM_SIZE ) {
			if ( srcX < 0 ) { continue ; }	// right-shifted
			cell = cells[ srcX ] ;

			if ( p.context.forceChar ) {
				// Use a forced character (i.e. hidden)
				attr = cell.attr ;
				char = p.context.forceChar ;
			}
			else {
				attr =
					cell.width === 2 ? cell.attr | this.ScreenBuffer.prototype.LEADING_FULLWIDTH :
					cell.width === 0 ? cell.attr | this.ScreenBuffer.prototype.TRAILING_FULLWIDTH :
					cell.attr ;
				char =
					( ( charCode = cell.char.charCodeAt( 0 ) ) < 0x20 || charCode === 0x7f ) ? ' ' :
					cell.char ;
			}

			if ( srcX >= invXmin && srcX <= invXmax ) { attr = this.ScreenBuffer.attrInverse( attr ) ; }

			// Write the attributes
			p.context.writeAttr( p.context.dstBuffer , attr , dstOffset ) ;
			// Write the char
			p.context.dstBuffer.write( char , dstOffset + this.ScreenBuffer.prototype.ATTR_SIZE , this.ScreenBuffer.prototype.CHAR_SIZE ) ;
		}
	}


	if ( p.context.srcAltBuffer ) {
		cells = p.context.srcAltBuffer[ p.srcY ] ;

		if ( cells ) {
			//console.error( "  C2" , srcX ) ;
			srcExistingXmax = srcXmax ;

			if ( srcExistingXmax >= cells.length ) { srcExistingXmax = cells.length - 1 ; }

			// Write existing cells
			for ( ; srcX <= srcExistingXmax ; srcX ++ , dstOffset += this.ScreenBuffer.prototype.ITEM_SIZE ) {
				if ( srcX < 0 ) { continue ; }	// right-shifted
				cell = cells[ srcX ] ;

				attr =
					cell.width === 2 ? cell.attr | this.ScreenBuffer.prototype.LEADING_FULLWIDTH :
					cell.width === 0 ? cell.attr | this.ScreenBuffer.prototype.TRAILING_FULLWIDTH :
					cell.attr ;
				char =
					( ( charCode = cell.char.charCodeAt( 0 ) ) < 0x20 || charCode === 0x7f ) ? ' ' :
					cell.char ;

				// Write the attributes
				p.context.writeAttr( p.context.dstBuffer , attr , dstOffset ) ;
				// Write the char
				p.context.dstBuffer.write( char , dstOffset + this.ScreenBuffer.prototype.ATTR_SIZE , this.ScreenBuffer.prototype.CHAR_SIZE ) ;
			}
		}
	}


	// Write blank
	// Temp?
	attr = p.context.voidAttr ;
	if ( attr !== null ) {
		for ( ; srcX <= srcXmax ; srcX ++ , dstOffset += this.ScreenBuffer.prototype.ITEM_SIZE ) {
			// Write the attributes
			p.context.writeAttr( p.context.dstBuffer , attr , dstOffset ) ;

			// Write the character
			p.context.dstBuffer.write( ' ' , dstOffset + this.ScreenBuffer.prototype.ATTR_SIZE , this.ScreenBuffer.prototype.CHAR_SIZE ) ;
		}
	}
} ;



// Naive loading
// Using callback is DEPRECATED.
TextBuffer.prototype.load = async function( path , callback ) {
	var content ;

	this.buffer[ 0 ] = [] ;
	this.buffer.length = 1 ;

	// Naive file loading, should be optimized later
	try {
		content = await fs.promises.readFile( path ) ;
	}
	catch ( error ) {
		if ( callback ) { callback( error ) ; return ; }
		throw error ;
	}

	this.setText( content.toString() ) ;

	if ( callback ) { callback() ; return ; }
} ;



// Naive saving
// Using callback is DEPRECATED.
TextBuffer.prototype.save = async function( path , callback ) {
	// Naive file saving, optimization are for later
	try {
		await fs.promises.writeFile( path , this.getText() ) ;
	}
	catch ( error ) {
		if ( callback ) { callback( error ) ; return ; }
		throw error ;
	}

	if ( callback ) { callback() ; return ; }
} ;





/* Utilities */



TextBuffer.prototype.object2attr = function( attrObject , colorNameToIndex = this.palette?.colorNameToIndex , legacyColor = false ) {
	return this.ScreenBuffer.object2attr( attrObject , colorNameToIndex , legacyColor ) ;
} ;



// TODOC
// A small utility function that returns the coordinate one step backward, if needed it point to the end of the previous line
TextBuffer.prototype.oneStepBackward = function( x = this.cx , y = this.cy ) {
	x -- ;

	if ( x < 0 ) {
		y -- ;
		if ( y < 0 ) { return null ; }
		x = this.buffer[ y ].length - 1 ;
	}

	return { x , y } ;
} ;





/* API for the text-machine module */



TextBuffer.prototype.runStateMachine = function() {
	if ( ! this.stateMachine ) { return ; }

	this.stateMachine.reset() ;

	this.iterate( { finalCall: true , fillerCopyAttr: true } , context => {
		context.textBuffer = this ;
		this.stateMachine.pushEvent( context.text , context ) ;
	} ) ;
} ;



const TextMachineApi = {} ;
TextBuffer.TextMachineApi = TextMachineApi ;



TextMachineApi.style = ( context , style ) => {
	if ( ! context || context.x === null ) { return ; }	// This is a newline or end of buffer character, there is no style to apply here
	if ( ! style.code ) { style.code = context.textBuffer.ScreenBuffer.object2attr( style ) ; }	// cache it now

	context.textBuffer.setAttrCodeAt( style.code , context.x , context.y ) ;
} ;



TextMachineApi.blockStyle = function( startingContext , endingContext , style ) {
	if ( ! startingContext || ! endingContext || startingContext.x === null || endingContext.x === null ) { return ; }
	if ( ! style.code ) { style.code = startingContext.textBuffer.ScreenBuffer.object2attr( style ) ; }	// cache it now

	startingContext.textBuffer.setAttrCodeRegion( style.code , {
		xmin: startingContext.x ,
		xmax: endingContext.x ,
		ymin: startingContext.y ,
		ymax: endingContext.y
	} ) ;
} ;



TextMachineApi.hint = function( context , buffer , hints ) {
	if ( ! context || context.x === null || context.y === null ) { return ; }

	if ( hints[ buffer ] ) {
		let misc_ = context.textBuffer.getMiscAt( context.x , context.y ) ;
		if ( misc_ ) { misc_.hint = hints[ buffer ] ; }
	}
} ;


},{"./misc.js":48,"./termkit.js":56,"fs":148,"string-kit":133}],7:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



/*
	Ideally, this should be done using a graph algorithm, but we will just brute-force it for instance...
*/

module.exports = function autoComplete( array , startString , returnAlternatives , prefix , postfix ) {
	var i , j , exitLoop , candidates = [] , completed = startString , hasCompleted = false ;

	if ( ! prefix ) { prefix = '' ; }
	if ( ! postfix ) { postfix = '' ; }

	for ( i = 0 ; i < array.length ; i ++ ) {
		if ( array[ i ].slice( 0 , startString.length ) === startString ) { candidates.push( array[ i ] ) ; }
	}

	if ( ! candidates.length ) { return prefix + completed + postfix ; }

	if ( candidates.length === 1 ) { return prefix + candidates[ 0 ] + postfix ; }


	// Multiple candidates, complete only the part they have in common

	j = startString.length ;

	exitLoop = false ;

	for ( j = startString.length ; j < candidates[ 0 ].length ; j ++ ) {
		for ( i = 1 ; i < candidates.length ; i ++ ) {
			if ( candidates[ i ][ j ] !== candidates[ 0 ][ j ] ) { exitLoop = true ; break ; }
		}

		if ( exitLoop ) { break ; }

		completed += candidates[ 0 ][ j ] ;
		hasCompleted = true ;
	}

	if ( returnAlternatives && ! hasCompleted ) {
		candidates.prefix = prefix ;
		candidates.postfix = postfix ;
		return candidates ;
	}

	return prefix + completed + postfix ;
} ;


},{}],8:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const spChars = require( './spChars.js' ) ;



/*
	bar( value , options )
		* value `number` the value to display as bar
		* options `object` of options, where:
			* innerSize `number` the inner width in characters (default: 10)
			* barStyle `function` the style of the bar, default to term.blue
			* str `boolean` (default: false) if true it outputs nothing, instead it returns a string
*/
module.exports = function( value , options ) {
	var str = '' , barString = '' ;

	options = options || {} ;

	if ( isNaN( value ) || value < 0 ) { value = 0 ; }
	else if ( value > 1 ) { value = 1 ; }

	var innerSize = options.innerSize || 10 ;
	var fullBlocks = Math.floor( value * innerSize ) ;
	var partialBlock = Math.round( ( value * innerSize - fullBlocks ) * 8 ) ;
	var barStyle = options.barStyle || this.blue ;

	barString += '█'.repeat( fullBlocks ) ;

	if ( fullBlocks < innerSize ) {
		barString += spChars.enlargingBlock[ partialBlock ] ;
		barString += ' '.repeat( innerSize - fullBlocks - 1 ) ;
	}

	if ( options.str ) {
		str += this.str.inverse( '▉' ) ;
		str += barStyle.str( barString ) ;
		str += this.str( '▏' ) ;
		return str ;
	}

	this.inverse( '▉' ) ;
	barStyle( barString ) ;
	this( '▏' ) ;

	return this ;
} ;


},{"./spChars.js":54}],9:[function(require,module,exports){
(function (global){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



module.exports = require( './termkit-no-lazy-require.js' ) ;
global.IS_BROWSER = true ;


}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./termkit-no-lazy-require.js":55}],10:[function(require,module,exports){
module.exports=[
	{ "r": 0, "g": 0, "b": 0, "names": [ "black" ] } ,
	{ "r": 180, "g": 0, "b": 0, "names": [ "red" ] } ,
	{ "r": 0, "g": 180, "b": 0, "names": [ "green" ] } ,
	{ "r": 180, "g": 180, "b": 0, "names": [ "yellow" ] } ,
	{ "r": 0, "g": 0, "b": 180, "names": [ "blue" ] } ,
	{ "r": 180, "g": 0, "b": 180, "names": [ "magenta" ] } ,
	{ "r": 0, "g": 180, "b": 180, "names": [ "cyan" ] } ,
	{ "r": 220, "g": 220, "b": 220, "names": [ "white" ] } ,
	{ "r": 55, "g": 55, "b": 55, "names": [ "brightBlack" , "gray" , "grey" ] } ,
	{ "r": 250, "g": 0, "b": 0, "names": [ "brightRed" ] } ,
	{ "r": 0, "g": 250, "b": 0, "names": [ "brightGreen" ] } ,
	{ "r": 250, "g": 250, "b": 0, "names": [ "brightYellow" ] } ,
	{ "r": 0, "g": 0, "b": 250, "names": [ "brightBlue" ] } ,
	{ "r": 250, "g": 0, "b": 250, "names": [ "brightMagenta" ] } ,
	{ "r": 0, "g": 250, "b": 250, "names": [ "brightCyan" ] } ,
	{ "r": 250, "g": 250, "b": 250, "names": [ "brightWhite" ] }
]
},{}],11:[function(require,module,exports){
module.exports=[
	{ "r": 0, "g": 0, "b": 0, "names": [ "black" ] } ,
	{ "r": 204, "g": 0, "b": 0, "names": [ "red" ] } ,
	{ "r": 78, "g": 154, "b": 6, "names": [ "green" ] } ,
	{ "r": 196, "g": 160, "b": 0, "names": [ "yellow" ] } ,
	{ "r": 52, "g": 101, "b": 164, "names": [ "blue" ] } ,
	{ "r": 117, "g": 80, "b": 123, "names": [ "magenta" ] } ,
	{ "r": 6, "g": 152, "b": 154, "names": [ "cyan" ] } ,
	{ "r": 211, "g": 215, "b": 207, "names": [ "white" ] } ,
	{ "r": 85, "g": 87, "b": 83, "names": [ "brightBlack" , "gray" , "grey" ] } ,
	{ "r": 239, "g": 41, "b": 41, "names": [ "brightRed" ] } ,
	{ "r": 138, "g": 226, "b": 52, "names": [ "brightGreen" ] } ,
	{ "r": 252, "g": 233, "b": 79, "names": [ "brightYellow" ] } ,
	{ "r": 114, "g": 159, "b": 207, "names": [ "brightBlue" ] } ,
	{ "r": 173, "g": 127, "b": 168, "names": [ "brightMagenta" ] } ,
	{ "r": 52, "g": 226, "b": 226, "names": [ "brightCyan" ] } ,
	{ "r": 238, "g": 238, "b": 236, "names": [ "brightWhite" ] }
]
},{}],12:[function(require,module,exports){
(function (process){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Promise = require( 'seventh' ) ;
const exec = require( 'child_process' ).exec ;
const path = require( 'path' ) ;
const os = require( 'os' ) ;

const termkit = require( './termkit.js' ) ;



// Try to guess the terminal without any async system call, using TERM and COLORTERM.
// Argument 'unpipe' is used when we will get a TTY even if we haven't one ATM.
exports.guessTerminal = function( unpipe ) {
	var envVar , version ;

	var isSSH = !! process.env.SSH_CONNECTION ;
	var isTTY = !! process.stdout.isTTY ;

	if ( ! isTTY && ! unpipe ) {
		return {
			isTTY: isTTY ,
			isSSH: isSSH ,
			appId: 'none' ,
			safe: true ,
			generic: 'none'
		} ;
	}

	var platform = os.platform() ;
	var t256color = ( process.env.TERM && process.env.TERM.match( /256/ ) ) ||
		( process.env.COLORTERM && process.env.COLORTERM.match( /256/ ) ) ;
	var tTrueColor = process.env.COLORTERM && process.env.COLORTERM.match( /^(truecolor|24bits?)$/ ) ;

	var appId =
		process.env.COLORTERM && ! tTrueColor ? process.env.COLORTERM :
		process.env.TERM_PROGRAM ? process.env.TERM_PROGRAM :
		process.env.TERM ;

	if ( platform === 'darwin' ) {
		appId = path.parse( appId ).name ;
	}
	else if ( platform === 'android' && process.env.TERMUX_VERSION ) {
		appId = 'termux' ;
	}

	// safe is true if we are sure about our guess
	var safe =
		appId !== process.env.TERM
		|| ( process.env.TERM && process.env.TERM !== 'xterm' && process.env.TERM !== 'xterm-256color' ) ;

	var generic = appId ;

	switch ( appId ) {
		case 'xterm' :
		case 'xterm-256color' :
			if ( safe ) { break ; }

			if ( tTrueColor ) {
				appId = generic = 'xterm-truecolor' ;
			}

			// Many terminal advertise them as xterm, we will try to guess some of them here,
			// using environment variable
			if ( process.env.VTE_VERSION ) {
				version = parseInt( process.env.VTE_VERSION , 10 ) ;

				if ( version >= 3803 ) {
					appId = t256color || tTrueColor ? 'gnome-256color' : 'gnome' ;
					safe = true ;
					break ;
				}
			}

			// BTW OSX terminals advertise them as xterm, while having their own key mapping...
			if ( platform === 'darwin' ) {
				appId = 'osx-256color' ;
				break ;
			}

			for ( envVar in process.env ) {
				if ( envVar.match( /KONSOLE/ ) ) {
					appId = t256color || tTrueColor ? 'konsole-256color' : 'konsole' ;
					safe = true ;
					break ;
				}
			}

			break ;

		case 'linux' :
		case 'aterm' :
		case 'kuake' :
		case 'tilda' :
		case 'terminology' :
		case 'wterm' :
		case 'mrxvt' :
			break ;

		case 'gnome' :
		case 'gnome-256color' :
		case 'gnome-terminal' :
		case 'gnome-terminal-256color' :
		case 'terminator' :	// it uses gnome terminal lib
		case 'guake' :	// same here
			appId = t256color || tTrueColor ? 'gnome-256color' : 'gnome' ;
			break ;
		case 'konsole' :
			appId = t256color || tTrueColor ? 'konsole-256color' : 'konsole' ;
			break ;
		case 'rxvt' :
		case 'rxvt-xpm' :
		case 'rxvt-unicode-256color' :
		case 'urxvt256c' :
		case 'urxvt256c-ml' :
		case 'rxvt-unicode' :
		case 'urxvt' :
		case 'urxvt-ml' :
			if ( process.env.TERM === 'rxvt' ) { appId = 'rxvt-256color' ; }
			else { appId = t256color || tTrueColor ? 'rxvt-256color' : 'rxvt' ; }
			break ;
		case 'xfce' :
		case 'xfce-terminal' :
		case 'xfce4-terminal' :
			appId = 'xfce' ;
			break ;
		case 'eterm' :
		case 'Eterm' :
			appId = t256color || tTrueColor ? 'eterm-256color' : 'eterm' ;
			break ;
		case 'atomic-terminal' :
			appId = 'atomic-terminal' ;
			break ;
		case 'xterm-kitty' :
		case 'kitty' :
			appId = 'kitty' ;
			break ;

			// OSX Terminals

		case 'iTerm' :
		case 'iterm' :
		case 'iTerm2' :
		case 'iterm2' :
		case 'Terminal' :
		case 'terminal' :
		case 'Apple_Terminal' :
			appId = 'osx-256color' ;
			break ;

			// Android

		case 'termux' :
			break ;

		default :
			if ( ! appId ) { generic = 'unknown' ; }
			else { generic = appId = generic.toLowerCase() ; }
			break ;
	}

	return {
		isTTY , isSSH , appId , safe , generic: safe ? appId : generic
	} ;
} ;



function getParentProcess( pid ) {
	var parentPid , appName ;

	return new Promise( ( resolve , reject ) => {
		exec( 'ps -h -o ppid -p ' + pid , ( error , stdout ) => {
			if ( error ) { reject( error ) ; return ; }

			parentPid = parseInt( stdout.match(  /[0-9]+/gm )[ 0 ] , 10 ) ;
			//console.error( "--- Parent PID: " , parentPid , stdout.match(  /[0-9]+/gm ) ) ;
			if( ! parentPid ) { reject( new Error( "Couldn't get parent PID" ) ) ; return ; }

			exec( 'ps -h -o comm -p ' + parentPid , ( error_ , stdout_ ) => {
				if ( error_ ) { reject( error_ ) ; return ; }

				appName = stdout_.trim() ;
				//console.error( "+++ appName: " , appName ) ;
				resolve( { pid: parentPid , appName } ) ;
			} ) ;
		} ) ;
	} ) ;
}



// Work localy, do not work over SSH
exports.getParentTerminalInfo = async function( callback ) {
	var loopAgain , error , appName , appNames = [] , appId , pid = process.pid ;

	if ( process.env.SSH_CONNECTION ) {
		error = new Error( 'SSH connection detected, .getParentTerminalInfo() is useless in this context.' ) ;
		if ( callback ) { callback( error ) ; return ; }
		throw error ;
	}

	var platform = os.platform() ;
	var t256color = ( process.env.TERM && process.env.TERM.match( /256/ ) ) ||
		( process.env.COLORTERM && process.env.COLORTERM.match( /256/ ) ) ;
	var tTrueColor = process.env.COLORTERM && process.env.COLORTERM.match( /^(truecolor|24bits?)$/ ) ;

	try {
		loopAgain = true ;

		while ( loopAgain ) {
			( { appName , pid } = await getParentProcess( pid ) ) ;

			//console.log( 'found:' , appName , pid ) ;

			if ( platform === 'darwin' ) { appName = path.parse( appName ).name ; }
			appNames.push( appName ) ;

			// Do NOT skip the first, there are case where the terminal may run directly node.js without any shell in between
			//if ( ++ loop <= 1 ) { asyncCallback( undefined , true ) ; return ; }

			loopAgain = false ;

			switch ( appName ) {
				case 'linux' :
				case 'xterm' :
				case 'konsole' :
				case 'gnome-terminal' :
				case 'aterm' :
				case 'guake' :
				case 'kuake' :
				case 'tilda' :
				case 'terminology' :
				case 'wterm' :
				case 'mrxvt' :
					appId = t256color || tTrueColor ? appName + '-256color' : appName ;
					break ;
				case 'atomic-terminal' :
					appId = appName ;
					break ;
				case 'login' :
					appName = 'linux' ;
					appId = appName ;
					break ;
				// Use terminator as gnome-terminal, since it uses the gnome-terminal renderer
				case 'terminator' :
					appId = t256color || tTrueColor ? 'gnome-256color' : 'gnome' ;
					break ;
				// Use rxvt as xterm-256color
				case 'rxvt' :
				case 'urxvt256c' :
				case 'urxvt256c-ml' :
					appId = 'rxvt-256color' ;
					break ;
				// Use rxvt as xterm
				case 'urxvt' :
				case 'urxvt-ml' :
					appId = 'rxvt' ;
					break ;
				// xfce4-terminal
				case 'xfce4-terminal' :
					appId = 'xfce' ;
					break ;
				case 'gnome-terminal-' :
					appName = 'gnome-terminal' ;
					appId = t256color || tTrueColor ? 'gnome-256color' : 'gnome' ;
					break ;
				case 'Eterm' :
				case 'eterm' :
					appName = 'Eterm' ;
					appId = t256color || tTrueColor ? 'eterm-256color' : 'eterm' ;
					break ;
				case 'kitty' :
					appName = appId = 'kitty' ;
					break ;

					// OSX Terminals

				case 'iTerm' :
				case 'iTerm2' :
				case 'Terminal' :
					appId = 'osx-256color' ;
					break ;

				default :
					if ( appName.match( /gnome-terminal/ ) ) {
						appName = 'gnome-terminal' ;
						appId = t256color || tTrueColor ? 'gnome-256color' : 'gnome' ;
						break ;
					}

					if ( ! pid || pid === 1 ) {
						throw new Error( 'Terminal not found, app names: ' + appNames.join( ', ' ) ) ;
					}

					loopAgain = true ;
			}
		}
	}
	catch ( error_ ) {
		if ( callback ) { callback( error_ ) ; return ; }
		throw error_ ;
	}

	var result = {
		appId: appId ,
		appName: appName ,
		pid: pid ,
		safe: true
	} ;

	if ( callback ) { callback( undefined , result ) ; return ; }

	return result ;
} ;



// Work locally, do not work over SSH
exports.getDetectedTerminal = async function( callback ) {
	var terminal , info ,
		guessed = termkit.guessTerminal() ;

	if ( guessed.safe || guessed.isSSH ) {
		// If we have a good guess, use it now
		terminal = termkit.createTerminal( {
			stdin: process.stdin ,
			stdout: process.stdout ,
			stderr: process.stderr ,
			generic: ( process.env.TERM && process.env.TERM.toLowerCase() ) || 'unknown' ,
			appId: guessed.safe ? guessed.appId : undefined ,
			//  appName: guessed.safe ? guessed.appName : undefined ,
			isTTY: guessed.isTTY ,
			isSSH: guessed.isSSH ,
			processSigwinch: true ,
			preferProcessSigwinch: !! termkit.globalConfig.preferProcessSigwinch
		} ) ;

		if ( callback ) { callback( undefined , terminal ) ; }
		return terminal ;
	}

	try {
		info = await termkit.getParentTerminalInfo() ;

		terminal = termkit.createTerminal( {
			stdin: process.stdin ,
			stdout: process.stdout ,
			stderr: process.stderr ,
			generic: ( process.env.TERM && process.env.TERM.toLowerCase() ) || 'unknown' ,
			appId: info.appId ,
			appName: info.appName ,
			isTTY: guessed.isTTY ,
			isSSH: guessed.isSSH ,
			pid: info.pid ,
			processSigwinch: true ,
			preferProcessSigwinch: !! termkit.globalConfig.preferProcessSigwinch
		} ) ;
	}
	catch ( error ) {
		// Do not issue error
		terminal = termkit.createTerminal( {
			stdin: process.stdin ,
			stdout: process.stdout ,
			stderr: process.stderr ,
			generic: ( process.env.TERM && process.env.TERM.toLowerCase() ) || 'unknown' ,
			appId: guessed.safe ? guessed.appId : undefined ,
			//  appName: guessed.safe ? guessed.appName : undefined ,
			isTTY: guessed.isTTY ,
			isSSH: guessed.isSSH ,
			processSigwinch: true ,
			preferProcessSigwinch: !! termkit.globalConfig.preferProcessSigwinch
		} ) ;
	}

	if ( callback ) { callback( undefined , terminal ) ; }
	return terminal ;
} ;


}).call(this)}).call(this,require('_process'))
},{"./termkit.js":56,"_process":191,"child_process":148,"os":178,"path":190,"seventh":114}],13:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const Text = require( './Text.js' ) ;
const spChars = require( '../spChars.js' ) ;



function AnimatedText( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	options.attr = options.attr || {} ;

	if ( Array.isArray( options.animation ) ) {
		this.animation = options.animation ;
	}
	else if ( typeof options.animation === 'string' ) {
		this.animation = spChars.animation[ options.animation ] || spChars.animation.lineSpinner ;
		if ( options.contentHasMarkup !== false ) { options.contentHasMarkup = true ; }
	}
	else {
		this.animation = spChars.animation.lineSpinner ;
	}

	this.animation = this.animation.map( e => Array.isArray( e ) ? e : [ e ] ) ;

	this.isAnimated = false ;
	this.frameDuration = options.frameDuration || 150 ;
	this.animationSpeed = options.animationSpeed || 1 ;
	this.frame = options.frame || 0 ;
	this.autoUpdateTimer = null ;

	this.autoUpdate = this.autoUpdate.bind( this ) ;

	options.content = this.animation[ this.frame ] ;

	Text.call( this , options ) ;

	if ( this.elementType === 'AnimatedText' && ! options.noDraw ) {
		this.draw() ;
		this.animate() ;
	}
}

module.exports = AnimatedText ;
Element.inherit( AnimatedText , Text ) ;



AnimatedText.prototype.inlineCursorRestoreAfterDraw = true ;



AnimatedText.prototype.animate = function( animationSpeed = 1 ) {
	this.isAnimated = !! animationSpeed ;
	this.animationSpeed = + animationSpeed || 0 ;

	if ( ! this.isAnimated ) {
		if ( this.autoUpdateTimer ) { clearTimeout( this.autoUpdateTimer ) ; }
		this.autoUpdateTimer = null ;
		return ;
	}

	if ( ! this.autoUpdateTimer ) {
		this.autoUpdateTimer = setTimeout( () => this.autoUpdate() , this.frameDuration / this.animationSpeed ) ;
	}
} ;



AnimatedText.prototype.autoUpdate = function() {
	this.frame = ( this.frame + 1 ) % this.animation.length ;
	this.content = this.animation[ this.frame ] ;
	this.draw() ;
	this.autoUpdateTimer = setTimeout( () => this.autoUpdate() , this.frameDuration / this.animationSpeed ) ;
} ;


},{"../spChars.js":54,"./Element.js":25,"./Text.js":37}],14:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const builtinBarChars = require( '../spChars.js' ).bar ;



function Bar( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	Element.call( this , options ) ;

	this.minValue = + options.minValue || 0 ;
	this.maxValue = options.maxValue !== undefined ? + options.maxValue || 0 : 1 ;
	this.value = + options.value || 0 ;

	if ( this.value < this.minValue ) { this.value = this.minValue ; }
	else if ( this.value > this.maxValue ) { this.value = this.maxValue ; }

	this.borderAttr = options.borderAttr || { bold: true } ;
	this.bodyAttr = options.bodyAttr || { color: 'blue' } ;
	this.barChars = builtinBarChars.classic ;

	if ( typeof options.barChars === 'object' ) {
		this.barChars = options.barChars ;
	}
	else if ( typeof options.barChars === 'string' && builtinBarChars[ options.barChars ] ) {
		this.barChars = builtinBarChars[ options.barChars ] ;
	}

	this.overTextFullAttr = options.overTextFullAttr || { bgColor: 'blue' } ;
	this.overTextEmptyAttr = options.overTextEmptyAttr || { bgColor: 'default' } ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'Bar' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Bar ;
Element.inherit( Bar ) ;



Bar.prototype.preDrawSelf = function() {
	var index , x , fullCells , emptyCells , partialCellRate ,
		fullContent , fullContentWidth = 0 , emptyContent , emptyContentWidth = 0 ,
		noPartialCell = false ,
		partialCell = null ,
		innerSize = this.outputWidth - 2 ,
		rate = ( this.value - this.minValue ) / ( this.maxValue - this.minValue ) ;

	if ( ! rate || rate < 0 ) { rate = 0 ; }
	else if ( rate > 1 ) { rate = 1 ; }

	fullCells = Math.floor( rate * innerSize ) ;
	partialCellRate = rate * innerSize - fullCells ;

	if ( this.content ) {
		if ( partialCellRate < 0.5 ) {
			fullContent = Element.truncateContent( this.content , fullCells , this.contentHasMarkup ) ;
			fullContentWidth = Element.getLastTruncateWidth() ;
			if ( fullContentWidth < this.contentWidth ) { noPartialCell = true ; }
		}
		else {
			fullContent = Element.truncateContent( this.content , fullCells + 1 , this.contentHasMarkup ) ;
			fullContentWidth = Element.getLastTruncateWidth() ;

			if ( fullContentWidth < this.contentWidth || fullContentWidth === fullCells + 1 ) {
				noPartialCell = true ;
				fullCells ++ ;
			}
		}
	}


	if ( ! noPartialCell && fullCells < innerSize ) {
		if ( this.barChars.body.length <= 2 ) {
			// There is no chars for partial cells
			if ( partialCellRate >= 0.5 ) { fullCells ++ ; }
		}
		else if ( this.barChars.body.length === 3 ) {
			partialCell = this.barChars.body[ 1 ] ;
		}
		else if ( this.barChars.body.length === 4 ) {
			partialCell = this.barChars.body[ partialCellRate < 0.5 ? 1 : 2 ] ;
		}
		else {
			index = Math.floor( 1.5 + partialCellRate * ( this.barChars.body.length - 3 ) ) ;
			partialCell = this.barChars.body[ index ] ;
		}
	}

	emptyCells = innerSize - fullCells - ( partialCell ? 1 : 0 ) ;

	if ( this.content && fullContentWidth < this.contentWidth ) {
		emptyContent = Element.truncateContent( this.content.slice( fullContent.length ) , emptyCells , this.contentHasMarkup ) ;
		emptyContentWidth = Element.getLastTruncateWidth() ;
	}


	// Rendering...

	x = this.outputX ;
	this.outputDst.put( {
		x: x ++ , y: this.outputY , attr: this.borderAttr , markup: true
	} , this.barChars.border[ 0 ] ) ;

	if ( fullContentWidth ) {
		this.outputDst.put( {
			x: x , y: this.outputY , attr: this.overTextFullAttr , markup: true
		} , fullContent ) ;
		x += fullContentWidth ;
	}

	if ( fullCells - fullContentWidth > 0 ) {
		this.outputDst.put( {
			x: x , y: this.outputY , attr: this.bodyAttr , markup: true
		} , this.barChars.body[ 0 ].repeat( fullCells - fullContentWidth ) ) ;
		x += fullCells - fullContentWidth ;
	}

	if ( partialCell ) {
		this.outputDst.put( {
			x: x ++ , y: this.outputY , attr: this.bodyAttr , markup: true
		} , partialCell ) ;
	}

	if ( emptyContentWidth ) {
		this.outputDst.put( {
			x: x , y: this.outputY , attr: this.overTextEmptyAttr , markup: true
		} , emptyContent ) ;
		x += emptyContentWidth ;
	}

	if ( emptyCells - emptyContentWidth > 0 ) {
		this.outputDst.put( {
			x: x , y: this.outputY , attr: this.bodyAttr , markup: true
		} , this.barChars.body[ this.barChars.body.length - 1 ].repeat( emptyCells - emptyContentWidth ) ) ;
		x += emptyCells - emptyContentWidth ;
	}

	this.outputDst.put( {
		x: x , y: this.outputY , attr: this.borderAttr , markup: true
	} , this.barChars.border[ 1 ] ) ;
} ;



Bar.prototype.getValue = function() { return this.value ; } ;

Bar.prototype.setValue = function( value , internalAndNoDraw ) {
	this.value = + value || 0 ;

	if ( this.value < this.minValue ) { this.value = this.minValue ; }
	else if ( this.value > this.maxValue ) { this.value = this.maxValue ; }

	if ( ! internalAndNoDraw ) { this.draw() ; }
} ;


},{"../spChars.js":54,"./Element.js":25}],15:[function(require,module,exports){
(function (process){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



/* Base class for menus (ColumnMenu, RowMenu, etc) */



const tree = require( 'tree-kit' ) ;

const Element = require( './Element.js' ) ;
const Button = require( './Button.js' ) ;



function BaseMenu( options = {} ) {
	Element.call( this , options ) ;

	this.backgroundAttr = options.backgroundAttr || { bgColor: 'white' , color: 'black' } ;
	this.contentEllipsis = options.contentEllipsis === true ? '…' : options.contentEllipsis || '…' ;
	this.previousPageContent = options.previousPageContent || '«' ;
	this.previousPageContentHasMarkup = !! options.previousPageContentHasMarkup ;
	this.nextPageContent = options.nextPageContent || '»' ;
	this.nextPageContentHasMarkup = !! options.nextPageContentHasMarkup ;
	this.itemsDef = options.items || [] ;
	this.previousPageDef = options.previousPage ;
	this.nextPageDef = options.nextPage ;
	this.masterDef = options.master ;
	this.separatorDef = options.separator ;
	this.buttons = [] ;
	this.hotkeyToButtonIndex = new Map() ;
	this.focusChild = null ;

	// Pagination
	this.page = 0 ;
	this.maxPage = 0 ;

	// Submenu
	this.hasSubmenu = !! options.submenu ;
	this.isSubmenu = !! options.isSubmenu ;
	this.submenu = null ;	// A child (column) menu
	this.submenuParentButton = null ;	// The button that opened the submenu
	this.submenuOptions = null ;

	if ( this.hasSubmenu ) {
		// Use tree-kit because 'options' comes from an Object.create() and has almost no owned properties
		this.submenuOptions = tree.extend( null , {} , options , {
			// Things to clear or to force
			internal: true ,
			parent: null ,
			items: null
			//x: undefined , outputX: undefined ,
			//y: undefined , outputY: undefined ,
			//width: undefined , outputWidth: undefined ,
			//height: undefined , outputHeight: undefined ,
			//submenu: false
		} ) ;

		if ( options.submenu && typeof options.submenu === 'object' ) {
			Object.assign( this.submenuOptions , options.submenu ) ;
		}
	}

	this.onButtonSubmit = this.onButtonSubmit.bind( this ) ;
	this.onButtonToggle = this.onButtonToggle.bind( this ) ;
	this.onButtonFocus = this.onButtonFocus.bind( this ) ;
	this.onButtonBlinked = this.onButtonBlinked.bind( this ) ;
	this.onSubmenuSubmit = this.onSubmenuSubmit.bind( this ) ;
	this.onWheel = this.onWheel.bind( this ) ;
	this.onFocus = this.onFocus.bind( this ) ;

	// Global default attributes
	this.buttonBlurAttr = options.buttonBlurAttr || this.defaultOptions.buttonBlurAttr || { bgColor: 'black' , color: 'white' , bold: true } ;
	this.buttonEvenBlurAttr = options.buttonEvenBlurAttr || null ;
	this.buttonFocusAttr = options.buttonFocusAttr || this.defaultOptions.buttonFocusAttr || { bgColor: 'white' , color: 'black' , bold: true } ;
	this.buttonDisabledAttr = options.buttonDisabledAttr || this.defaultOptions.buttonDisabledAttr || { bgColor: 'black' , color: 'gray' , bold: true } ;
	this.buttonSubmittedAttr = options.buttonSubmittedAttr || this.defaultOptions.buttonSubmittedAttr || { bgColor: 'gray' , color: 'brightWhite' , bold: true } ;
	this.buttonTurnedOnBlurAttr = options.buttonTurnedOnBlurAttr || this.defaultOptions.buttonTurnedOnBlurAttr || { bgColor: 'cyan' } ;
	this.buttonTurnedOnFocusAttr = options.buttonTurnedOnFocusAttr || this.defaultOptions.buttonTurnedOnFocusAttr || { bgColor: 'brightCyan' , color: 'gray' , bold: true } ;
	this.buttonTurnedOffBlurAttr = options.buttonTurnedOffBlurAttr || this.defaultOptions.buttonTurnedOffBlurAttr || { bgColor: 'gray' , dim: true } ;
	this.buttonTurnedOffFocusAttr = options.buttonTurnedOffFocusAttr || this.defaultOptions.buttonTurnedOffFocusAttr || { bgColor: 'white' , color: 'black' , bold: true } ;

	// Padding
	this.blurLeftPadding = options.blurLeftPadding ?? options.leftPadding ?? '' ;
	this.blurRightPadding = options.blurRightPadding ?? options.rightPadding ?? '' ;
	this.focusLeftPadding = options.focusLeftPadding ?? options.leftPadding ?? '' ;
	this.focusRightPadding = options.focusRightPadding ?? options.rightPadding ?? '' ;
	this.disabledLeftPadding = options.disabledLeftPadding ?? options.leftPadding ?? '' ;
	this.disabledRightPadding = options.disabledRightPadding ?? options.rightPadding ?? '' ;
	this.submittedLeftPadding = options.submittedLeftPadding ?? options.leftPadding ?? '' ;
	this.submittedRightPadding = options.submittedRightPadding ?? options.rightPadding ?? '' ;
	this.turnedOnBlurLeftPadding = options.turnedOnBlurLeftPadding ?? options.turnedOnLeftPadding ?? this.blurLeftPadding ;
	this.turnedOnBlurRightPadding = options.turnedOnBlurRightPadding ?? options.turnedOnRightPadding ?? this.blurRightPadding ;
	this.turnedOffBlurLeftPadding = options.turnedOffBlurLeftPadding ?? options.turnedOffLeftPadding ?? this.blurLeftPadding ;
	this.turnedOffBlurRightPadding = options.turnedOffBlurRightPadding ?? options.turnedOffRightPadding ?? this.blurRightPadding ;
	this.turnedOnFocusLeftPadding = options.turnedOnFocusLeftPadding ?? options.turnedOnLeftPadding ?? this.focusLeftPadding ;
	this.turnedOnFocusRightPadding = options.turnedOnFocusRightPadding ?? options.turnedOnRightPadding ?? this.focusRightPadding ;
	this.turnedOffFocusLeftPadding = options.turnedOffFocusLeftPadding ?? options.turnedOffLeftPadding ?? this.focusLeftPadding ;
	this.turnedOffFocusRightPadding = options.turnedOffFocusRightPadding ?? options.turnedOffRightPadding ?? this.focusRightPadding ;
	this.paddingHasMarkup = !! options.paddingHasMarkup ;

	if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; }
	if ( options.buttonKeyBindings ) { this.buttonKeyBindings = options.buttonKeyBindings ; }
	if ( options.buttonActionKeyBindings ) { this.buttonActionKeyBindings = options.buttonActionKeyBindings ; }
	if ( options.toggleButtonKeyBindings ) { this.toggleButtonKeyBindings = options.toggleButtonKeyBindings ; }
	if ( options.toggleButtonActionKeyBindings ) { this.toggleButtonActionKeyBindings = options.toggleButtonActionKeyBindings ; }

	this.on( 'key' , this.onKey ) ;
	this.on( 'wheel' , this.onWheel ) ;
	this.on( 'focus' , this.onFocus ) ;
}

module.exports = BaseMenu ;
Element.inherit( BaseMenu ) ;



BaseMenu.prototype.needInput = true ;



BaseMenu.prototype.destroy = function( isSubDestroy , noDraw = false ) {
	if ( this.destroyed ) { return ; }
	if ( this.submenu ) { this.submenu.destroy( true ) ; }

	Element.prototype.destroy.call( this , isSubDestroy , noDraw ) ;
} ;



BaseMenu.prototype.focusFirstPageItem = function( focusType ) {
	this.focusChild = this.children[ this.page ? 1 : 0 ] ;
	var focusAware = this.document.giveFocusTo_( this.focusChild , focusType ) ;
	if ( ! focusAware ) { this.document.focusNext() ; }
} ;



BaseMenu.prototype.focusLastPageItem = function( focusType ) {
	this.focusChild = this.children[  this.page === this.maxPage  ?  this.children.length - 1  :  this.children.length - 2  ] ;
	var focusAware = this.document.giveFocusTo_( this.focusChild , focusType ) ;
	if ( ! focusAware ) { this.document.focusNext() ; }
} ;



BaseMenu.prototype.previousPage = function( focusType ) {
	if ( this.maxPage && this.page > 0 ) {
		this.page -- ;
		this.initPage() ;
		this.focusChild = this.children[ this.children.length - 2 ] ;
		let focusAware = this.document.giveFocusTo_( this.focusChild , focusType ) ;
		if ( ! focusAware ) { this.document.focusPrevious() ; }
		this.updateDraw() ;
		this.emit( 'previousPage' , this.page , this ) ;
	}
} ;



BaseMenu.prototype.nextPage = function( focusType ) {
	if ( this.maxPage && this.page < this.maxPage ) {
		this.page ++ ;
		this.initPage() ;
		this.focusChild = this.children[ 1 ] ;
		let focusAware = this.document.giveFocusTo_( this.focusChild , focusType ) ;
		if ( ! focusAware ) { this.document.focusNext() ; }
		this.updateDraw() ;
		this.emit( 'nextPage' , this.page , this ) ;
	}
} ;



BaseMenu.prototype.toPage = function( page , focusType ) {
	if ( this.maxPage && page !== this.page ) {
		this.page = page ;
		this.initPage() ;
		this.focusChild = this.children[ this.page ? 1 : 0 ] ;
		let focusAware = this.document.giveFocusTo_( this.focusChild , focusType ) ;
		if ( ! focusAware ) { this.document.focusNext() ; }
		this.updateDraw() ;
	}
} ;



BaseMenu.prototype.focusValue = function( itemValue , focusType , forceInitPage = false ) {
	var focusAware , itemDef , item ;

	itemDef = this.itemsDef.find( it => ! it.disabled && it.value === itemValue ) ;
	if ( ! itemDef ) { return ; }

	if ( this.page !== itemDef.page || forceInitPage ) {
		this.page = itemDef.page ;
		this.initPage() ;
	}

	item = this.buttons.find( it => it.def === itemDef ) ;
	if ( ! item ) { return ; }	// Not possible, but well...

	this.focusChild = item ;

	focusAware = this.document.giveFocusTo_( this.focusChild , focusType ) ;
	if ( ! focusAware ) { this.document.focusNext() ; }

	this.draw() ;
} ;



BaseMenu.prototype.setItem = function( itemValue , itemOptions ) {
	var itemDef , focusValue ;

	itemDef = this.itemsDef.find( it => it.value === itemValue ) ;
	if ( ! itemDef ) { return false ; }

	Object.assign( itemDef , itemOptions ) ;

	focusValue = this.focusChild && this.focusChild.value ;

	this.initChildren( true ) ;

	if ( focusValue !== undefined ) {
		// Note: .focusValue() call .draw() behind the scene, and last argument force a .initPage() call
		this.focusValue( focusValue , 'refocus' , true ) ;
	}
	else {
		this.initPage() ;
		this.draw() ;
	}

	return true ;
} ;



BaseMenu.prototype.onWheel = function( data ) {
	if ( data.yDirection < 0 ) { this.previousPage( 'backCycle' ) ; }
	else if ( data.yDirection > 0 ) { this.nextPage( 'cycle' ) ; }
} ;



BaseMenu.prototype.onFocus = function( focus , type ) {
	if ( type === 'cycle' || type === 'backCycle' ) { return ; }
	//if ( type === 'backCycle' ) { return ; }

	if ( focus ) {
		// Defer to the next tick to avoid recursive events producing wrong listener order
		process.nextTick( () => {
			let forceType = type === 'clear' ? type : undefined ;

			if ( this.focusChild && ! this.focusChild.destroyed ) {
				this.document.giveFocusTo( this.focusChild , forceType || 'delegate' ) ;
			}
			else {
				this.focusChild = this.focusNextChild( undefined , forceType ) ;
			}
		} ) ;
	}
} ;



BaseMenu.prototype.onButtonSubmit = function( buttonValue , action , button ) {
	switch ( button.internalRole ) {
		case 'previousPage' :
			this.previousPage() ;
			break ;
		case 'nextPage' :
			this.nextPage() ;
			break ;
		default :
			if ( this.hasSubmenu && button.def.items ) {
				if ( this.submenuOptions.openOn === 'parentSubmit' ) {
					this.openSubmenu( button.value , button ) ;
				}

				if ( this.submenu ) {
					this.document.giveFocusTo( this.submenu ) ;
				}
			}
			else {
				this.emit( 'submit' , buttonValue , action , this , button ) ;
			}
	}
} ;



BaseMenu.prototype.onButtonBlinked = function( buttonValue , action , button ) {
	switch ( button.internalRole ) {
		case 'previousPage' :
		case 'nextPage' :
			break ;
		default :
			if ( this.hasSubmenu && button.def.items ) {
				if ( this.submenuOptions.openOn === 'parentBlinked' ) {
					this.openSubmenu( button.value , button ) ;
				}
			}
			else {
				this.emit( 'blinked' , buttonValue , action , this , button ) ;
			}
	}
} ;



BaseMenu.prototype.onButtonFocus = function( focus , type , button ) {
	switch ( button.internalRole ) {
		case 'previousPage' :
		case 'nextPage' :
			break ;
		default :
			if ( focus && this.hasSubmenu && button.def.items && this.submenuOptions.openOn === 'parentFocus' ) {
				this.openSubmenu( button.value , button ) ;
			}

			this.emit( 'itemFocus' , button.value , focus , button ) ;
	}
} ;



BaseMenu.prototype.onSubmenuSubmit = function( buttonValue , action , submenu , button ) {
	submenu.once( 'blinked' , ( buttonValue_ , reserved , submenu_ , button_ ) => {
		if ( this.submenuOptions.closeOn === 'childSubmit' ) {
			this.closeSubmenu() ;
			this.document.giveFocusTo( this.submenuParentButton || this ) ;
		}
		this.emit( 'blinked' , buttonValue_ , reserved , this , button ) ;
	} ) ;

	this.emit( 'submit' , buttonValue , action , this , button ) ;
} ;



// Userland: .submenu( itemValue )
// Internal: .submenu( itemValue , button )
BaseMenu.prototype.openSubmenu = function( itemValue , button = null ) {
	var x , y , width , height ,
		itemDef = button ? this.itemsDef.find( it => it === button.def ) :
		this.itemsDef.find( it => it.value === itemValue ) ;

	if ( ! itemDef || ! itemDef.items || ! itemDef.items.length ) { return ; }

	if ( this.submenu ) {
		if ( this.submenu.def === itemDef ) { return ; }
		this.closeSubmenu() ;
	}

	this.submenuParentButton = button ;

	switch ( this.submenuOptions.disposition ) {
		case 'overwrite' :
			x = this.outputX ;
			y = this.outputY ;
			//width = this.outputWidth ;
			width = this.submenuOptions.width ;
			//height = this.outputHeight ;
			height = this.submenuOptions.height ;
			break ;
		case 'right' :
		default :
			x = this.outputX + this.outputWidth ;
			y = this.outputY ;
			width = this.submenuOptions.width || this.outputWidth ;
			break ;
	}

	if ( this.submenuOptions.hideParent ) {
		this.children.forEach( e => e.hidden = true ) ;
	}

	this.submenu = new this.constructor( Object.assign( {} , this.submenuOptions , {
		internal: true ,
		parent: this ,
		isSubmenu: true ,
		def: itemDef ,
		outputX: x ,
		outputY: y ,
		outputWidth: width ,
		outputHeight: height ,
		items: itemDef.items ,
		noDraw: true
	} ) ) ;

	// Draw instead of outerDraw?
	//this.draw() ;
	this.outerDraw() ;

	if ( this.submenuOptions.focusOnOpen ) {
		this.document.giveFocusTo( this.submenu ) ;
	}

	this.submenu.on( 'submit' , this.onSubmenuSubmit ) ;

	// Re-emit itemFocus from child
	this.submenu.on( 'itemFocus' , ( ... args ) => this.emit( 'itemFocus' , ... args ) ) ;
} ;



BaseMenu.prototype.closeSubmenu = function() {
	if ( ! this.submenu ) { return false ; }
	if ( this.submenuOptions.hideParent ) { this.children.forEach( e => e.hidden = false ) ; }
	this.submenu.destroy() ;
	this.submenu = null ;
	return true ;
} ;



// Should be redefined in the derivative class
BaseMenu.prototype.defaultOptions = {} ;
BaseMenu.prototype.buttonKeyBindings = {} ;
BaseMenu.prototype.buttonActionKeyBindings = {} ;
BaseMenu.prototype.toggleButtonKeyBindings = {} ;
BaseMenu.prototype.toggleButtonActionKeyBindings = {} ;
BaseMenu.prototype.initPage = function() {} ;
BaseMenu.prototype.onButtonToggle = function() {} ;
BaseMenu.prototype.childUseParentKeyValue = false ;



const userActions = BaseMenu.prototype.userActions ;

userActions.character =
userActions.specialKey = function( key ) {
	var index = this.hotkeyToButtonIndex.get( key ) ;
	if ( index !== undefined && this.buttons[ index ] ) {
		if ( this.document ) {
			this.document.giveFocusTo( this.buttons[ index ] ) ;
		}
		this.buttons[ index ].submit() ;
	}
	else {
		// Force bubble
		return false ;
	}
} ;

userActions.previous = function() {
	this.focusChild = this.focusPreviousChild( ! this.maxPage ) ;
	if ( this.focusChild === this.children[ 0 ] && this.maxPage && this.page > 0 ) {
		this.previousPage( 'backCycle' ) ;
	}
} ;

userActions.next = function() {
	this.focusChild = this.focusNextChild( ! this.maxPage ) ;
	if ( this.focusChild === this.children[ this.children.length - 1 ] && this.maxPage && this.page < this.maxPage ) {
		this.nextPage( 'cycle' ) ;
	}
} ;

userActions.previousPage = function() {
	if ( this.maxPage && this.page > 0 ) {
		this.previousPage( 'backCycle' ) ;
	}
	else {
		this.focusFirstPageItem( 'backCycle' ) ;
	}
} ;

userActions.nextPage = function() {
	if ( this.maxPage && this.page < this.maxPage ) {
		this.nextPage( 'cycle' ) ;
	}
	else {
		this.focusLastPageItem( 'cycle' ) ;
	}
} ;

userActions.firstPage = function() {
	if ( this.maxPage && this.page !== 0 ) {
		this.toPage( 0 , 'backCycle' ) ;
	}
	else {
		this.focusFirstPageItem( 'backCycle' ) ;
	}
} ;

userActions.lastPage = function() {
	if ( this.maxPage && this.page !== this.maxPage ) {
		this.toPage( this.maxPage , 'cycle' ) ;
	}
	else {
		this.focusLastPageItem( 'cycle' ) ;
	}
} ;

userActions.firstPageItem = function() {
	this.focusFirstPageItem( 'backCycle' ) ;
} ;

userActions.lastPageItem = function() {
	this.focusLastPageItem( 'cycle' ) ;
} ;

userActions.parentMenu = function() {
	if ( this.isSubmenu ) {
		// Back up the parent, because current instance can be destroyed by parent.closeSubmenu()
		let parent = this.parent ;
		if ( this.parent.submenuOptions.hideParent ) { this.parent.closeSubmenu() ; }
		parent.document.giveFocusTo( parent ) ;
	}
	else {
		return false ;
	}
} ;

userActions.submenu = function() {
	if ( this.hasSubmenu && this.focusChild?.def?.items ) {
		this.openSubmenu( this.focusChild.value , this.focusChild ) ;
		if ( this.submenu ) { this.document.giveFocusTo( this.submenu ) ; }
	}
	else {
		return false ;
	}
} ;


}).call(this)}).call(this,require('_process'))
},{"./Button.js":17,"./Element.js":25,"_process":191,"tree-kit":145}],16:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const framesChars = require( '../spChars.js' ).box ;



function Border( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	this.onParentResize = this.onParentResize.bind( this ) ;

	Element.call( this , options ) ;

	if ( ! this.parent ) {
		throw new Error( "Border requires a parent widget" ) ;
	}

	this.attr = options.attr || { bgColor: 'gray' , color: 'white' , dim: true } ;

	this.shadow = !! options.shadow ;
	this.shadowChar = options.shadowChar || ' ' ;
	this.shadowAttr = options.shadowAttr || { bgColor: 'black' , color: 'gray' } ;

	this.outputX = this.parent.outputX - 1 ;
	this.outputY = this.parent.outputY - 1 ;
	this.outputWidth = this.parent.outputWidth + ( this.shadow ? 3 : 2 ) ;
	this.outputHeight = this.parent.outputHeight + ( this.shadow ? 3 : 2 ) ;

	this.frameChars = framesChars.double ;

	if ( options.frameChars ) {
		if ( typeof options.frameChars === 'object' ) {
			this.frameChars = options.frameChars ;
		}
		else if ( typeof options.frameChars === 'string' && framesChars[ options.frameChars ] ) {
			this.frameChars = framesChars[ options.frameChars ] ;
		}
	}

	this.on( 'parentResize' , this.onParentResize ) ;

	if ( this.elementType === 'Border' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Border ;
Element.inherit( Border ) ;



Border.prototype.preDrawSelf = function() {
	var y , ymax , vFrame ,
		extra = this.shadow ? 1 : 0 ;

	// Draw the top border
	this.outputDst.put(
		{ x: this.outputX , y: this.outputY , attr: this.attr } ,
		this.frameChars.topLeft + this.frameChars.horizontal.repeat( this.outputWidth - 2 - extra ) + this.frameChars.topRight
	) ;

	// Draw the bottom border
	this.outputDst.put(
		{ x: this.outputX , y: this.outputY + this.outputHeight - 1 - extra , attr: this.attr } ,
		this.frameChars.bottomLeft + this.frameChars.horizontal.repeat( this.outputWidth - 2 - extra ) + this.frameChars.bottomRight
	) ;

	// Draw the left and right border
	vFrame = this.frameChars.vertical.repeat( this.outputHeight - 2 - extra ) ;
	this.outputDst.put( {
		x: this.outputX , y: this.outputY + 1 , direction: 'down' , attr: this.attr
	} , vFrame ) ;
	this.outputDst.put( {
		x: this.outputX + this.outputWidth - 1 - extra , y: this.outputY + 1 , direction: 'down' , attr: this.attr
	} , vFrame ) ;

	if ( this.shadow ) {
		// Draw the bottom shadow
		this.outputDst.put(
			{ x: this.outputX + 1 , y: this.outputY + this.outputHeight - 1 , attr: this.shadowAttr } ,
			this.shadowChar.repeat( this.outputWidth - 1 )
		) ;

		// Draw the right shadow
		this.outputDst.put(
			{
				x: this.outputX + this.outputWidth - 1 , y: this.outputY + 1 , direction: 'down' , attr: this.shadowAttr
			} ,
			this.shadowChar.repeat( this.outputHeight - 2 )
		) ;
	}
} ;



Border.prototype.onParentResize = function() {
	//this.outputX = this.parent.outputX - 1 ;
	//this.outputY = this.parent.outputY - 1 ;
	this.outputWidth = this.parent.outputWidth + ( this.shadow ? 3 : 2 ) ;
	this.outputHeight = this.parent.outputHeight + ( this.shadow ? 3 : 2 ) ;
	this.draw() ;
} ;


},{"../spChars.js":54,"./Element.js":25}],17:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const Text = require( './Text.js' ) ;



function Button( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	if ( ! Array.isArray( options.content ) ) { options.content = [ options.content || '' ] ; }

	this.blurLeftPadding = options.blurLeftPadding ?? options.leftPadding ?? '' ;
	this.blurRightPadding = options.blurRightPadding ?? options.rightPadding ?? '' ;
	this.focusLeftPadding = options.focusLeftPadding ?? options.leftPadding ?? '' ;
	this.focusRightPadding = options.focusRightPadding ?? options.rightPadding ?? '' ;
	this.disabledLeftPadding = options.disabledLeftPadding ?? options.leftPadding ?? '' ;
	this.disabledRightPadding = options.disabledRightPadding ?? options.rightPadding ?? '' ;
	this.submittedLeftPadding = options.submittedLeftPadding ?? options.leftPadding ?? '' ;
	this.submittedRightPadding = options.submittedRightPadding ?? options.rightPadding ?? '' ;

	// Used by ToggleButton, it's easier to move the functionality here to compute size at only one place
	this.turnedOnBlurLeftPadding = options.turnedOnBlurLeftPadding ?? options.turnedOnLeftPadding ?? this.blurLeftPadding ;
	this.turnedOnBlurRightPadding = options.turnedOnBlurRightPadding ?? options.turnedOnRightPadding ?? this.blurRightPadding ;
	this.turnedOffBlurLeftPadding = options.turnedOffBlurLeftPadding ?? options.turnedOffLeftPadding ?? this.blurLeftPadding ;
	this.turnedOffBlurRightPadding = options.turnedOffBlurRightPadding ?? options.turnedOffRightPadding ?? this.blurRightPadding ;
	this.turnedOnFocusLeftPadding = options.turnedOnFocusLeftPadding ?? options.turnedOnLeftPadding ?? this.focusLeftPadding ;
	this.turnedOnFocusRightPadding = options.turnedOnFocusRightPadding ?? options.turnedOnRightPadding ?? this.focusRightPadding ;
	this.turnedOffFocusLeftPadding = options.turnedOffFocusLeftPadding ?? options.turnedOffLeftPadding ?? this.focusLeftPadding ;
	this.turnedOffFocusRightPadding = options.turnedOffFocusRightPadding ?? options.turnedOffRightPadding ?? this.focusRightPadding ;

	this.contentHasMarkup = !! options.contentHasMarkup ;	// Force this now, instead of Element behavior
	this.paddingHasMarkup = !! options.paddingHasMarkup ;

	// Set by .setContent()
	this.content =
		this.blurContent = this.focusContent =
		this.disabledContent = this.submittedContent =
		this.turnedOnBlurContent = this.turnedOffBlurContent =
		this.turnedOnFocusContent = this.turnedOffFocusContent = '' ;

	// We need to compute that now
	if ( this.setContent === Button.prototype.setContent ) {
		this.setContent( options , this.contentHasMarkup , true , true ) ;
	}

	// Forced or adaptative windth/height?
	if ( ! options.width ) {
		options.width = this.innerWidth ;
		options.contentAdaptativeWidth = true ;
	}

	if ( ! options.height ) {
		options.height = this.contentHeight ;
		options.contentAdaptativeHeight = true ;
	}

	// Used by menus, to assign nextPage/previousPage action
	this.internalRole = options.internalRole || null ;

	// Force delete on width/height, should be computed from content
	//delete options.width ;
	//delete options.height ;

	Text.call( this , options ) ;

	this.blurAttr = options.blurAttr || { bgColor: 'brightBlack' } ;
	this.focusAttr = options.focusAttr || { bgColor: 'blue' } ;
	this.disabledAttr = options.disabledAttr || { bgColor: 'black' , color: 'brightBlack' } ;
	this.submittedAttr = options.submittedAttr || { bgColor: 'brightBlue' } ;
	this.turnedOnBlurAttr = options.turnedOnBlurAttr || { bgColor: 'cyan' } ;
	this.turnedOnFocusAttr = options.turnedOnFocusAttr || { bgColor: 'brightCyan' , color: 'gray' , bold: true } ;
	this.turnedOffBlurAttr = options.turnedOffBlurAttr || { bgColor: 'gray' , dim: true } ;
	this.turnedOffFocusAttr = options.turnedOffFocusAttr || { bgColor: 'white' , color: 'black' , bold: true } ;

	this.disabled = !! options.disabled ;
	this.submitted = !! options.submitted ;
	this.submitOnce = !! options.submitOnce ;

	this.attr = null ;
	this.leftPadding = null ;
	this.rightPadding = null ;
	this.updateStatus() ;

	this.onShortcut = this.onShortcut.bind( this ) ;
	this.onFocus = this.onFocus.bind( this ) ;
	this.onClick = this.onClick.bind( this ) ;
	this.onRightClick = this.onRightClick.bind( this ) ;
	this.onMiddleClick = this.onMiddleClick.bind( this ) ;
	this.onHover = this.onHover.bind( this ) ;

	if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; }
	if ( options.actionKeyBindings ) { this.actionKeyBindings = options.actionKeyBindings ; }

	this.on( 'key' , this.onKey ) ;
	this.on( 'shortcut' , this.onShortcut ) ;
	this.on( 'focus' , this.onFocus ) ;
	this.on( 'click' , this.onClick ) ;
	this.on( 'rightClick' , this.onRightClick ) ;
	this.on( 'middleClick' , this.onMiddleClick ) ;
	this.on( 'hover' , this.onHover ) ;

	if ( this.elementType === 'Button' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Button ;
Element.inherit( Button , Text ) ;



Button.prototype.needInput = true ;



Button.prototype.keyBindings = {
	ENTER: 'submit' ,
	KP_ENTER: 'submit' ,
	ALT_ENTER: 'submit'
} ;



Button.prototype.actionKeyBindings = {} ;



// Utility function
Button.prototype._toContentArray = function( content ) {
	return ! this.forceContentArray || Array.isArray( content ) ? content : [ content || '' ] ;
} ;



Button.prototype.setContent = function( content , hasMarkup , dontDraw = false , dontResize = false ) {
	this.contentHasMarkup = hasMarkup ;

	if ( ! content || typeof content !== 'object' || Array.isArray( content ) ) {
		this.content = this._toContentArray( content ) ;

		this.blurContent = this.focusContent =
			this.disabledContent = this.submittedContent =
			this.turnedOnBlurContent = this.turnedOffBlurContent =
			this.turnedOnFocusContent = this.turnedOffFocusContent =
			this.content ;

		//this.contentWidth = Element.computeContentWidth( this.content , this.contentHasMarkup ) ;
		this.computeRequiredWidth() ;
		this.computeRequiredHeight() ;
	}
	else {
		if ( content.internal ) {
			// This is called from the constructor using the options argument
			this.content = this._toContentArray( content.content ) ;
			this.blurContent = this._toContentArray( content.blurContent ?? this.content ) ;
			this.focusContent = this._toContentArray( content.focusContent ?? this.content ) ;
			this.disabledContent = this._toContentArray( content.disabledContent ?? this.content ) ;
			this.submittedContent = this._toContentArray( content.submittedContent ?? this.content ) ;
			this.turnedOnBlurContent = this._toContentArray( content.turnedOnBlurContent ?? content.turnedOnContent ?? this.content ) ;
			this.turnedOffBlurContent = this._toContentArray( content.turnedOffBlurContent ?? content.turnedOffContent ?? this.content ) ;
			this.turnedOnFocusContent = this._toContentArray( content.turnedOnFocusContent ?? content.turnedOnContent ?? this.content ) ;
			this.turnedOffFocusContent = this._toContentArray( content.turnedOffFocusContent ?? content.turnedOffContent ?? this.content ) ;
		}
		else {
			// Regular call (user, normal case)
			this.content = this._toContentArray( content.default ) ;
			this.blurContent = this._toContentArray( content.blur ?? this.content ) ;
			this.focusContent = this._toContentArray( content.focus ?? this.content ) ;
			this.disabledContent = this._toContentArray( content.disabled ?? this.content ) ;
			this.submittedContent = this._toContentArray( content.submitted ?? this.content ) ;
			this.turnedOnBlurContent = this._toContentArray( content.turnedOnBlur ?? content.turnedOn ?? this.content ) ;
			this.turnedOffBlurContent = this._toContentArray( content.turnedOffBlur ?? content.turnedOff ?? this.content ) ;
			this.turnedOnFocusContent = this._toContentArray( content.turnedOnFocus ?? content.turnedOn ?? this.content ) ;
			this.turnedOffFocusContent = this._toContentArray( content.turnedOffFocus ?? content.turnedOff ?? this.content ) ;
		}

		//this.computeContentWidth() ;
		this.computeRequiredWidth() ;
		this.computeRequiredHeight() ;
	}

	if ( ! dontResize && this.resizeOnContent ) { this.resizeOnContent() ; }
	if ( ! dontDraw ) { this.outerDraw() ; }
} ;



Button.prototype.computeRequiredWidth = function() {
	this.leftPaddingWidth = Math.max(
		Element.computeContentWidth( this.blurLeftPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.focusLeftPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.disabledLeftPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.submittedLeftPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.turnedOnFocusLeftPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.turnedOffFocusLeftPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.turnedOnBlurLeftPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.turnedOffBlurLeftPadding , this.paddingHasMarkup )
	) ;

	this.rightPaddingWidth = Math.max(
		Element.computeContentWidth( this.blurRightPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.focusRightPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.disabledRightPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.submittedRightPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.turnedOnFocusRightPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.turnedOffFocusRightPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.turnedOnBlurRightPadding , this.paddingHasMarkup ) ,
		Element.computeContentWidth( this.turnedOffBlurRightPadding , this.paddingHasMarkup )
	) ;

	this.contentWidth = Math.max(
		Element.computeContentWidth( this.blurContent , this.contentHasMarkup ) ,
		Element.computeContentWidth( this.focusContent , this.contentHasMarkup ) ,
		Element.computeContentWidth( this.disabledContent , this.contentHasMarkup ) ,
		Element.computeContentWidth( this.submittedContent , this.contentHasMarkup ) ,
		Element.computeContentWidth( this.turnedOnFocusContent , this.contentHasMarkup ) ,
		Element.computeContentWidth( this.turnedOffFocusContent , this.contentHasMarkup ) ,
		Element.computeContentWidth( this.turnedOnBlurContent , this.contentHasMarkup ) ,
		Element.computeContentWidth( this.turnedOffBlurContent , this.contentHasMarkup )
	) ;

	this.innerWidth = this.leftPaddingWidth + this.rightPaddingWidth + this.contentWidth || 1 ;

	return this.innerWidth ;
} ;



Button.prototype.drawSelfCursor = function() {
	// Move the cursor back to the first cell
	this.outputDst.moveTo( this.outputX , this.outputY ) ;
	this.outputDst.drawCursor() ;
} ;



// Blink effect, when the button is submitted
Button.prototype.blink = function( special = null , animationCountdown = 4 ) {
	if ( animationCountdown ) {
		if ( animationCountdown % 2 ) { this.attr = this.focusAttr ; }
		else { this.attr = this.blurAttr ; }

		this.draw() ;
		setTimeout( () => this.blink( special , animationCountdown - 1 ) , 80 ) ;
	}
	else {
		this.updateStatus() ;
		this.draw() ;
		this.emit( 'blinked' , this.value , special , this ) ;
	}
} ;



Button.prototype.onFocus = function( focus , type ) {
	this.updateStatus() ;
	this.draw() ;
} ;



Button.prototype.updateStatus = function() {
	if ( this.disabled ) {
		this.attr = this.disabledAttr ;
		this.content = this.disabledContent ;
		this.leftPadding = this.disabledLeftPadding ;
		this.rightPadding = this.disabledRightPadding ;
	}
	else if ( this.submitted ) {
		this.attr = this.submittedAttr ;
		this.content = this.submittedContent ;
		this.leftPadding = this.submittedLeftPadding ;
		this.rightPadding = this.submittedRightPadding ;
	}
	else if ( this.hasFocus ) {
		this.attr = this.focusAttr ;
		this.content = this.focusContent ;
		this.leftPadding = this.focusLeftPadding ;
		this.rightPadding = this.focusRightPadding ;
	}
	else {
		this.attr = this.blurAttr ;
		this.content = this.blurContent ;
		this.leftPadding = this.blurLeftPadding ;
		this.rightPadding = this.blurRightPadding ;
	}
} ;



Button.prototype.submit = function( special ) {
	if ( this.submitOnce ) { this.submitted = true ; }
	this.emit( 'submit' , this.value , special , this ) ;

	// Blink call .updateStatus()
	this.blink( special ) ;
} ;



Button.prototype.unsubmit = function() {
	this.submitted = false ;
	this.updateStatus() ;
} ;



Button.prototype.onHover = function( data ) {
	if ( this.disabled || this.submitted ) { return ; }
	this.document.giveFocusTo( this , 'hover' ) ;
} ;



Button.prototype.onClick = function( data ) {
	if ( this.disabled || this.submitted ) { return ; }
	this.document.giveFocusTo( this , 'select' ) ;
	this.submit( this.actionKeyBindings.click ) ;
} ;



Button.prototype.onRightClick = function( data ) {
	if ( this.disabled || this.submitted ) { return ; }
	this.document.giveFocusTo( this , 'select' ) ;
	this.submit( this.actionKeyBindings.rightClick ) ;
} ;



Button.prototype.onMiddleClick = function( data ) {
	if ( this.disabled || this.submitted ) { return ; }
	this.document.giveFocusTo( this , 'select' ) ;
	this.submit( this.actionKeyBindings.middleClick ) ;
} ;



Button.prototype.onShortcut = function() {
	if ( this.disabled || this.submitted ) { return ; }
	this.document.giveFocusTo( this , 'select' ) ;
	this.submit() ;
} ;



const userActions = Button.prototype.userActions ;

userActions.submit = function( key ) {
	if ( this.disabled || this.submitted ) { return ; }
	this.submit( this.actionKeyBindings[ key ] ) ;
} ;


},{"./Element.js":25,"./Text.js":37}],18:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const BaseMenu = require( './BaseMenu.js' ) ;
const Button = require( './Button.js' ) ;
const ToggleButton = require( './ToggleButton.js' ) ;



// Inherit from BaseMenu for common methods

function ColumnMenu( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	this.onParentResize = this.onParentResize.bind( this ) ;

	// Overwritten by Element() when .autoWidth is set
	if ( ! options.outputWidth && ! options.width ) {
		if ( options.parent ) {
			options.outputWidth = Math.min( options.parent.inputWidth , options.parent.outputWidth ) ;
		}
		else if ( options.inlineTerm ) {
			options.outputWidth = options.inlineTerm.width ;
		}
	}

	this.buttonsMaxWidth = 0 ;
	this.buttonPaddingWidth = 0 ;
	this.buttonSymbolWidth = 0 ;

	this.pageHeight = 0 ;	// current page height, computed
	this.pageItemsDef = null ;
	//this.masterItem = options.masterItem || null ;

	if ( ! options.multiLineItems ) {
		options.height = options.items && options.items.length ;
	}

	BaseMenu.call( this , options ) ;

	this.maxHeight =
		this.autoHeight && this.outputDst ? Math.round( this.outputDst.height * this.autoHeight ) :
		options.maxHeight ? options.maxHeight :
		options.pageMaxHeight ? options.pageMaxHeight :	// for backward compatibility
		this.strictInline ? this.inlineTerm.height :
		Infinity ;

	this.on( 'parentResize' , this.onParentResize ) ;

	this.multiLineItems = !! options.multiLineItems ;

	this.initChildren() ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'ColumnMenu' && ! options.noDraw ) { this.draw() ; }
}

module.exports = ColumnMenu ;
Element.inherit( ColumnMenu , BaseMenu ) ;



ColumnMenu.prototype.inlineNewLine = true ;
ColumnMenu.prototype.ButtonClass = Button ;



ColumnMenu.prototype.defaultOptions = {
	buttonBlurAttr: { bgColor: 'black' , color: 'white' , bold: true } ,
	buttonEvenBlurAttr: null ,
	buttonFocusAttr: { bgColor: 'white' , color: 'black' , bold: true } ,
	buttonDisabledAttr: { bgColor: 'black' , color: 'gray' , bold: true } ,
	buttonSubmittedAttr: { bgColor: 'gray' , color: 'brightWhite' , bold: true } ,
	buttonTurnedOnBlurAttr: { bgColor: 'cyan' } ,
	buttonTurnedOnFocusAttr: { bgColor: 'brightCyan' , color: 'gray' , bold: true } ,
	buttonTurnedOffBlurAttr: { bgColor: 'gray' , dim: true } ,
	buttonTurnedOffFocusAttr: { bgColor: 'white' , color: 'black' , bold: true }
} ;



ColumnMenu.prototype.keyBindings = {
	UP: 'previous' ,
	DOWN: 'next' ,
	PAGE_UP: 'previousPage' ,
	PAGE_DOWN: 'nextPage' ,
	HOME: 'firstPage' ,
	END: 'lastPage' ,
	//	ENTER: 'submit' ,
	//	KP_ENTER: 'submit' ,
	ALT_ENTER: 'submit' ,
	ESCAPE: 'parentMenu' ,
	LEFT: 'parentMenu' ,
	RIGHT: 'submenu'
} ;

ColumnMenu.prototype.buttonKeyBindings = {
	ENTER: 'submit' ,
	KP_ENTER: 'submit'
} ;

ColumnMenu.prototype.toggleButtonKeyBindings = {
	ENTER: 'toggle' ,
	KP_ENTER: 'toggle'
} ;



// Pre-compute page and eventually create Buttons automatically
ColumnMenu.prototype.initChildren = function( noInitPage = false ) {
	// Do not exit now: maybe there are masterDef and separatorDef (SelectList*)
	//if ( ! this.itemsDef.length ) { return ; }

	// Reset things, because .initChildren() can be called multiple times on 'parentResize' events
	this.pageItemsDef = [] ;

	this.buttonPaddingWidth =
		Math.max(
			Element.computeContentWidth( this.blurLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.focusLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.disabledLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.submittedLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOnFocusLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOffFocusLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOnBlurLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOffBlurLeftPadding , this.paddingHasMarkup )
		) + Math.max(
			Element.computeContentWidth( this.blurRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.focusRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.disabledRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.submittedRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOnFocusRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOffFocusRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOnBlurRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOffBlurRightPadding , this.paddingHasMarkup )
		) ;

	if ( this.buttonPaddingWidth > this.outputWidth ) {
		// The padding itself is bigger than the width... so what should we do?
		return ;
	}

	var ellipsisWidth = Element.computeContentWidth( this.contentEllipsis , false ) ;


	this.previousPageDef = Object.assign( { content: '▲' , internalRole: 'previousPage' } , this.previousPageDef ) ;
	this.previousPageDef.contentHasMarkup = this.previousPageDef.contentHasMarkup || this.previousPageDef.markup ;
	this.previousPageDef.width = this.buttonPaddingWidth + Element.computeContentWidth( this.previousPageDef.content , this.previousPageDef.contentHasMarkup ) ;
	this.previousPageDef.buttonContent = this.previousPageDef.content ;

	this.nextPageDef = Object.assign( { content: '▼' , internalRole: 'nextPage' } , this.nextPageDef ) ;
	this.nextPageDef.contentHasMarkup = this.nextPageDef.contentHasMarkup || this.nextPageDef.markup ;
	this.nextPageDef.width = this.buttonPaddingWidth + Element.computeContentWidth( this.nextPageDef.content , this.nextPageDef.contentHasMarkup ) ;
	this.nextPageDef.buttonContent = this.nextPageDef.content ;

	if ( this.masterDef ) {
		this.masterDef = Object.assign( { content: 'column-menu' , internalRole: 'master' } , this.masterDef ) ;
		this.masterDef.contentHasMarkup = this.masterDef.contentHasMarkup || this.masterDef.markup ;

		this.masterDef.buttonContent = this.masterDef.content ;

		if ( this.masterDef.symbol ) {
			this.buttonSymbolWidth = 1 + Element.computeContentWidth( this.masterDef.symbol ) ;
			this.masterDef.buttonContent += ' ' + this.masterDef.symbol ;
		}

		this.masterDef.width = this.buttonPaddingWidth + Element.computeContentWidth( this.masterDef.buttonContent , this.masterDef.contentHasMarkup ) ;
	}

	this.buttonsMaxWidth = Math.max( this.buttonsMaxWidth , this.previousPageDef.width , this.nextPageDef.width , this.masterDef ? this.masterDef.width : 0 ) ;


	var page = 0 , pageHeight = 0 ;

	this.itemsDef.forEach( ( def , index ) => {
		def.contentHasMarkup = def.contentHasMarkup || def.markup ;
		def.buttonContent = def.content ;
		def.buttonBlurContent = def.blurContent ;
		def.buttonFocusContent = def.focusContent ;
		def.buttonDisabledContent = def.disabledContent ;
		def.buttonSubmittedContent = def.submittedContent ;
		def.buttonTurnedOnBlurContent = def.turnedOnBlurContent ;
		def.buttonTurnedOffBlurContent = def.turnedOffBlurContent ;
		def.buttonTurnedOnFocusContent = def.turnedOnFocusContent ;
		def.buttonTurnedOffFocusContent = def.turnedOffFocusContent ;

		// Computing all button content variant like suggested by issue #144 is really complicated,
		// All variant should come out with the same width and height.

		var contentWidth = Element.computeContentWidth( def.content , def.contentHasMarkup ) ,
			buttonHeight = 1 ,
			isLastItem = index === this.itemsDef.length - 1 ,
			// height without previous/next buttons:
			pageMaxInnerHeight = Math.max( 1 , ! isLastItem ? this.maxHeight - 1 : this.maxHeight ) ,
			overflow = this.buttonPaddingWidth + contentWidth - this.outputWidth ;

		if ( overflow > 0 ) {
			if ( this.multiLineItems ) {
				def.buttonContent = Element.wordWrapContent( def.content , this.outputWidth - this.buttonPaddingWidth , def.contentHasMarkup ) ;
				contentWidth = this.outputWidth - this.buttonPaddingWidth ;
				buttonHeight = def.buttonContent.length ;
			}
			else {
				def.buttonContent = Element.truncateContent( def.content , contentWidth - overflow - ellipsisWidth , def.contentHasMarkup ) + this.contentEllipsis ;
				contentWidth = Element.computeContentWidth( def.buttonContent , def.contentHasMarkup ) ;
			}
		}

		if ( index && pageHeight + buttonHeight > pageMaxInnerHeight ) {
			// ^--- check if there is content on this page, which is always true except for the very first item of page 0
			page ++ ;
			pageHeight = 1 ;

			// Still too big?
			if ( pageHeight + buttonHeight > pageMaxInnerHeight && buttonHeight > 1 ) {
				buttonHeight = pageMaxInnerHeight - pageHeight ;
				def.buttonContent.length = buttonHeight ;
				def.buttonContent[ buttonHeight - 1 ] =
					Element.truncateContent(
						def.buttonContent[ buttonHeight - 1 ].trimRight() ,
						contentWidth - ellipsisWidth ,
						def.contentHasMarkup
					)
					+ this.contentEllipsis ;
			}
		}

		pageHeight += buttonHeight ;

		def.width = this.buttonPaddingWidth + contentWidth ;
		def.page = page ;

		if ( def.width + this.buttonSymbolWidth > this.buttonsMaxWidth ) {
			this.buttonsMaxWidth = def.width + this.buttonSymbolWidth ;
		}

		if ( ! this.pageItemsDef[ page ] ) { this.pageItemsDef[ page ] = [] ; }
		this.pageItemsDef[ page ].push( def ) ;
	} ) ;

	this.maxPage = page ;

	if ( this.separatorDef ) {
		this.separatorDef = Object.assign( { content: '-' , disabled: true , internalRole: 'separator' } , this.separatorDef ) ;
		this.separatorDef.width = Element.computeContentWidth( this.separatorDef.content , this.separatorDef.contentHasMarkup ) ;

		if ( this.separatorDef.contentRepeat && this.separatorDef.width < this.buttonsMaxWidth - this.buttonPaddingWidth ) {
			this.separatorDef.content = this.separatorDef.content.repeat( Math.floor( ( this.buttonsMaxWidth - this.buttonPaddingWidth ) / this.separatorDef.width ) ) ;
			this.separatorDef.width = Element.computeContentWidth( this.separatorDef.content , this.separatorDef.contentHasMarkup ) ;
		}

		this.separatorDef.width += this.buttonPaddingWidth ;
		this.separatorDef.buttonContent = this.separatorDef.content ;
	}

	if ( this.masterDef && this.masterDef.width < this.buttonsMaxWidth ) {
		this.masterDef.buttonContent = this.masterDef.content + ' ' + ' '.repeat( this.buttonsMaxWidth - this.masterDef.width ) + this.masterDef.symbol ;
		this.masterDef.width = this.buttonsMaxWidth ;
	}


	// Force at least an empty page
	if ( ! this.pageItemsDef.length ) { this.pageItemsDef.push( [] ) ; }

	this.pageItemsDef.forEach( ( pageDef , index ) => {
		if ( index ) { pageDef.unshift( this.previousPageDef ) ; }
		if ( index < this.pageItemsDef.length - 1 ) { pageDef.push( this.nextPageDef ) ; }
		if ( this.separatorDef ) { pageDef.unshift( this.separatorDef ) ; }
		if ( this.masterDef ) { pageDef.unshift( this.masterDef ) ; }
	} ) ;

	// /!\ Adjust the output width? /!\
	if ( this.outputWidth > this.buttonsMaxWidth ) {
		this.outputWidth = this.buttonsMaxWidth ;
	}

	// Only initPage if we are not a superclass of the object
	if ( this.elementType === 'ColumnMenu' && ! noInitPage ) { this.initPage() ; }
} ;



ColumnMenu.prototype.initPage = function( page = this.page ) {
	var buttonOffsetX = 0 , buttonOffsetY = 0 ;

	if ( ! this.pageItemsDef[ page ] ) { return ; }

	this.buttons.forEach( button => button.destroy( false , true ) ) ;
	this.buttons.length = 0 ;
	this.hotkeyToButtonIndex.clear() ;

	this.pageItemsDef[ page ].forEach( ( def , index ) => {
		var ButtonConstructor , isToggle , key , value , blurAttr ;

		if ( ! Array.isArray( def.buttonContent ) ) {
			//def.buttonContent = [ def.buttonContent + ' '.repeat( this.buttonsMaxWidth - def.width ) ] ;
			def.buttonContent = [ def.buttonContent ] ;
		}

		ButtonConstructor = def.internalRole ? Button :
			def.type === 'button' ? Button :
			def.type === 'toggle' ? ToggleButton :
			this.ButtonClass ?? Button ;
		isToggle = ButtonConstructor === ToggleButton || ButtonConstructor.prototype instanceof ToggleButton ;

		key = def.key ;		// For ToggleButton
		value = this.childUseParentKeyValue && key && this.value && typeof this.value === 'object' ? this.value[ key ] :
			def.value === undefined && ! isToggle && def.key ? def.key :
			def.value ;

		if ( index % 2 ) {
			// Odd
			blurAttr = def.blurAttr || this.buttonBlurAttr ;
		}
		else {
			// Even
			blurAttr = def.evenBlurAttr || def.blurAttr || this.buttonEvenBlurAttr || this.buttonBlurAttr ;
		}

		this.buttons[ index ] = new ButtonConstructor( {
			internal: true ,
			parent: this ,
			childId: index ,
			internalRole: def.internalRole ,
			width: this.buttonsMaxWidth ,	// force a common fixed width
			contentHasMarkup: def.contentHasMarkup ,
			content: def.buttonContent ,
			blurContent: def.buttonBlurContent ,
			focusContent: def.buttonFocusContent ,
			disabledContent: def.buttonDisabledContent ,
			submittedContent: def.buttonSubmittedContent ,
			turnedOnBlurContent: def.buttonTurnedOnBlurContent ,
			turnedOffBlurContent: def.buttonTurnedOffBlurContent ,
			turnedOnFocusContent: def.buttonTurnedOnFocusContent ,
			turnedOffFocusContent: def.buttonTurnedOffFocusContent ,
			disabled: def.disabled ,
			def ,
			key ,
			value ,
			outputX: this.outputX + buttonOffsetX ,
			outputY: this.outputY + buttonOffsetY ,

			blurAttr ,
			focusAttr: def.focusAttr || this.buttonFocusAttr ,
			disabledAttr: def.disabledAttr || this.buttonDisabledAttr ,
			submittedAttr: def.submittedAttr || this.buttonSubmittedAttr ,
			turnedOnFocusAttr: def.turnedOnFocusAttr || this.buttonTurnedOnFocusAttr ,
			turnedOffFocusAttr: def.turnedOffFocusAttr || this.buttonTurnedOffFocusAttr ,
			turnedOnBlurAttr: def.turnedOnBlurAttr || this.buttonTurnedOnBlurAttr ,
			turnedOffBlurAttr: def.turnedOffBlurAttr || this.buttonTurnedOffBlurAttr ,

			blurLeftPadding: this.blurLeftPadding ,
			blurRightPadding: this.blurRightPadding ,
			focusLeftPadding: this.focusLeftPadding ,
			focusRightPadding: this.focusRightPadding ,
			disabledLeftPadding: this.disabledLeftPadding ,
			disabledRightPadding: this.disabledRightPadding ,
			submittedLeftPadding: this.submittedLeftPadding ,
			submittedRightPadding: this.submittedRightPadding ,

			turnedOnFocusLeftPadding: this.turnedOnFocusLeftPadding ,
			turnedOnFocusRightPadding: this.turnedOnFocusRightPadding ,
			turnedOffFocusLeftPadding: this.turnedOffFocusLeftPadding ,
			turnedOffFocusRightPadding: this.turnedOffFocusRightPadding ,
			turnedOnBlurLeftPadding: this.turnedOnBlurLeftPadding ,
			turnedOnBlurRightPadding: this.turnedOnBlurRightPadding ,
			turnedOffBlurLeftPadding: this.turnedOffBlurLeftPadding ,
			turnedOffBlurRightPadding: this.turnedOffBlurRightPadding ,

			paddingHasMarkup: this.paddingHasMarkup ,

			keyBindings: isToggle ? this.toggleButtonKeyBindings : this.buttonKeyBindings ,
			actionKeyBindings: isToggle ? this.toggleButtonActionKeyBindings : this.buttonActionKeyBindings ,
			shortcuts: def.shortcuts ,

			noDraw: true
		} ) ;

		this.buttons[ index ].on( 'submit' , this.onButtonSubmit ) ;
		this.buttons[ index ].on( 'blinked' , this.onButtonBlinked ) ;
		this.buttons[ index ].on( 'focus' , this.onButtonFocus ) ;

		if ( def.hotkey ) {
			if ( Array.isArray( def.hotkey ) ) {
				def.hotkey.forEach( hotkey => this.hotkeyToButtonIndex.set( hotkey , index ) ) ;
			}
			else {
				this.hotkeyToButtonIndex.set( def.hotkey , index ) ;
			}
		}

		if ( isToggle ) {
			this.buttons[ index ].on( 'toggle' , this.onButtonToggle ) ;
		}

		buttonOffsetY += this.buttons[ index ].outputHeight ;
	} ) ;

	// Set outputHeight to the correct value
	if ( buttonOffsetY < this.outputHeight ) { this.needOuterDraw = true ; }
	this.pageHeight = this.outputHeight = buttonOffsetY ;
} ;



ColumnMenu.prototype.onParentResize = function() {
	if ( ! this.autoWidth && ! this.autoHeight ) { return ; }

	if ( this.autoWidth ) {
		this.outputWidth = Math.round( this.outputDst.width * this.autoWidth ) ;
	}

	if ( this.autoHeight ) {
		this.maxHeight = Math.round( this.outputDst.height * this.autoHeight ) ;
	}

	this.initChildren( true ) ;
	this.page = 0 ;
	this.initPage() ;
	this.draw() ;
} ;


},{"./BaseMenu.js":15,"./Button.js":17,"./Element.js":25,"./ToggleButton.js":40}],19:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



/*
	This is a mix between ColumnMenu and ColumnMenuMulti: some items have states, some haven't.
	In fact it is closer from ColumnMenu in behavior: any click submit the button, not a key/value object.
	This is useful to improve the DropDownMenu, where it is common to have menu items states (turned on/off).
*/

const Element = require( './Element.js' ) ;
const ColumnMenu = require( './ColumnMenu.js' ) ;
const Button = require( './Button.js' ) ;
const ToggleButton = require( './ToggleButton.js' ) ;



// Inherit from ColumnMenu for common methods

function ColumnMenuMixed( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	// ColumnMenuMixed value is always a set of values
	if ( options.value && typeof options.value === 'object' ) {
		this.setValue( options.value , true ) ;
	}
	else {
		this.value = {} ;
	}

	ColumnMenu.call( this , options ) ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'ColumnMenuMixed' && ! options.noDraw ) { this.draw() ; }
}

module.exports = ColumnMenuMixed ;
Element.inherit( ColumnMenuMixed , ColumnMenu ) ;



ColumnMenuMixed.prototype.ButtonClass = null ;
ColumnMenuMixed.prototype.childUseParentKeyValue = true ;



ColumnMenuMixed.prototype.initChildren = function( noInitPage = false ) {
	ColumnMenu.prototype.initChildren.call( this ) ;

	// Only initPage if we are not a superclass of the object
	if ( this.elementType === 'ColumnMenuMixed' && ! noInitPage ) { this.initPage() ; }
} ;



// Set all existing children ToggleButton to the correct value
ColumnMenuMixed.prototype.setValue = function( value , noDraw ) {
	if ( ! value || typeof value !== 'object' ) { return ; }

	this.value = {} ;
	for ( let key in value ) { this.value[ key ] = !! value[ key ] ; }

	// Can be called during init, before .initPage() is called
	if ( ! this.buttons ) { return ; }

	this.buttons.forEach( button => {
		if ( button.internalRole || ! button.key || ! ( button instanceof ToggleButton ) ) { return ; }
		button.setValue( !! this.value[ button.key ] , true , true ) ;
	} ) ;

	if ( ! noDraw ) { this.draw() ; }
} ;



ColumnMenuMixed.prototype.setKeyValue = function( key , value , noDraw ) {
	if ( ! key ) { return ; }

	this.value[ key ] = !! value ;

	// Can be called during init, before .initPage() is called
	if ( ! this.buttons ) { return ; }

	this.buttons.forEach( button => {
		if ( button.internalRole || button.key !== key || ! ( button instanceof ToggleButton ) ) { return ; }
		button.setValue( !! value , true , true ) ;
	} ) ;

	if ( ! noDraw ) { this.draw() ; }
} ;



ColumnMenuMixed.prototype.onButtonToggle = function( buttonValue , action , button ) {
	if ( ! button.key ) { return ; }

	if ( buttonValue ) { this.value[ button.key ] = true ; }
	else { delete this.value[ button.key ] ; }

	this.emit( 'submit' , button.key , buttonValue , this , button ) ;
} ;


},{"./Button.js":17,"./ColumnMenu.js":18,"./Element.js":25,"./ToggleButton.js":40}],20:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const ColumnMenu = require( './ColumnMenu.js' ) ;
const ToggleButton = require( './ToggleButton.js' ) ;



// Inherit from ColumnMenu for common methods

function ColumnMenuMulti( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	// ColumnMenuMulti value is always a set of values
	if ( options.value && typeof options.value === 'object' ) {
		this.setValue( options.value , true ) ;
	}
	else {
		this.value = {} ;
	}

	ColumnMenu.call( this , options ) ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'ColumnMenuMulti' && ! options.noDraw ) { this.draw() ; }
}

module.exports = ColumnMenuMulti ;
Element.inherit( ColumnMenuMulti , ColumnMenu ) ;



ColumnMenuMulti.prototype.ButtonClass = ToggleButton ;
ColumnMenuMulti.prototype.childUseParentKeyValue = true ;



ColumnMenuMulti.prototype.initChildren = function( noInitPage = false ) {
	ColumnMenu.prototype.initChildren.call( this ) ;

	// Only initPage if we are not a superclass of the object
	if ( this.elementType === 'ColumnMenuMulti' && ! noInitPage ) { this.initPage() ; }
} ;



// Set all existing children ToggleButton to the correct value
ColumnMenuMulti.prototype.setValue = function( value , noDraw ) {
	if ( ! value || typeof value !== 'object' ) { return ; }

	this.value = {} ;

	if ( Array.isArray( value ) || value instanceof Set ) {
		for ( let key of value ) {
			if ( key && typeof key === 'string' ) {
				this.value[ key ] = true ;
			}
		}
	}
	else {
		for ( let key in value ) {
			this.value[ key ] = !! value[ key ] ;
		}
	}

	// Can be called during init, before .initPage() is called
	if ( ! this.buttons ) { return ; }

	this.buttons.forEach( button => {
		if ( button.internalRole || ! button.key || ! ( button instanceof ToggleButton ) ) { return ; }
		button.setValue( !! this.value[ button.key ] , true , true ) ;
	} ) ;

	if ( ! noDraw ) { this.draw() ; }
} ;



ColumnMenuMulti.prototype.setKeyValue = function( key , value , noDraw ) {
	if ( ! key ) { return ; }

	this.value[ key ] = !! value ;

	// Can be called during init, before .initPage() is called
	if ( ! this.buttons ) { return ; }

	this.buttons.forEach( button => {
		if ( button.internalRole || button.key !== key || ! ( button instanceof ToggleButton ) ) { return ; }
		button.setValue( !! value , true , true ) ;
	} ) ;

	if ( ! noDraw ) { this.draw() ; }
} ;



ColumnMenuMulti.prototype.onButtonSubmit = function( buttonValue , action , button ) {
	switch ( button.internalRole ) {
		case 'previousPage' :
			this.previousPage() ;
			break ;
		case 'nextPage' :
			this.nextPage() ;
			break ;
		default :
			this.emit( 'submit' , this.value , action , this , button ) ;
	}
} ;



ColumnMenuMulti.prototype.onButtonToggle = function( buttonValue , action , button ) {
	if ( ! button.key ) { return ; }

	if ( buttonValue ) { this.value[ button.key ] = true ; }
	else { delete this.value[ button.key ] ; }

	this.emit( 'itemToggle' , button.key , buttonValue , button ) ;
} ;



const userActions = ColumnMenuMulti.prototype.userActions ;

userActions.submit = function() {
	this.emit( 'submit' , this.value , undefined , this ) ;
} ;


},{"./ColumnMenu.js":18,"./Element.js":25,"./ToggleButton.js":40}],21:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



// Container: an enclosed surface (ScreenBuffer), with a viewport to allow scrolling.



const Element = require( './Element.js' ) ;
const ScreenBuffer = require( '../ScreenBuffer.js' ) ;

// Avoid requiring Slider at top-level, it could cause circular require troubles
//const Slider = require( './Slider.js' ) ;



function Container( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	Element.call( this , options ) ;

	this.onClick = this.onClick.bind( this ) ;
	this.onDrag = this.onDrag.bind( this ) ;
	this.onWheel = this.onWheel.bind( this ) ;

	if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; }

	//console.error( 'this.document:' , !! this.document , !! ( this.document && this.document.palette ) , !! options.palette ) ;
	this.palette = options.palette || ( this.document && this.document.palette ) ;
	this.object2attr = object => ScreenBuffer.object2attr( object , this.palette && this.palette.colorNameToIndex ) ;

	this.scrollable = !! options.scrollable ;
	this.hasVScrollBar = this.scrollable && !! options.vScrollBar ;
	this.hasHScrollBar = this.scrollable && !! options.hScrollBar ;
	this.scrollX = options.scrollX || 0 ;
	this.scrollY = options.scrollY || 0 ;
	this.vScrollBarSlider = null ;
	this.hScrollBarSlider = null ;

	this.movable = !! options.movable ;

	this.viewportX = this.outputX + this.containerBorderSize ;
	this.viewportY = this.outputY + this.containerBorderSize ;
	this.viewportWidth = this.outputWidth - this.containerBorderSize * 2 ;
	this.viewportHeight = this.outputHeight - this.containerBorderSize * 2 ;

	this.inputX = options.inputX || this.viewportX + this.scrollX ;
	this.inputY = options.inputY || this.viewportY + this.scrollY ;
	this.inputWidth = options.inputWidth || this.viewportWidth ;
	this.inputHeight = options.inputHeight || this.viewportHeight ;

	this.inputDst = new ScreenBuffer( {
		dst: this.outputDst ,
		x: this.inputX ,
		y: this.inputY ,
		width: this.inputWidth ,
		height: this.inputHeight ,
		palette: this.palette
	} ) ;

	this.deltaDraw = false ;	// Useful for Document, not so useful for other containers
	this.backgroundAttr = options.backgroundAttr || { bgColor: 'default' } ;

	this.on( 'key' , this.onKey ) ;
	this.on( 'click' , this.onClick ) ;
	this.on( 'drag' , this.onDrag ) ;
	this.on( 'wheel' , this.onWheel ) ;

	this.initChildren() ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'Container' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Container ;
Element.inherit( Container ) ;



Container.prototype.isContainer = true ;
Container.prototype.containerBorderSize = 0 ;

const termkit = require( '../termkit.js' ) ;



Container.prototype.keyBindings = {
	UP: 'tinyScrollUp' ,
	DOWN: 'tinyScrollDown' ,
	PAGE_UP: 'scrollUp' ,
	PAGE_DOWN: 'scrollDown' ,
	' ': 'scrollDown' ,
	HOME: 'scrollTop' ,
	END: 'scrollBottom' ,
	LEFT: 'scrollLeft' ,
	RIGHT: 'scrollRight'
} ;



Container.prototype.initChildren = function() {
	// Avoid requiring Slider at top-level, it could cause circular require troubles

	if ( this.hasVScrollBar ) {
		this.vScrollBarSlider = new termkit.Slider( {
			internal: true ,
			parent: this ,
			x: this.viewportWidth - 1 ,
			y: 0 ,
			height: this.viewportHeight ,
			isVertical: true ,
			valueToRate: scrollY => -scrollY / Math.max( 1 , this.inputHeight - this.viewportHeight ) ,
			rateToValue: rate => -rate * Math.max( 1 , this.inputHeight - this.viewportHeight ) ,
			noDraw: true
		} ) ;

		this.vScrollBarSlider.on( 'slideStep' , d => this.scroll( 0 , -d * Math.ceil( this.viewportHeight / 2 ) ) ) ;
		this.vScrollBarSlider.on( 'slide' , value => this.scrollTo( null , value ) ) ;
	}

	if ( this.hasHScrollBar ) {
		this.hScrollBarSlider = new termkit.Slider( {
			internal: true ,
			parent: this ,
			x: 0 ,
			y: this.inputWidth - this.containerBorderSize * 2 - 1 ,
			width: this.viewportWidth - this.hasVScrollBar ,
			valueToRate: scrollY => -scrollY / Math.max( 1 , this.inputWidth - this.viewportWidth ) ,
			rateToValue: rate => -rate * Math.max( 1 , this.inputWidth - this.viewportWidth ) ,
			noDraw: true
		} ) ;

		this.hScrollBarSlider.on( 'slideStep' , d => this.scroll( -d * Math.ceil( this.viewportWidth / 2 ) , 0 ) ) ;
		this.hScrollBarSlider.on( 'slide' , value => this.scrollTo( value , null ) ) ;
	}
} ;



// Accept ScreenBuffer#resize() argument: x, y, width, height.
// Should it support output* and input* args?
Container.prototype.resizeViewport = function( to ) {
	this.viewportWidth = to.width ;
	this.viewportHeight = to.height ;
} ;



Container.prototype.resizeInput = function( to ) {
	if ( ! to.x ) { to.x = 0 ; }
	if ( ! to.y ) { to.y = 0 ; }

	this.inputDst.resize( to ) ;

	this.inputWidth = this.inputDst.width ;
	this.inputHeight = this.inputDst.height ;

	this.children.forEach( child => child.emit( 'parentResize' , to ) ) ;
} ;



Container.prototype.resize = function( to ) {
	if ( ! to.x ) { to.x = 0 ; }
	if ( ! to.y ) { to.y = 0 ; }

	this.inputDst.resize( to ) ;

	this.viewportWidth = this.inputWidth = this.inputDst.width ;
	this.viewportHeight = this.inputHeight = this.inputDst.height ;

	this.children.forEach( child => child.emit( 'parentResize' , to ) ) ;
} ;



Container.prototype.move = function( dx , dy , noDraw = false ) {
	return this.moveTo( this.outputX + dx , this.outputY + dy , noDraw ) ;
} ;



Container.prototype.moveTo = function( x , y , noDraw = false ) {
	this.outputX = x ;
	this.outputY = y ;
	this.viewportX = this.outputX + this.containerBorderSize ;
	this.viewportY = this.outputY + this.containerBorderSize ;
	this.inputDst.x = this.inputX = this.viewportX + this.scrollX ;
	this.inputDst.y = this.inputY = this.viewportY + this.scrollY ;

	if ( ! noDraw ) { this.outerDraw() ; }
} ;



Container.prototype.scroll = function( dx , dy , dontDraw = false ) {
	return this.scrollTo( dx ? this.scrollX + dx : null , dy ? this.scrollY + dy : null , dontDraw ) ;
} ;



Container.prototype.scrollToTop = function( dontDraw = false ) {
	return this.scrollTo( null , 0 , dontDraw ) ;
} ;



Container.prototype.scrollToBottom = function( dontDraw = false ) {
	// Ignore extra scrolling here
	return this.scrollTo( null , this.viewportHeight - this.inputHeight , dontDraw ) ;
} ;



Container.prototype.scrollTo = function( x , y , noDraw = false ) {
	if ( ! this.scrollable ) { return ; }

	if ( x !== undefined && x !== null ) {
		// Got a +1 after content size because of the word-wrap thing and eventual invisible \n
		this.scrollX = Math.min( 0 , Math.max( Math.round( x ) ,
			this.viewportWidth - this.inputWidth + 1
		) ) ;

		this.inputDst.x = this.inputX = this.viewportX + this.scrollX ;
		//console.error( "New x-scrolling:" , x , this.scrollX , this.viewportX , this.inputX ) ;
	}

	if ( y !== undefined && y !== null ) {
		this.scrollY = Math.min( 0 , Math.max( Math.round( y ) ,
			this.viewportHeight - this.inputHeight
		) ) ;

		this.inputDst.y = this.inputY = this.viewportY + this.scrollY ;
		//console.error( "New y-scrolling:" , y , this.scrollY , this.viewportY , this.inputY ) ;
	}

	if ( this.vScrollBarSlider ) {
		this.vScrollBarSlider.setValue( this.scrollY , true ) ;
		this.vScrollBarSlider.setSizeAndPosition( { y: -this.scrollY } ) ;
	}

	if ( this.hScrollBarSlider ) {
		this.hScrollBarSlider.setValue( this.scrollX , true ) ;
	}

	if ( ! noDraw ) { this.draw() ; }
} ;



Container.prototype.preDrawSelf = function() {
	this.inputDst.fill( { char: ' ' , attr: this.backgroundAttr } ) ;
} ;



Container.prototype.postDrawSelf = function() {
	this.inputDst.draw( {
		dst: this.outputDst ,
		delta: this.deltaDraw ,		// Draw only diff or not?
		inline: this.strictInline ,
		x: this.inputX ,
		y: this.inputY ,
		dstClipRect: {
			x: this.viewportX ,
			y: this.viewportY ,
			width: this.viewportWidth ,
			height: this.viewportHeight
		}
	} ) ;
} ;



Container.prototype.drawSelfCursor = function( elementTargeted ) {
	if ( elementTargeted ) { this.restoreCursor() ; }
	else { this.inputDst.drawCursor() ; }
} ;



Container.prototype.onClick = function( data ) {
	// It is susceptible to click event only when it is scrollable
	if ( this.scrollable && ! this.hasFocus ) {
		this.document.giveFocusTo( this , 'select' ) ;
	}
} ;



Container.prototype.onWheel = function( data ) {
	// It's a "tiny" scroll
	if ( ! this.hasFocus ) {
		this.document.giveFocusTo( this , 'select' ) ;
	}

	if ( this.scrollable ) {
		this.scroll( 0 , -data.yDirection * Math.ceil( this.viewportHeight / 5 ) ) ;
	}
} ;



Container.prototype.onDrag = function( data ) {
	if ( ! this.movable || ( ! data.dx && ! data.dy ) ) { return ; }
	this.move( data.dx , data.dy ) ;
} ;



const userActions = Container.prototype.userActions ;

userActions.tinyScrollUp = function() {
	this.scroll( 0 , Math.ceil( this.viewportHeight / 5 ) ) ;
} ;

userActions.tinyScrollDown = function() {
	this.scroll( 0 , -Math.ceil( this.viewportHeight / 5 ) ) ;
} ;

userActions.scrollUp = function() {
	this.scroll( 0 , Math.ceil( this.viewportHeight / 2 ) ) ;
} ;

userActions.scrollDown = function() {
	this.scroll( 0 , -Math.ceil( this.viewportHeight / 2 ) ) ;
} ;

userActions.scrollLeft = function() {
	this.scroll( Math.ceil( this.viewportWidth / 2 ) , 0 ) ;
} ;

userActions.scrollRight = function() {
	this.scroll( -Math.ceil( this.viewportWidth / 2 ) , 0 ) ;
} ;

userActions.scrollTop = function() {
	this.scrollToTop() ;
} ;

userActions.scrollBottom = function() {
	this.scrollToBottom() ;
} ;


},{"../ScreenBuffer.js":3,"../termkit.js":56,"./Element.js":25}],22:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const Container = require( './Container.js' ) ;

const Promise = require( 'seventh' ) ;



function Document( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	if ( ! options.inlineTerm ) {
		options.outputX = 1 ;
		options.outputY = 1 ;
		options.outputWidth = options.outputDst.width ;
		options.outputHeight = options.outputDst.height ;
	}

	// Bypass the Element rule for strictInline, this mode should only be used for inline static Element
	this.strictInlineSupport = !! options.strictInline ;
	this.noInput = !! options.noInput ;

	Container.call( this , options ) ;

	// A document does not have parent
	this.parent = null ;

	// The document of a document is itself
	this.document = this ;

	// Being the top-level element before the Terminal object, this must use delta-drawing (except for strictInline mode)
	this.deltaDraw = ! this.strictInline ;

	this.id = '_document' + '_' + ( nextId ++ ) ;
	this.eventSource = options.eventSource ;
	this.focusElement = null ;
	this.hoverElement = null ;
	this.clickOutCandidates = new Set() ;

	this.motionData = {
		motion: false ,
		xFrom: null ,
		yFrom: null ,
		x: null ,
		y: null ,
		dx: null ,
		dy: null
		//element: null ,
		//localDx: null ,
		//localDy: null
	} ;

	this.draggingData = {
		dragging: false ,
		xFrom: null ,
		yFrom: null ,
		x: null ,
		y: null ,
		dx: null ,
		dy: null ,
		element: null ,
		localDx: null ,
		localDy: null
	} ;

	this.elements = {} ;
	this.onEventSourceKey = this.onEventSourceKey.bind( this ) ;
	this.onEventSourceMouse = this.onEventSourceMouse.bind( this ) ;
	this.onEventSourceResize = this.onEventSourceResize.bind( this ) ;

	if ( ! this.strictInline && ! this.noInput ) {
		// Do not change turn on/change input grabbing mode in strictInline mode
		this.eventSource.grabInput( { mouse: 'motion' } ) ;
		//this.eventSource.grabInput( { mouse: 'button' } ) ;
	}

	this.elementByShortcut = {} ;
	this.documentClipboards = {} ;

	//*
	this.getSystemClipboard = Promise.debounceDelay( 500 , async ( source ) => {
		if ( ! this.outputDst.getClipboard ) { return '' ; }
		return this.outputDst.getClipboard( source ) ;
	} ) ;

	this.setSystemClipboard = Promise.debounceUpdate( async ( str , source ) => {
		if ( ! this.outputDst.setClipboard ) { return ; }
		await this.outputDst.setClipboard( str , source ) ;

		// Avoid running too much xclip shell command
		await Promise.resolveTimeout( 500 ) ;
	} ) ;

	this.clearSystemClipboard = Promise.debounceUpdate( async ( str , source ) => {
		if ( ! this.outputDst.setClipboard ) { return ; }
		await this.outputDst.setClipboard( '' , source ) ;

		// Avoid running too much xclip shell command
		await Promise.resolveTimeout( 500 ) ;
	} ) ;
	//*/

	this.eventSource.on( 'key' , this.onEventSourceKey ) ;
	this.eventSource.on( 'mouse' , this.onEventSourceMouse ) ;
	this.eventSource.on( 'resize' , this.onEventSourceResize ) ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'Document' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Document ;
Element.inherit( Document , Container ) ;



Document.prototype.destroy = function( isSubDestroy , noDraw = false ) {
	if ( this.destroyed ) { return ; }

	this.eventSource.off( 'key' , this.onEventSourceKey ) ;
	this.eventSource.off( 'mouse' , this.onEventSourceMouse ) ;
	this.eventSource.off( 'resize' , this.onEventSourceResize ) ;

	Element.prototype.destroy.call( this , isSubDestroy , noDraw ) ;

	this.eventSource = null ;
	this.setSystemClipboard = null ;
	this.getSystemClipboard = null ;
} ;



Document.prototype.keyBindings = Object.assign( {} , Container.prototype.keyBindings , {
	TAB: 'focusNext' ,
	SHIFT_TAB: 'focusPrevious'
} ) ;



// Next element ID
var nextId = 0 ;

Document.prototype.assignId = function( element , id ) {
	if ( ! id || typeof id !== 'string' || id[ 0 ] === '_' || this.elements[ id ] ) {
		id = '_' + element.elementType + '_' + ( nextId ++ ) ;
	}

	element.id = id ;
	this.elements[ id ] = element ;
} ;



Document.prototype.unassignId = function( element , id ) {
	element.id = null ;
	delete this.elements[ id ] ;
} ;



Document.prototype.giveFocusTo = function( element , type = 'direct' ) {
	if ( ! ( element instanceof Element ) ) { throw new TypeError( '' + element + ' is not an instance of Element.' ) ; }
	if ( this.isAncestorOf( element ) ) { return this.giveFocusTo_( element , type ) ; }
} ;



Document.prototype.giveFocusTo_ = function( element , type ) {
	var ancestor , focusAware ;

	if ( this.focusElement !== element ) {
		if ( this.focusElement ) {
			this.focusElement.hasFocus = false ;
			this.focusElement.emit( 'focus' , false , type , this.focusElement ) ;
		}

		this.focusElement = element ;
		this.focusElement.hasFocus = true ;
		this.focusElement.emit( 'focus' , true , type , this.focusElement ) ;
	}

	// Return false if the focus was given to an element that does not care about focus and key event
	focusAware = ! this.focusElement.disabled && ( this.focusElement.listenerCount( 'focus' ) || this.focusElement.listenerCount( 'key' ) ) ;

	if ( focusAware ) {
		ancestor = this.focusElement ;

		while ( ancestor ) {
			if ( ancestor.listenerCount( 'clickOut' ) ) { this.clickOutCandidates.add( ancestor ) ; }
			ancestor = ancestor.parent ;
		}
	}

	return focusAware ;
} ;



Document.prototype.focusNext = function() {
	var index , startingElement , currentElement , focusAware ;

	if ( ! this.focusElement || ! this.isAncestorOf( this.focusElement ) ) { currentElement = this ; }
	else { currentElement = this.focusElement ; }

	if ( currentElement === this && ! this.children.length ) { return ; }

	startingElement = currentElement ;

	for ( ;; ) {
		if ( currentElement.children.length && ! currentElement.noChildFocus ) {
			// Give focus to the first child of the element
			currentElement = currentElement.children[ 0 ] ;
			if ( ! currentElement.hidden ) { focusAware = this.giveFocusTo_( currentElement , 'cycle' ) ; }
		}
		else if ( currentElement.parent ) {
			for ( ;; ) {
				index = currentElement.parent.children.indexOf( currentElement ) ;

				if ( index + 1 < currentElement.parent.children.length ) {
					// Give focus to the next sibling
					currentElement = currentElement.parent.children[ index + 1 ] ;
					if ( ! currentElement.hidden ) {
						focusAware = this.giveFocusTo_( currentElement , 'cycle' ) ;
						break ;
					}
				}
				else if ( currentElement.parent.parent ) {
					currentElement = currentElement.parent ;
				}
				else {
					// We are at the top-level, just below the document, so cycle again at the first-top-level child

					// This check fixes infinite loop
					if ( startingElement === currentElement.parent ) { return ; }

					currentElement = currentElement.parent.children[ 0 ] ;
					if ( ! currentElement.hidden ) {
						focusAware = this.giveFocusTo_( currentElement , 'cycle' ) ;
						break ;
					}
				}
			}
		}
		else {
			// Nothing to do: no children, no parent, nothing...
			return ;
		}

		// Exit if the focus was given to a focus-aware element or if we have done a full loop already
		//console.error( 'end of loop: ' , focusAware , startingElement.content , currentElement.content ) ;
		if ( startingElement === currentElement || ( ! currentElement.hidden && focusAware ) ) { break ; }
	}
} ;



Document.prototype.focusPrevious = function() {
	var index , startingElement , currentElement , focusAware ;

	if ( ! this.focusElement || ! this.isAncestorOf( this.focusElement ) ) { currentElement = this ; }
	else { currentElement = this.focusElement ; }

	startingElement = currentElement ;

	for ( ;; ) {
		if ( currentElement.parent ) {
			index = currentElement.parent.children.indexOf( currentElement ) ;

			if ( index - 1 >= 0 ) {
				// Give focus to the last child of the last child of the ... of the previous sibling
				currentElement = currentElement.parent.children[ index - 1 ] ;

				while ( currentElement.children.length && ! currentElement.noChildFocus ) {
					currentElement = currentElement.children[ currentElement.children.length - 1 ] ;
				}

				if ( ! currentElement.hidden ) { focusAware = this.giveFocusTo_( currentElement , 'backCycle' ) ; }
			}
			else if ( currentElement.parent.parent ) {
				currentElement = currentElement.parent ;
				if ( ! currentElement.hidden ) { focusAware = this.giveFocusTo_( currentElement , 'backCycle' ) ; }
			}
			else {
				// We are at the top-level, just below the document, so cycle again to the last child of the last child
				// of the ... of the last-top-level child

				// This check fixes infinite loop
				if ( startingElement === currentElement.parent ) { return ; }

				currentElement = currentElement.parent.children[ currentElement.parent.children.length - 1 ] ;

				while ( currentElement.children.length && ! currentElement.noChildFocus ) {
					currentElement = currentElement.children[ currentElement.children.length - 1 ] ;
				}

				if ( ! currentElement.hidden ) { focusAware = this.giveFocusTo_( currentElement , 'backCycle' ) ; }
			}
		}
		else if ( currentElement.children.length ) {
			// Give focus to the last child of the element
			currentElement = currentElement.children[ currentElement.children.length - 1 ] ;

			while ( currentElement.children.length && ! currentElement.noChildFocus ) {
				currentElement = currentElement.children[ currentElement.children.length - 1 ] ;
			}

			if ( ! currentElement.hidden ) { focusAware = this.giveFocusTo_( currentElement , 'backCycle' ) ; }
		}
		else {
			// Nothing to do: no children, no parent, nothing...
			return ;
		}

		// Exit if the focus was given to a focus-aware element or if we have done a full loop already
		//console.error( 'end of loop: ' , focusAware , startingElement.content , currentElement.content ) ;
		if ( startingElement === currentElement || ( ! currentElement.hidden && focusAware ) ) { break ; }
	}
} ;



Document.prototype.onEventSourceKey = function( key , altKeys , data ) {
	if ( this.focusElement ) {
		this.bubblingEvent( this.focusElement , key , altKeys , data ) ;
	}
	else {
		this.defaultKeyHandling( key , altKeys , data ) ;
	}
} ;



Document.prototype.bubblingEvent = function( element , key , altKeys , data ) {
	if ( element !== this ) {
		element.emit( 'key' , key , altKeys , data , ( interruption , event ) => {
			// Interruption means: the child consume the event, it does not want bubbling
			if ( ! interruption ) {
				if ( element.parent ) { this.bubblingEvent( element.parent , key , altKeys , data ) ; }
				else { this.defaultKeyHandling( key , altKeys , data ) ; }
			}
		} ) ;
	}
	else {
		this.defaultKeyHandling( key , altKeys , data ) ;
	}
} ;



Document.prototype.defaultKeyHandling = function( key , altKeys , data ) {
	switch ( this.keyBindings[ key ] ) {
		case 'focusNext' :
			this.focusNext() ;
			break ;
		case 'focusPrevious' :
			this.focusPrevious() ;
			break ;
		default :
			if ( this.elementByShortcut[ key ] && this.elementByShortcut[ key ].document === this ) {
				this.elementByShortcut[ key ].emit( 'shortcut' , key , altKeys , data ) ;
			}
			else {
				this.emit( 'key' , key , altKeys , data ) ;
			}
			break ;
	}
} ;



// TODOC
Document.prototype.setMetaKeyPrefix = function( prefix , remove ) { this.eventSource.setMetaKeyPrefix( prefix , remove ) ; } ;
Document.prototype.getDocumentClipboard = function( key = 'content' ) { return this.documentClipboards[ key ] ; } ;
Document.prototype.setDocumentClipboard = function( value , key = 'content' ) { this.documentClipboards[ key ] = '' + value ; } ;
Document.prototype.clearDocumentClipboard = function( value , key = 'content' ) { delete this.documentClipboards[ key ] ; } ;



Document.prototype.createShortcuts = function( element , ... keys ) {
	if ( element.document !== this ) { return ; }
	keys.forEach( key => this.elementByShortcut[ key ] = element ) ;
} ;



Document.prototype.removeElementShortcuts = function( element ) {
	for ( let key in this.elementByShortcut ) {
		if ( this.elementByShortcut[ key ] === element ) { this.elementByShortcut[ key ] = null ; }
	}
} ;



Document.prototype.onEventSourceMouse = function( name , data ) {
	var matches ;

	switch ( name ) {
		case 'MOUSE_LEFT_BUTTON_PRESSED' :
			this.mouseClick( data ) ;
			break ;

		case 'MOUSE_MOTION' :
			this.mouseMotion( data ) ;
			break ;

		case 'MOUSE_DRAG' :
			this.mouseDrag( data ) ;
			break ;

		case 'MOUSE_RIGHT_BUTTON_PRESSED' :
			this.mouseClick( data , 'rightClick' ) ;
			break ;

		case 'MOUSE_MIDDLE_BUTTON_PRESSED' :
			this.mouseClick( data , 'middleClick' ) ;
			break ;

		case 'MOUSE_WHEEL_UP' :
			data.yDirection = -1 ;
			this.mouseWheel( data ) ;
			break ;

		case 'MOUSE_WHEEL_DOWN' :
			data.yDirection = 1 ;
			this.mouseWheel( data ) ;
			break ;

		// We only catch left mouse dragging ATM
		case 'MOUSE_LEFT_BUTTON_RELEASED' :
			if ( this.draggingData.dragging ) { this.mouseDragEnd( data ) ; }
			break ;
	}
} ;



/*
	/!\ Not sure if it's the correct way to do that /!\
	E.g: Does an element that listen to 'hover' intercept 'click'?
	It is already proven to be bad for the mouse wheel, for ColumnMenu, it would prevent the menu to scroll on mouse wheel
	because the buttons (children) catch the event without doing anything at all with it.

	Mouse event must have event bubbling too.
*/
const COMMON_MOUSE_AWARE_FILTER = element =>
	element.listenerCount( 'click' ) || element.listenerCount( 'clickOut' ) ||
	element.listenerCount( 'rightClick' ) || element.listenerCount( 'middleClick' ) || //element.listenerCount( 'wheel' ) ||
	element.listenerCount( 'dragStart' ) || element.listenerCount( 'drag' ) || element.listenerCount( 'dragEnd' ) ||
	element.listenerCount( 'hover' ) || element.listenerCount( 'leave' ) || element.listenerCount( 'enter' ) ;



// 'clickType' can be 'click' (normal left click), 'rightClick' or 'middleClick'
Document.prototype.mouseClick = function( data , clickType = 'click' ) {
	var matches = this.childrenAt( data.x - this.outputX , data.y - this.outputY , COMMON_MOUSE_AWARE_FILTER ) ;
	//console.error( "\n\n\n\n" , matches ) ;

	if ( ! matches.length ) {
		if ( this.clickOutCandidates.size ) {
			for ( let candidate of this.clickOutCandidates ) {
				// Check that the candidate is still attached
				if ( candidate.document === this ) {
					candidate.emit( 'clickOut' ) ;
				}
			}
			this.clickOutCandidates.clear() ;
		}

		return ;
	}

	if ( this.clickOutCandidates.size ) {
		for ( let candidate of this.clickOutCandidates ) {
			// Check that the candidate is still attached and is not on the click's tree branch
			if ( candidate.document === this && candidate !== matches[ 0 ].element && ! candidate.isAncestorOf( matches[ 0 ].element ) ) {
				candidate.emit( 'clickOut' ) ;
			}
		}

		this.clickOutCandidates.clear() ;
	}

	matches[ 0 ].element.emit( clickType , { x: matches[ 0 ].x , y: matches[ 0 ].y } , matches[ 0 ].element ) ;
} ;



// Also called from within .mouseDrag()
Document.prototype.mouseMotion = function( data , exclude = null ) {
	var starting = false ;

	if ( ! this.motionData.motion ) {
		starting = true ;
		this.mouseMotionStart( data ) ;
	}

	this.motionData.dx = data.x - this.motionData.x ;
	this.motionData.dy = data.y - this.motionData.y ;
	this.motionData.x = data.x ;
	this.motionData.y = data.y ;

	// Newest Gnome-Terminal send motion event even when no progress have been made, this check avoid useless computing.
	if ( ! starting && ! this.motionData.dx && ! this.motionData.dy ) { return ; }

	var matches = this.childrenAt( data.x - this.outputX , data.y - this.outputY , COMMON_MOUSE_AWARE_FILTER ) ;
	//console.error( "\n\n\n\n" , matches ) ;

	if ( ! matches.length ) {
		if ( this.hoverElement ) {
			this.hoverElement.emit( 'leave' ) ;
			this.hoverElement = null ;
		}

		return ;
	}

	if ( matches[ 0 ] !== exclude ) {
		matches[ 0 ].element.emit( 'hover' , { x: matches[ 0 ].x , y: matches[ 0 ].y } , matches[ 0 ].element ) ;
	}

	matches.forEach( match => {
		if ( match.element.listenerCount( 'clickOut' ) ) {
			this.clickOutCandidates.add( match.element ) ;
		}
	} ) ;

	if ( matches[ 0 ].element !== this.hoverElement ) {
		if ( this.hoverElement ) { this.hoverElement.emit( 'leave' ) ; }

		this.hoverElement = matches[ 0 ].element ;
		this.hoverElement.emit( 'enter' ) ;
	}
} ;



Document.prototype.mouseMotionStart = function( data ) {
	var matches ;

	this.motionData.motion = true ;
	this.motionData.xFrom = data.xFrom ;
	this.motionData.yFrom = data.yFrom ;
	this.motionData.x = data.xFrom ;		// We use xFrom/yFrom, .mouseMotion() will update it using x/y, setting dx/dy to the delta
	this.motionData.y = data.yFrom ;

	//this.motionData.element = matches[ 0 ].element ;
	//this.motionData.localDx = matches[ 0 ].x - data.xFrom ;
	//this.motionData.localDy = matches[ 0 ].y - data.yFrom ;
	//matches[ 0 ].element.emit( 'motionStart' , { x: matches[ 0 ].x , y: matches[ 0 ].y } , matches[ 0 ].element ) ;
} ;



Document.prototype.mouseMotionEnd = function() {
	this.motionData.motion = false ;
} ;



Document.prototype.mouseDrag = function( data ) {
	var starting = false ;

	//console.error( "Drag event:" , JSON.stringify( data ) ) ;
	if ( ! this.draggingData.dragging ) {
		starting = true ;
		this.mouseDragStart( data ) ;
	}

	this.draggingData.dx = data.x - this.draggingData.x ;
	this.draggingData.dy = data.y - this.draggingData.y ;
	this.draggingData.x = data.x ;
	this.draggingData.y = data.y ;

	// Newest Gnome-Terminal send drag event even when no progress have been made, this check avoid useless computing.
	if ( ! starting && ! this.draggingData.dx && ! this.draggingData.dy ) { return ; }


	// To send a 'drag' event, the origin of the drag should be on the same element
	if ( this.draggingData.element ) {
		let emitDrag = true ;

		if ( ! this.draggingData.element.outerDrag ) {
			let matches = this.childrenAt( data.x - this.outputX , data.y - this.outputY , COMMON_MOUSE_AWARE_FILTER ) ;
			//console.error( "\tDrag event test:" , this.draggingData.element.debugId() , matches.map( m => m.element.debugId() ) ) ;
			emitDrag = matches.some( m => m.element === this.draggingData.element ) ;
		}

		if ( emitDrag ) {
			this.draggingData.element.emit(
				'drag' ,
				{
					xFrom: this.draggingData.xFrom + this.draggingData.localDx ,
					yFrom: this.draggingData.yFrom + this.draggingData.localDy ,
					x: data.x + this.draggingData.localDx ,
					y: data.y + this.draggingData.localDy ,
					dx: this.draggingData.dx ,
					dy: this.draggingData.dy
				} ,
				this.draggingData.element
			) ;
		}
	}

	// Call .mouseMotion() but exclude the current dragged element
	this.mouseMotion( data , this.draggingData.element ) ;
} ;



Document.prototype.mouseDragStart = function( data ) {
	//console.error( "Drag START event:" , JSON.stringify( data ) ) ;
	var matches ;

	this.draggingData.dragging = true ;
	this.draggingData.xFrom = data.xFrom ;
	this.draggingData.yFrom = data.yFrom ;
	this.draggingData.x = data.xFrom ;		// We use xFrom/yFrom, .mouseDrag() will update it using x/y, setting dx/dy to the delta
	this.draggingData.y = data.yFrom ;

	matches = this.childrenAt( data.xFrom - this.outputX , data.yFrom - this.outputY , COMMON_MOUSE_AWARE_FILTER ) ;

	if ( ! matches.length ) {
		this.draggingData.element = null ;
		this.draggingData.localDx = null ;
		this.draggingData.localDy = null ;

		if ( this.hoverElement ) {
			this.hoverElement.emit( 'leave' ) ;
			this.hoverElement = null ;
		}

		return ;
	}

	this.draggingData.element = matches[ 0 ].element ;
	this.draggingData.localDx = matches[ 0 ].x - data.xFrom ;
	this.draggingData.localDy = matches[ 0 ].y - data.yFrom ;

	matches[ 0 ].element.emit( 'dragStart' , { x: matches[ 0 ].x , y: matches[ 0 ].y } , matches[ 0 ].element ) ;
} ;



Document.prototype.mouseDragEnd = function( data ) {
	//console.error( "Drag END event:" , JSON.stringify( data ) ) ;
	if ( this.draggingData.element ) {
		this.draggingData.element.emit(
			'dragEnd' ,
			{
				xFrom: this.draggingData.xFrom + this.draggingData.localDx ,
				yFrom: this.draggingData.yFrom + this.draggingData.localDy ,
				x: this.draggingData.x + this.draggingData.localDx ,
				y: this.draggingData.y + this.draggingData.localDy
			} ,
			this.draggingData.element
		) ;
	}

	this.draggingData.dragging = false ;
} ;



Document.prototype.mouseWheel = function( data ) {
	//var matches = this.childrenAt( data.x - this.outputX , data.y - this.outputY , COMMON_MOUSE_AWARE_FILTER ) ;
	var matches = this.childrenAt( data.x - this.outputX , data.y - this.outputY , element => element.listenerCount( 'wheel' ) ) ;
	if ( ! matches.length ) { return ; }
	matches[ 0 ].element.emit( 'wheel' , { x: matches[ 0 ].x , y: matches[ 0 ].y , yDirection: data.yDirection } , matches[ 0 ].element ) ;
} ;



Document.prototype.onEventSourceResize = function( width , height ) {
	// Do not resize when on inline mode
	if ( this.inlineTerm ) { return ; }
	//console.error( "Document#onEventSourceResize() " , width , height ) ;

	// Always resize inputDst/viewport to match outputDst (Terminal)
	this.resize( {
		x: 0 ,
		y: 0 ,
		width: width ,
		height: height
	} ) ;

	this.outputWidth = width ;
	this.outputHeight = height ;

	//this.inputDst.clear() ;
	//this.postDrawSelf() ;

	this.draw() ;
} ;


},{"./Container.js":21,"./Element.js":25,"seventh":114}],23:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const ToggleButton = require( './ToggleButton.js' ) ;
const RowMenu = require( './RowMenu.js' ) ;
const ColumnMenuMixed = require( './ColumnMenuMixed.js' ) ;



function DropDownMenu( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	var i , iMax ;

	if ( options.value && typeof options.value === 'object' ) {
		this.setValue( options.value , true ) ;
	}
	else {
		this.value = {} ;
	}

	RowMenu.call( this , options ) ;

	this.initPage() ;

	this.columnMenu = null ;
	this.columnButtonBlurAttr = options.buttonBlurAttr || { bgColor: 'gray' , color: 'white' , bold: true } ;
	this.columnButtonFocusAttr = options.buttonFocusAttr || { bgColor: 'blue' , color: 'white' , bold: true } ;
	this.columnButtonTurnedOnBlurAttr = options.buttonTurnedOnBlurAttr || { bgColor: 'gray' , color: 'white' , bold: true } ;
	this.columnButtonTurnedOnFocusAttr = options.buttonTurnedOnFocusAttr || { bgColor: 'blue' , color: 'white' , bold: true } ;
	this.columnButtonTurnedOffBlurAttr = options.buttonTurnedOffBlurAttr || { bgColor: 'gray' , color: 'white' , dim: true } ;
	this.columnButtonTurnedOffFocusAttr = options.buttonTurnedOffFocusAttr || { bgColor: 'blue' , color: 'white' , dim: true } ;

	this.clearColumnMenuOnSubmit = !! options.clearColumnMenuOnSubmit ;

	this.lastFocusButton = null ;

	this.onClickOut = this.onClickOut.bind( this ) ;
	this.onColumnMenuSubmit = this.onColumnMenuSubmit.bind( this ) ;
	//this.onColumnMenuFocus = this.onColumnMenuFocus.bind( this ) ;
	// Bounded by BaseMenu:
	//this.onButtonSubmit = this.onButtonSubmit.bind( this ) ;
	//this.onButtonFocus = this.onButtonFocus.bind( this ) ;

	this.on( 'clickOut' , this.onClickOut ) ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'DropDownMenu' && ! options.noDraw ) { this.draw() ; }
}

module.exports = DropDownMenu ;
Element.inherit( DropDownMenu , RowMenu ) ;



DropDownMenu.prototype.keyBindings = {
	LEFT: 'previous' ,
	RIGHT: 'next' ,
	ESCAPE: 'clearColumnMenu' ,
	UP: 'clearColumnMenu' ,
	DOWN: 'dropDown' ,
	ENTER: 'submit' ,
	KP_ENTER: 'submit' ,
	ALT_ENTER: 'submit'
} ;



DropDownMenu.prototype.dropDown = function( index , x , y , submittedButtonValue , submittedButtonAction , button ) {
	var itemsDef = this.itemsDef[ index ].items ;

	if ( this.columnMenu ) {
		// Already dropped down? Nothing to do!
		if ( this.columnMenu.index === index ) { return ; }
		this.clearColumnMenu() ;
	}

	// No submenu, leave now...
	if ( ! itemsDef || ! itemsDef.length ) {
		if ( submittedButtonValue && this.itemsDef[ index ].topSubmit ) {
			// Top-button without submenu that have a 'topSubmit' flag on submits themselves
			this.emit( 'submit' , submittedButtonValue , submittedButtonAction , this , button ) ;
		}

		return ;
	}

	var hasToggle = itemsDef.some( def => def.type === 'toggle' ) ;

	// Make the ColumnMenu a child of the button, so focus cycle will work as expected
	var columnMenuOptions = {
		internal: true ,
		parent: this.children[ index ] ,
		x: x ,
		y: y ,
		width: this.outputWidth - x ,
		leftPadding: ' ' ,
		rightPadding: ' ' ,
		items: itemsDef ,
		value: this.value ,
		buttonFocusAttr: this.columnButtonFocusAttr ,
		buttonBlurAttr: this.columnButtonBlurAttr ,
		buttonTurnedOnBlurAttr: this.columnButtonTurnedOnBlurAttr ,
		buttonTurnedOnFocusAttr: this.columnButtonTurnedOnFocusAttr ,
		buttonTurnedOffBlurAttr: this.columnButtonTurnedOffBlurAttr ,
		buttonTurnedOffFocusAttr: this.columnButtonTurnedOffFocusAttr
	} ;

	if ( hasToggle ) {
		columnMenuOptions.leftPadding = '   ' ;
		//columnMenuOptions.turnedOnLeftPadding = ' ✓ ' ;
		//columnMenuOptions.turnedOffLeftPadding = ' ✗ ' ;
		columnMenuOptions.turnedOnLeftPadding = ' ☑ ' ;
		columnMenuOptions.turnedOffLeftPadding = ' ☐ ' ;
	}

	this.columnMenu = new ColumnMenuMixed( columnMenuOptions ) ;

	this.columnMenu.on( 'submit' , this.onColumnMenuSubmit ) ;
	//this.columnMenu.on( 'focus' , this.onColumnMenuFocus ) ;

	// unused ATM
	//this.columnMenu.menuIndex = index ;

	//this.document.giveFocusTo( this.columnMenu , 'delegate' ) ;
} ;



DropDownMenu.prototype.clearColumnMenu = function( focusHeadButton = false ) {
	if ( ! this.columnMenu ) { return false ; }
	this.columnMenu.destroy() ;
	this.columnMenu = null ;

	if ( focusHeadButton && this.lastFocusButton ) {
		this.document.giveFocusTo( this.lastFocusButton , 'clear' ) ;
	}

	return true ;
} ;



DropDownMenu.prototype.setValue = function( value , noDraw ) {
	if ( ! value || typeof value !== 'object' ) { return ; }

	this.value = {} ;
	for ( let key in value ) { this.value[ key ] = !! value[ key ] ; }

	if ( this.columnMenu ) { this.columnMenu.setValue( value , noDraw ) ; }
} ;



DropDownMenu.prototype.setKeyValue = function( key , value , noDraw ) {
	if ( ! key ) { return ; }
	this.value[ key ] = !! value ;
	if ( this.columnMenu ) { this.columnMenu.setKeyValue( key , value , noDraw ) ; }
} ;



DropDownMenu.prototype.setDropDownItem = function( topItemValue , dropDownItemValue , itemOptions ) {
	var topItem = this.itemsDef.find( e => e.value === topItemValue ) ;
	if ( ! topItem ) { return false ; }
	var dropDownItem = topItem.items && topItem.items.find( e => e.value === dropDownItemValue ) ;
	if ( ! dropDownItem ) { return false ; }
	this.clearColumnMenu() ;
	Object.assign( dropDownItem , itemOptions ) ;
	return true ;
} ;



DropDownMenu.prototype.onClickOut = function( buttonValue , data , button ) {
	this.clearColumnMenu() ;
} ;



/*	Does not work: focus is on column-menu children, we should have a way to make focus event bubbling up
DropDownMenu.prototype.onColumnMenuFocus = function( focus , type ) {
	if ( ! focus && this.columnMenu ) {
		this.clearColumnMenu() ;
	}
} ;
*/



DropDownMenu.prototype.onButtonSubmit = function( buttonValue , action , button ) {
	this.dropDown( button.childId , button.outputX , button.outputY + 1 , buttonValue , action , button ) ;
} ;



DropDownMenu.prototype.onButtonFocus = function( focus , type , button ) {
	this.lastFocusButton = button ;

	if ( focus && type !== 'clear' ) {
		this.dropDown( button.childId , button.outputX , button.outputY + 1 ) ;
	}
} ;



DropDownMenu.prototype.onColumnMenuSubmit = function( buttonValue , action , columnMenu , button ) {
	//console.error( "DropDownMenu#onColumnMenuSubmit()" , buttonValue , action , columnMenu?.elementType , button?.elementType ) ;
	if ( button instanceof ToggleButton ) {
		//console.error( ">>> is ToggleButton" ) ;
		if ( button.key ) {
			this.value[ button.key ] = button.value ;
		}

		if ( this.clearColumnMenuOnSubmit ) {
			setTimeout( () => this.clearColumnMenu( true ) , 400 ) ;
		}
	}
	else {
		columnMenu.once( 'blinked' , ( buttonValue_ , reserved , columnMenu_ , button_ ) => {
			if ( this.clearColumnMenuOnSubmit ) { this.clearColumnMenu( true ) ; }
			this.emit( 'blinked' , buttonValue_ , reserved , this , button_ ) ;
		} ) ;
	}

	this.emit( 'submit' , buttonValue , action , this , button ) ;
} ;



const userActions = DropDownMenu.prototype.userActions ;

userActions.previous = function() {
	this.focusChild = this.focusPreviousChild() ;
	//this.clearColumnMenu() ;
} ;

userActions.next = function() {
	this.focusChild = this.focusNextChild() ;
	//this.clearColumnMenu() ;
} ;

userActions.dropDown = function() {
	if ( this.columnMenu ) {
		this.columnMenu.focusNextChild() ;
	}
	else if ( this.lastFocusButton ) {
		this.dropDown( this.lastFocusButton.childId , this.lastFocusButton.outputX , this.lastFocusButton.outputY + 1 ) ;
	}

	//this.focusChild = this.focusNextChild() ;
	//this.clearColumnMenu() ;
} ;

userActions.clearColumnMenu = function() {
	// Bubble up only if something was cleared
	return this.clearColumnMenu( true ) ;
} ;


},{"./ColumnMenuMixed.js":19,"./Element.js":25,"./RowMenu.js":33,"./ToggleButton.js":40}],24:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const TextBox = require( './TextBox.js' ) ;

const string = require( 'string-kit' ) ;
const Promise = require( 'seventh' ) ;



function EditableTextBox( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	if ( options.value ) { options.content = options.value ; }

	TextBox.call( this , options ) ;

	this.onFocus = this.onFocus.bind( this ) ;
	this.onDragEnd = this.onDragEnd.bind( this ) ;
	this.onMiddleClick = this.onMiddleClick.bind( this ) ;

	this.debounceTimeout = options.debounceTimeout ?? 100 ;
	this.editionUpdateDebounced =
		this.debounceTimeout ? Promise.debounceUpdate( { delay: this.debounceTimeout } , this.editionUpdate ) :
		this.editionUpdate ;

	if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; }

	// Editable textbox get extraScrolling by default
	this.extraScrolling = options.extraScrolling !== undefined ? !! options.extraScrolling : true ;

	this.updateStatus() ;

	this.on( 'focus' , this.onFocus ) ;
	this.on( 'dragEnd' , this.onDragEnd ) ;
	this.on( 'middleClick' , this.onMiddleClick ) ;

	if ( this.setContent === EditableTextBox.prototype.setContent ) {
		this.setContent( options.content , options.contentHasMarkup , true ) ;
	}

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'EditableTextBox' && ! options.noDraw ) { this.draw() ; }
}

module.exports = EditableTextBox ;
Element.inherit( EditableTextBox , TextBox ) ;



EditableTextBox.prototype.needInput = true ;



EditableTextBox.prototype.keyBindings = {
	CTRL_K: 'meta' ,
	ENTER: 'newLine' ,
	KP_ENTER: 'newLine' ,
	BACKSPACE: 'backDelete' ,
	DELETE: 'delete' ,
	CTRL_DELETE: 'deleteLine' ,
	LEFT: 'backward' ,
	RIGHT: 'forward' ,
	CTRL_LEFT: 'startOfWord' ,
	CTRL_RIGHT: 'endOfWord' ,
	UP: 'up' ,
	DOWN: 'down' ,
	HOME: 'startOfLine' ,
	END: 'endOfLine' ,
	TAB: 'tab' ,
	PAGE_UP: 'scrollUp' ,
	PAGE_DOWN: 'scrollDown' ,
	META_HOME: 'scrollToCursor' ,
	CTRL_B: 'startOfSelection' ,
	CTRL_E: 'endOfSelection' ,
	SHIFT_LEFT: 'expandSelectionBackward' ,
	SHIFT_RIGHT: 'expandSelectionForward' ,
	SHIFT_UP: 'expandSelectionUp' ,
	SHIFT_DOWN: 'expandSelectionDown' ,
	CTRL_SHIFT_LEFT: 'expandSelectionStartOfWord' ,
	CTRL_SHIFT_RIGHT: 'expandSelectionEndOfWord' ,

	// T for Transfer
	CTRL_T: 'moveSelection' ,
	ALT_T: 'copyToDocumentClipboard' ,
	META_T: 'copyToSystemClipboard' ,
	// P for Paste / Put
	CTRL_P: 'pasteSelection' ,
	ALT_P: 'pasteDocumentClipboard' ,
	META_P: 'pasteSystemClipboard' ,
	// D for Delete
	CTRL_D: 'deleteSelection' ,
	ALT_D: 'clearDocumentClipboard' ,
	META_D: 'clearSystemClipboard'
} ;



EditableTextBox.prototype.insert = function( str , selectIt = false , internal = false ) {
	let x = this.textBuffer.cx ,
		y = this.textBuffer.cy ;

	let count = this.textBuffer.insert( str , this.textAttr ) ;

	if ( ! internal ) {
		if ( selectIt ) {
			this.textBuffer.setSelectionRegion( {
				xmin: x , ymin: y , xmax: this.textBuffer.cx , ymax: this.textBuffer.cy
			} ) ;
		}

		this.editionUpdateDebounced() ;
	}
	else if ( selectIt ) {
		this.textBuffer.setSelectionRegion( {
			xmin: x , ymin: y , xmax: this.textBuffer.cx , ymax: this.textBuffer.cy
		} ) ;
	}

	this.emit( 'change' , {
		type: 'insert' ,
		insertedString: str ,
		count ,
		internal ,
		startPosition: { x , y } ,
		endPosition: { x: this.textBuffer.cx , y: this.textBuffer.cy }
	} ) ;
} ;



EditableTextBox.prototype.deleteSelection = function( internal = false ) {
	if ( ! this.textBuffer.selectionRegion ) { return ; }

	var { xmin , xmax , ymin , ymax } = this.textBuffer.selectionRegion ;
	var deleted = this.textBuffer.deleteSelection( true ) ;

	if ( deleted && deleted.count ) {
		if ( ! internal ) {
			this.textBuffer.cx = xmin ;
			this.textBuffer.cy = ymin ;

			this.editionUpdateDebounced() ;
		}

		this.emit( 'change' , {
			type: 'delete' ,
			count: deleted.count ,
			internal ,
			deletedString: deleted.string ,
			startPosition: { x: xmin , y: ymin } ,
			endPosition: { x: xmin , y: ymin }
		} ) ;
	}
} ;



EditableTextBox.prototype.deleteRegion = function( region , internal = false ) {
	var { xmin , xmax , ymin , ymax } = region ;
	var deleted = this.textBuffer.deleteRegion( region , true ) ;

	if ( deleted && deleted.count ) {
		if ( ! internal ) {
			this.textBuffer.cx = xmin ;
			this.textBuffer.cy = ymin ;

			this.editionUpdateDebounced() ;
		}

		this.emit( 'change' , {
			type: 'delete' ,
			count: deleted.count ,
			internal ,
			deletedString: deleted.string ,
			startPosition: { x: xmin , y: ymin } ,
			endPosition: { x: xmin , y: ymin }
		} ) ;
	}
} ;



EditableTextBox.prototype.drawSelfCursor = function() {
	this.textBuffer.drawCursor() ;
} ;



EditableTextBox.prototype.getValue = TextBox.prototype.getContent ;



EditableTextBox.prototype.setValue = function( value , dontDraw ) {
	return this.setContent( value , false , dontDraw ) ;
} ;



// Called when something was edited, usually requiring to run state machine, auto-scroll and draw.
// Usually, editionUpdateDebounced is called instead.
// Sync, but return a promise (needed for Promise.debounceUpdate())
EditableTextBox.prototype.editionUpdate = function() {
	if ( this.stateMachine ) {
		this.textBuffer.runStateMachine() ;
	}

	this.autoScrollAndDraw() ;

	return Promise.resolved ;
} ;



EditableTextBox.prototype.onFocus = function( focus , type ) {
	this.updateStatus() ;
	this.draw() ;
} ;



EditableTextBox.prototype.onClick = function( data ) {
	//console.error( "ETB Click:" , data ) ;
	if ( this.hasFocus ) {
		this.textBuffer.moveTo( data.x - this.scrollX , data.y - this.scrollY ) ;

		if ( this.textBuffer.selectionRegion ) {
			this.textBuffer.resetSelectionRegion() ;
			this.draw() ;
		}
		else {
			this.drawCursor() ;
		}
	}
	else {
		this.document.giveFocusTo( this , 'select' ) ;
	}
} ;



EditableTextBox.prototype.onDragEnd = function( data ) {
	if ( this.hasFocus ) {
		if ( data.yFrom < data.y || ( data.yFrom === data.y && data.xFrom <= data.x ) ) {
			// Forward selection, put the cursor one cell to the right
			this.textBuffer.moveTo( data.x - this.scrollX + 1 , data.y - this.scrollY ) ;
		}
		else {
			// Backward selection, put the cursor one the current cell
			this.textBuffer.moveTo( data.x - this.scrollX , data.y - this.scrollY ) ;
		}

		this.drawCursor() ;
	}
} ;



EditableTextBox.prototype.onMiddleClick = function( data ) {
	if ( ! this.hasFocus ) {
		this.document.giveFocusTo( this , 'select' ) ;
	}

	// Do not moveTo, it's quite boring
	//this.textBuffer.moveTo( data.x , data.y ) ;

	if ( this.document ) {
		this.document.getSystemClipboard( 'primary' ).then( str => {
			if ( str ) { this.insert( str ) ; }
			//else { this.drawCursor() ; }
		} )
			.catch( () => undefined ) ;
	}
	//else { this.drawCursor() ; }
} ;



// There isn't much to do ATM
EditableTextBox.prototype.updateStatus = function() {} ;



const userActions = EditableTextBox.prototype.userActions ;

userActions.character = function( key ) {
	var x = this.textBuffer.cx ,
		y = this.textBuffer.cy ;

	var count = this.textBuffer.insert( key , this.textAttr ) ;

	this.editionUpdateDebounced() ;

	this.emit( 'change' , {
		type: 'insert' ,
		insertedString: key ,
		count ,
		internal: false ,
		startPosition: { x , y } ,
		endPosition: { x: this.textBuffer.cx , y: this.textBuffer.cy }
	} ) ;
} ;

userActions.newLine = function() {
	var insertedString = '\n' ,
		count = 1 ,
		x = this.textBuffer.cx ,
		y = this.textBuffer.cy ;

	this.textBuffer.newLine() ;

	this.editionUpdateDebounced() ;

	this.emit( 'change' , {
		type: 'insert' ,
		insertedString ,
		count ,
		internal: false ,
		startPosition: { x , y } ,
		endPosition: { x: this.textBuffer.cx , y: this.textBuffer.cy }
	} ) ;
} ;

userActions.tab = function() {
	var x = this.textBuffer.cx ,
		y = this.textBuffer.cy ;

	this.textBuffer.insert( '\t' , this.textAttr ) ;

	this.editionUpdateDebounced() ;

	this.emit( 'change' , {
		type: 'insert' ,
		insertedString: '\t' ,
		count: 1 ,
		internal: false ,
		startPosition: { x , y } ,
		endPosition: { x: this.textBuffer.cx , y: this.textBuffer.cy }
	} ) ;
} ;

userActions.delete = function() {
	var x = this.textBuffer.cx ,
		y = this.textBuffer.cy ,
		selectionRegion = this.textBuffer.selectionRegion ;

	if ( selectionRegion && selectionRegion.ymin === y && selectionRegion.xmin === x ) {
		// Instead, delete the whole selection
		this.deleteSelection() ;
		return ;
	}

	var deleted = this.textBuffer.delete( 1 , true ) ;

	this.editionUpdateDebounced() ;

	if ( deleted && deleted.count ) {
		this.emit( 'change' , {
			type: 'delete' ,
			count: deleted.count ,
			internal: false ,
			deletedString: deleted.string ,
			startPosition: { x , y } ,
			endPosition: { x: this.textBuffer.cx , y: this.textBuffer.cy }
		} ) ;
	}
} ;

userActions.backDelete = function() {
	var x = this.textBuffer.cx ,
		y = this.textBuffer.cy ,
		selectionRegion = this.textBuffer.selectionRegion ;

	if ( selectionRegion ) {
		let coord = this.textBuffer.oneStepBackward() ;
		if ( selectionRegion.ymax === coord.y && selectionRegion.xmax === coord.x ) {
			// Instead, delete the whole selection
			this.deleteSelection() ;
			return ;
		}
	}

	var deleted = this.textBuffer.backDelete( 1 , true ) ;

	this.editionUpdateDebounced() ;

	if ( deleted && deleted.count ) {
		this.emit( 'change' , {
			type: 'backDelete' ,
			count: deleted.count ,
			internal: false ,
			deletedString: deleted.string ,
			startPosition: { x , y } ,
			endPosition: { x: this.textBuffer.cx , y: this.textBuffer.cy }
		} ) ;
	}
} ;

userActions.deleteLine = function() {
	var y = this.textBuffer.cy ;

	var deleted = this.textBuffer.deleteLine( true ) ;

	this.editionUpdateDebounced() ;

	if ( deleted && deleted.count ) {
		this.emit( 'change' , {
			type: 'delete' ,
			count: deleted.count ,
			internal: false ,
			deletedString: deleted.string ,
			startPosition: { x: 0 , y } ,
			endPosition: { x: 0 , y: this.textBuffer.cy }
		} ) ;
	}
} ;

userActions.backward = function() {
	this.textBuffer.moveBackward() ;
	this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.forward = function() {
	this.textBuffer.moveForward() ;
	this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.startOfWord = function() {
	this.textBuffer.moveToStartOfWord() ;
	this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.endOfWord = function() {
	this.textBuffer.moveToEndOfWord() ;
	this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.startOfLine = function() {
	this.textBuffer.moveToColumn( 0 ) ;
	this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

// Start of line, but if already at cx = 0, move to the first non-white char (skip indent),
// Also known as “smart home”.
userActions.smartStartOfLine = function() {
	if ( this.textBuffer.cx !== 0 ) {
		this.textBuffer.moveToColumn( 0 ) ;
	}
	else {
		let cy = this.textBuffer.cy ;
		this.textBuffer.moveForward( ( char , x , y ) => y !== cy || ( char !== ' ' && char !== '\t' ) ) ;

		if ( this.textBuffer.cy !== cy ) {
			// Line has changed, it was an empty line: fallback!
			this.textBuffer.moveTo( 0 , cy ) ;
		}
	}

	this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.endOfLine = function() {
	this.textBuffer.moveToEndOfLine() ;
	this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.down = function() {
	this.textBuffer.moveDown() ;
	this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.up = function() {
	this.textBuffer.moveUp() ;
	this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.left = function() {
	this.textBuffer.moveLeft() ;
	this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.right = function() {
	this.textBuffer.moveRight() ;
	this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.scrollUp = function() {
	var dy = Math.ceil( this.outputHeight / 2 ) ;
	this.textBuffer.move( 0 , -dy ) ;
	this.scroll( 0 , dy , true ) ; this.autoScrollAndDraw() ;
	//this.scroll( 0 , dy ) ; this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.scrollDown = function() {
	var dy = -Math.ceil( this.outputHeight / 2 ) ;
	this.textBuffer.move( 0 , -dy ) ;
	this.scroll( 0 , dy , true ) ; this.autoScrollAndDraw() ;
	//this.scroll( 0 , dy ) ; this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.scrollTop = function() {
	this.textBuffer.moveTo( 0 , 0 ) ;
	this.scrollTo( 0 , 0 ) ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.scrollBottom = function() {
	this.textBuffer.moveTo( 0 , this.textBuffer.buffer.length - 1 ) ;
	this.autoScrollAndSmartDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.scrollToCursor = function() {
	this.autoScrollAndDraw() ;
} ;

userActions.expandSelectionBackward = function() {
	var selection = this.textBuffer.selectionRegion ,
		cx = this.textBuffer.cx ,
		cy = this.textBuffer.cy ,
		oneStepBackward = this.textBuffer.oneStepBackward() ;

	if ( selection && selection.xmin === cx && selection.ymin === cy ) {
		// Can expand
		this.textBuffer.moveBackward() ;
		this.textBuffer.startOfSelection() ;
	}
	else if ( selection && selection.xmax === oneStepBackward.x && selection.ymax === oneStepBackward.y ) {
		// Can contract
		this.textBuffer.moveBackward() ;
		this.textBuffer.endOfSelection() ;
	}
	else {
		// Start a new selection
		this.textBuffer.endOfSelection() ;
		this.textBuffer.moveBackward() ;
		this.textBuffer.startOfSelection() ;
	}

	this.autoScrollAndDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.expandSelectionStartOfWord = function() {
	var selection = this.textBuffer.selectionRegion ,
		cx = this.textBuffer.cx ,
		cy = this.textBuffer.cy ,
		oneStepBackward = this.textBuffer.oneStepBackward() ;

	if ( selection && selection.xmin === cx && selection.ymin === cy ) {
		// Can expand
		this.textBuffer.moveToStartOfWord() ;
		this.textBuffer.startOfSelection() ;
	}
	else if ( selection && selection.xmax === oneStepBackward.x && selection.ymax === oneStepBackward.y ) {
		// Can contract
		this.textBuffer.moveToStartOfWord() ;
		this.textBuffer.endOfSelection() ;
	}
	else {
		// Start a new selection
		this.textBuffer.endOfSelection() ;
		this.textBuffer.moveToStartOfWord() ;
		this.textBuffer.startOfSelection() ;
	}

	this.autoScrollAndDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.expandSelectionUp = function() {
	var selection = this.textBuffer.selectionRegion ,
		cx = this.textBuffer.cx ,
		cy = this.textBuffer.cy ,
		oneStepBackward = this.textBuffer.oneStepBackward() ;

	if ( selection && selection.xmin === cx && selection.ymin === cy ) {
		// Can expand
		this.textBuffer.moveUp() ;
		this.textBuffer.startOfSelection() ;
	}
	else if (
		selection && selection.xmax === oneStepBackward.x && selection.ymax === oneStepBackward.y
		// Check that there is at least one line of selection
		&& ( selection.ymin < oneStepBackward.y - 1 || ( selection.ymin === oneStepBackward.y - 1 && selection.xmin <= oneStepBackward.x ) )
	) {
		// Can contract
		this.textBuffer.moveUp() ;
		this.textBuffer.endOfSelection() ;
	}
	else {
		// Start a new selection
		this.textBuffer.endOfSelection() ;
		this.textBuffer.moveUp() ;
		this.textBuffer.startOfSelection() ;
	}

	this.autoScrollAndDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.expandSelectionForward = function() {
	var selection = this.textBuffer.selectionRegion ,
		cx = this.textBuffer.cx ,
		cy = this.textBuffer.cy ,
		oneStepBackward = this.textBuffer.oneStepBackward() ;

	if ( selection && selection.xmax === oneStepBackward.x && selection.ymax === oneStepBackward.y ) {
		// Can expand
		this.textBuffer.moveForward() ;
		this.textBuffer.endOfSelection() ;
	}
	else if ( selection && selection.xmin === cx && selection.ymin === cy ) {
		// Can contract
		this.textBuffer.moveForward() ;
		this.textBuffer.startOfSelection() ;
	}
	else {
		// Start a new selection
		this.textBuffer.startOfSelection() ;
		this.textBuffer.moveForward() ;
		this.textBuffer.endOfSelection() ;
	}

	this.autoScrollAndDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.expandSelectionEndOfWord = function() {
	var selection = this.textBuffer.selectionRegion ,
		cx = this.textBuffer.cx ,
		cy = this.textBuffer.cy ,
		oneStepBackward = this.textBuffer.oneStepBackward() ;

	if ( selection && selection.xmax === oneStepBackward.x && selection.ymax === oneStepBackward.y ) {
		// Can expand
		this.textBuffer.moveToEndOfWord() ;
		this.textBuffer.endOfSelection() ;
	}
	else if ( selection && selection.xmin === cx && selection.ymin === cy ) {
		// Can contract
		this.textBuffer.moveToEndOfWord() ;
		this.textBuffer.startOfSelection() ;
	}
	else {
		// Start a new selection
		this.textBuffer.startOfSelection() ;
		this.textBuffer.moveToEndOfWord() ;
		this.textBuffer.endOfSelection() ;
	}

	this.autoScrollAndDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.expandSelectionDown = function() {
	var selection = this.textBuffer.selectionRegion ,
		cx = this.textBuffer.cx ,
		cy = this.textBuffer.cy ,
		oneStepBackward = this.textBuffer.oneStepBackward() ;

	if ( selection && selection.xmax === oneStepBackward.x && selection.ymax === oneStepBackward.y ) {
		// Can expand
		this.textBuffer.moveDown() ;
		this.textBuffer.endOfSelection() ;
	}
	else if ( selection && selection.xmin === cx && selection.ymin === cy
		&& ( selection.ymax > cy + 1 || ( selection.ymax === cy + 1 && selection.xmax >= cx - 1 ) )
	) {
		// Can contract
		this.textBuffer.moveDown() ;
		this.textBuffer.startOfSelection() ;
	}
	else {
		// Start a new selection
		this.textBuffer.startOfSelection() ;
		this.textBuffer.moveDown() ;
		this.textBuffer.endOfSelection() ;
	}

	this.autoScrollAndDraw() ;
	this.emit( 'cursorMove' ) ;
} ;

userActions.startOfSelection = function() {
	this.textBuffer.startOfSelection() ;
	this.draw() ;
} ;

userActions.endOfSelection = function() {
	this.textBuffer.endOfSelection() ;
	this.draw() ;
} ;

userActions.moveSelection = function() {
	var str = this.textBuffer.getSelectionText() ;
	if ( ! str ) { return ; }

	this.deleteSelection( true ) ;
	this.insert( str , true ) ;
} ;

userActions.pasteSelection = function() {
	var str = this.textBuffer.getSelectionText() ;
	if ( str ) { this.insert( str ) ; }
} ;

userActions.pasteDocumentClipboard = function() {
	if ( this.document ) {
		let str = this.document.getDocumentClipboard() ;
		if ( str && typeof str === 'string' ) {
			this.insert( str ) ;
		}
	}
} ;

userActions.pasteSystemClipboard = function() {
	if ( this.document ) {
		this.document.getSystemClipboard()
			.then( str => {
				if ( str && typeof str === 'string' ) {
					this.insert( str ) ;
				}
			} )
			.catch( () => undefined ) ;
	}
} ;

userActions.deleteSelection = function() {
	this.deleteSelection() ;
} ;

userActions.clearDocumentClipboard = function() {
	if ( this.document ) {
		this.document.clearDocumentClipboard( this.textBuffer.getSelectionText() ) ;
	}
} ;

userActions.clearSystemClipboard = function() {
	if ( this.document ) {
		this.document.clearSystemClipboard( this.textBuffer.getSelectionText() ).catch( () => undefined ) ;
	}
} ;


},{"./Element.js":25,"./TextBox.js":38,"seventh":114,"string-kit":133}],25:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const misc = require( '../misc.js' ) ;
const string = require( 'string-kit' ) ;
const NextGenEvents = require( 'nextgen-events' ) ;

// Avoid requiring Document at top-level, it could cause circular require troubles
//const Document = require( './Document.js' ) ;

var autoId = 0 ;



function Element( options = {} ) {
	this.setInterruptible( true ) ;

	this.uid = autoId ++ ;	// Useful for debugging
	this.parent = options.parent && options.parent.elementType ? options.parent : null ;
	//console.error( "Creating " + this.elementType + " #" + this.uid + ( this.parent ? " (from parent " + this.parent.elementType + " #" + this.parent.uid + ")" : '' ) ) ;

	this.document = null ;
	this.destroyed = false ;

	// Event handler bindings
	this.onKey = this.onKey.bind( this ) ;

	this.inlineTerm = options.inlineTerm || null ;	// inline mode, with this terminal as output
	this.strictInline = !! (
		this.inlineTerm && this.strictInlineSupport
		&& ( options.strictInline || options.strictInline === undefined )
	) ;
	this.restoreCursorAfterDraw = !! ( this.inlineTerm && this.inlineCursorRestoreAfterDraw && ! this.strictInline ) ;

	this.outputDst = options.outputDst || ( options.parent && options.parent.inputDst ) ,
	this.inputDst = null ;
	this.label = options.label || '' ;
	this.key = options.key || null ;

	if ( this.value === undefined ) {
		// Because it can be set already by the derivative class before calling Element (preprocessing of userland values)
		this.value = options.value === undefined ? null : options.value ;
	}

	this.childId = options.childId === undefined ? null : options.childId ;	// An ID given to this element by its parent, often the index in its children array
	this.def = options.def || null ;	// internal usage, store the original def object that created the item, if any...
	this.hidden = !! options.hidden ;	// hidden: not visible and no interaction possible with this element, it also affects children
	this.disabled = !! options.disabled ;	// disabled: mostly for user-input, the element is often grayed and unselectable, effect depends on the element's type

	// Default value (ensure it's not already set)
	this.content = this.content ?? '' ;
	this.contentHasMarkup = this.contentHasMarkup ?? false ;
	this.contentWidth = this.contentWidth ?? 0 ;
	this.contentHeight = this.contentHeight ?? 0 ;

	if ( this.setContent === Element.prototype.setContent ) {
		this.setContent( options.content || '' , options.contentHasMarkup , true , true ) ;
	}

	this.meta = options.meta ;	// associate data to the element for userland business logic

	this.autoWidth = + options.autoWidth || 0 ;
	this.autoHeight = + options.autoHeight || 0 ;
	this.outputX = options.outputX || options.x || 0 ;
	this.outputY = options.outputY || options.y || 0 ;
	this.savedZIndex = this.zIndex = options.zIndex || options.z || 0 ;
	this.interceptTempZIndex = !! options.interceptTempZIndex ;	// intercept child .topZ()/.bottomZ()/.restoreZ()
	this.outputWidth =
		this.autoWidth && this.outputDst ? Math.round( this.outputDst.width * this.autoWidth ) :
		options.outputWidth ? options.outputWidth :
		options.width ? options.width :
		this.strictInline ? this.inlineTerm.width :
		1 ;
	this.outputHeight =
		this.autoHeight && this.outputDst ? Math.round( this.outputDst.height * this.autoHeight ) :
		options.outputHeight ? options.outputHeight :
		options.height ? options.height :
		this.strictInline ? this.inlineTerm.height :
		1 ;
	this.contentAdaptativeWidth = this.contentAdaptativeWidth ?? !! options.contentAdaptativeWidth ;
	this.contentAdaptativeHeight = this.contentAdaptativeHeight ?? !! options.contentAdaptativeHeight ;

	// Used by .updateDraw()
	this.needOuterDraw = false ;

	this.savedCursorX = 0 ;
	this.savedCursorY = 0 ;

	this.hasFocus = false ;
	this.children = [] ;
	this.zChildren = [] ;	// like children, but ordered by zIndex

	// Children needs an inputDst, by default, everything is the same as for output (except for Container)
	this.inputDst = this.outputDst ;
	this.inputX = this.outputX ;
	this.inputY = this.outputY ;
	this.inputWidth = this.outputWidth ;
	this.inputHeight = this.outputHeight ;

	if ( this.parent ) { this.parent.attach( this , options.id ) ; }

	if ( options.shortcuts && this.document ) {
		if ( Array.isArray( options.shortcuts ) ) { this.document.createShortcuts( this , ... options.shortcuts ) ; }
		else { this.document.createShortcuts( this , options.shortcuts ) ; }
	}
}

module.exports = Element ;

Element.prototype = Object.create( NextGenEvents.prototype ) ;
Element.prototype.constructor = Element ;
Element.prototype.elementType = 'Element' ;

const termkit = require( '../termkit.js' ) ;



// Destroy the element and all its children, detaching them and removing listeners
Element.prototype.destroy = function( isSubDestroy = false , noDraw = false ) {
	if ( this.destroyed ) { return ; }
	//console.error( "Destroying" , this.elementType , this.uid , this.key ) ;

	var i , iMax , document = this.document ;

	// Destroy children first
	for ( i = 0 , iMax = this.children.length ; i < iMax ; i ++ ) {
		this.children[ i ].destroy( true ) ;
	}

	this.removeAllListeners() ;
	this.children.length = 0 ;
	this.zChildren.length = 0 ;
	this.document.removeElementShortcuts( this ) ;

	if ( ! isSubDestroy ) {
		this.detach( noDraw ) ;
		if ( this.inlineTerm && document !== this ) { document.destroy() ; }
	}
	else {
		delete this.document.elements[ this.id ] ;
		this.id = null ;
		this.parent = null ;
		this.document = null ;
	}

	this.destroyed = true ;
} ;

// User API
Element.prototype.destroyNoRedraw = function() { return this.destroy( undefined , true ) ; } ;



Element.inherit = function( Class , FromClass = Element ) {
	Class.prototype = Object.create( FromClass.prototype ) ;
	Class.prototype.constructor = Class ;
	Class.prototype.elementType = Class.name ;

	Class.prototype.userActions = Object.create( FromClass.prototype.userActions ) ;
	Class.prototype.userActions.__parent = FromClass.prototype.userActions ;
} ;



// Debug function
Element.prototype.debugId = function() { return this.elementType + '#' + this.uid ; } ;



Element.prototype.show = function( noDraw = false ) {
	if ( ! this.hidden ) { return this ; }
	this.hidden = false ;
	if ( ! noDraw ) { this.outerDraw() ; }
	return this ;
} ;



Element.prototype.hide = function( noDraw = false ) {
	if ( this.hidden ) { return this ; }
	this.hidden = true ;

	if ( ! noDraw ) {
		// .outerDraw() with the 'force' option on, because .outerDraw() does nothing if the element is hidden, but here we want to clear it from its parent
		this.outerDraw( true ) ;
	}

	return this ;
} ;



// Clear the Element, destroy all children
Element.prototype.clear = function() {
	var i , iMax ;

	// Destroy children first
	for ( i = 0 , iMax = this.children.length ; i < iMax ; i ++ ) {
		this.children[ i ].destroy( true ) ;
	}

	this.children.length = 0 ;
	this.zChildren.length = 0 ;
	this.draw() ;
} ;



Element.prototype.attach = function( child , id ) {
	// Insert it if it is not already a child
	if ( this.children.indexOf( child ) === -1 ) {
		child.parent = this ;
		this.children.push( child ) ;
		this.zInsert( child ) ;
		//this.zSort() ;

		//this.document.assignId( this , options.id ) ;

		// Re-assign the child's outputDst to this inputDst
		child.outputDst = this.inputDst ;
		if ( ! child.inputDst ) { child.inputDst = child.outputDst ; }

		if ( this.document !== child.document ) {
			child.recursiveFixAttachment( this.document , id ) ;
		}
	}

	// /!\ Draw? /!\

	return this ;
} ;



Element.prototype.attachTo = function( parent , id ) {
	if ( parent.elementType ) { parent.attach( this , id ) ; }
	return this ;
} ;



Element.prototype.recursiveFixAttachment = function( document , id = this.id ) {
	var i , iMax ;

	// Can be null when in inline mode, or when detaching
	if ( document ) { document.assignId( this , id ) ; }
	else if ( this.document ) { this.document.unassignId( this , this.id ) ; }	// force actual id here
	else { this.id = null ; }

	this.document = document || null ;

	if ( this.parent ) {
		// Re-assign the outputDst to the parent's inputDst
		this.outputDst = this.parent.inputDst ;
		if ( ! this.inputDst ) { this.inputDst = this.outputDst ; }
	}

	for ( i = 0 , iMax = this.children.length ; i < iMax ; i ++ ) {
		this.children[ i ].recursiveFixAttachment( document ) ;
	}
} ;




Element.prototype.detach = function( noDraw = false ) {
	var index , parent = this.parent ;

	// Already detached
	if ( ! parent ) { return ; }

	index = parent.children.indexOf( this ) ;
	if ( index >= 0 ) { parent.children.splice( index , 1 ) ; }

	index = parent.zChildren.indexOf( this ) ;
	if ( index >= 0 ) { parent.zChildren.splice( index , 1 ) ; }

	delete this.document.elements[ this.id ] ;
	this.parent = null ;
	this.recursiveFixAttachment( null ) ;

	// Redraw
	if ( ! noDraw ) {
		// /!\ Draw parent should work, but not always /!\
		//parent.draw() ;
		parent.document.draw() ;
	}

	return this ;
} ;



// Resize the element to its content
Element.prototype.resizeToContent = function() {
	this.outputWidth = this.contentWidth ;
	this.outputHeight = this.contentHeight ;
} ;



// Sort zChildren, only necessary when a child zIndex changed
Element.prototype.zSort = function() {
	this.zChildren.sort( ( a , b ) => a.zIndex - b.zIndex ) ;
} ;



// Insert a child into the zChildren array, shift all greater zIndex to the left
// Use this instead of .push() and .zSort()
Element.prototype.zInsert = function( child ) {
	var current ,
		i = this.zChildren.length ;

	while ( i -- ) {
		current = this.zChildren[ i ] ;
		if ( child.zIndex >= current.zIndex ) {
			this.zChildren[ i + 1 ] = child ;
			return ;
		}

		this.zChildren[ i + 1 ] = current ;
	}

	this.zChildren[ 0 ] = child ;
} ;



// Change zIndex and call parent.zSort() immediately
Element.prototype.updateZ = Element.prototype.updateZIndex = function( z ) {
	this.savedZIndex = this.zIndex = z ;
	this.parent.zSort() ;
} ;



// Change zIndex to make it on top of all siblings
Element.prototype.topZ = function() {
	if ( this.parent.interceptTempZIndex ) { return this.parent.topZ() ; }

	if ( ! this.parent.zChildren.length ) { return ; }
	this.zIndex = this.parent.zChildren[ this.parent.zChildren.length - 1 ].zIndex + 1 ;
	this.parent.zSort() ;
} ;



// Change zIndex to make it on bottom of all siblings
Element.prototype.bottomZ = function() {
	if ( this.parent.interceptTempZIndex ) { return this.parent.bottomZ() ; }

	if ( ! this.parent.zChildren.length ) { return ; }
	this.zIndex = this.parent.zChildren[ 0 ].zIndex - 1 ;
	this.parent.zSort() ;
} ;



Element.prototype.restoreZ = function() {
	if ( this.parent.interceptTempZIndex ) { return this.parent.restoreZ() ; }

	this.zIndex = this.savedZIndex ;
	this.parent.zSort() ;
} ;



Element.computeContentWidth = ( content , hasMarkup ) => {
	if ( Array.isArray( content ) ) {
		return (
			hasMarkup === 'ansi' || hasMarkup === 'legacyAnsi' ? Math.max( ... content.map( line => misc.ansiWidth( line ) ) ) :
			hasMarkup ? Math.max( ... content.map( line => misc.markupWidth( line ) ) ) :
			Math.max( ... content.map( line => string.unicode.width( line ) ) )
		) ;
	}

	return (
		hasMarkup === 'ansi' || hasMarkup === 'legacyAnsi' ? misc.ansiWidth( content ) :
		hasMarkup ? misc.markupWidth( content ) :
		string.unicode.width( content )
	) ;
} ;

var lastTruncateWidth = 0 ;
Element.getLastTruncateWidth = () => lastTruncateWidth ;

Element.truncateContent = ( content , maxWidth , hasMarkup ) => {
	var str ;

	if ( hasMarkup === 'ansi' || hasMarkup === 'legacyAnsi' ) {
		str = misc.truncateAnsiString( content , maxWidth ) ;
		lastTruncateWidth = misc.getLastTruncateWidth() ;
	}
	else if ( hasMarkup ) {
		str = misc.truncateMarkupString( content , maxWidth ) ;
		lastTruncateWidth = misc.getLastTruncateWidth() ;
	}
	else {
		str = string.unicode.truncateWidth( content , maxWidth ) ;
		lastTruncateWidth = string.unicode.getLastTruncateWidth() ;
	}

	return str ;
} ;

Element.wordwrapContent = 	// <-- DEPRECATED
Element.wordWrapContent = ( content , width , hasMarkup ) =>
	hasMarkup === 'ansi' || hasMarkup === 'legacyAnsi' ? misc.wordWrapAnsi( content , width ) :
	hasMarkup ? misc.wordWrapMarkup( content , width ) :
	string.wordwrap( content , { width , fill: true , noJoin: true } ) ;



Element.prototype.setContent = function( content , hasMarkup , dontDraw = false , dontResize = false ) {
	if ( this.forceContentArray && ! Array.isArray( content ) ) { content = [ content || '' ] ; }

	var oldOutputWidth = this.outputWidth ,
		oldOutputHeight = this.outputHeight ;

	this.content = content ;
	this.contentHasMarkup = hasMarkup ;

	this.contentWidth = Element.computeContentWidth( content , this.contentHasMarkup ) ;
	this.contentHeight = Array.isArray( content ) ? content.length : 1 ;

	if ( ! dontResize && this.resizeOnContent ) { this.resizeOnContent() ; }

	if ( ! dontDraw ) {
		if ( this.outputWidth < oldOutputWidth || this.outputHeight < oldOutputHeight ) {
			this.outerDraw() ;
		}
		else {
			this.draw() ;
		}
	}
} ;



Element.prototype.isAncestorOf = function( element ) {
	var currentElement = element ;

	for ( ;; ) {
		if ( currentElement === this ) {
			// Self found: ancestor match!
			return true ;
		}
		else if ( ! currentElement.parent ) {
			// The element is either detached or attached to another parent element
			return false ;
		}
		else if ( currentElement.parent.children.indexOf( currentElement ) === -1 ) {
			// Detached but still retain a ref to its parent.
			// It's probably a bug, so we will remove that link now.
			currentElement.parent = null ;
			return false ;
		}

		currentElement = currentElement.parent ;
	}
} ;



Element.prototype.getParentContainer = function() {
	var currentElement = this ;

	for ( ;; ) {
		if ( ! currentElement.parent ) { return null ; }
		if ( currentElement.parent.isContainer ) { return currentElement.parent ; }

		currentElement = currentElement.parent ;
	}
} ;



// Internal: get the index of the direct child that have the focus or have a descendant having the focus
Element.prototype.getFocusBranchIndex = function() {
	var index , currentElement ;

	if ( ! this.document.focusElement ) { return null ; }

	currentElement = this.document.focusElement ;

	for ( ;; ) {
		if ( currentElement === this ) {
			// Self found: ancestor match!
			return null ;
		}
		else if ( ! currentElement.parent ) {
			// The element is either detached or attached to another parent element
			return null ;
		}

		if ( currentElement.parent === this ) {
			index = currentElement.parent.children.indexOf( currentElement ) ;

			if ( index === -1 ) {
				// Detached but still retain a ref to its parent.
				// It's probably a bug, so we will remove that link now.
				currentElement.parent = null ;
				return null ;
			}

			return index ;
		}

		currentElement = currentElement.parent ;
	}
} ;



Element.prototype.focusNextChild = function( loop = true , type = 'cycle' ) {
	var index , startingIndex , focusAware ;

	if ( ! this.children.length || ! this.document ) { return null ; }

	//if ( ! this.document.focusElement || ( index = this.children.indexOf( this.document.focusElement ) ) === -1 )
	if ( ! this.document.focusElement || ( index = this.getFocusBranchIndex() ) === null ) {
		index = this.children.length - 1 ;
	}

	startingIndex = index ;

	for ( ;; ) {
		index ++ ;
		if ( index >= this.children.length ) {
			if ( loop ) { index = 0 ; }
			else { index = this.children.length - 1 ; break ; }
		}

		focusAware = this.document.giveFocusTo_( this.children[ index ] , type ) ;

		// Exit if the focus was given to a focus-aware element or if we have done a full loop already
		if ( focusAware || startingIndex === index ) { break ; }
	}

	return this.children[ index ] ;
} ;



Element.prototype.focusPreviousChild = function( loop = true ) {
	var index , startingIndex , focusAware ;

	if ( ! this.children.length || ! this.document ) { return null ; }

	//if ( ! this.document.focusElement || ( index = this.children.indexOf( this.document.focusElement ) ) === -1 )
	if ( ! this.document.focusElement || ( index = this.getFocusBranchIndex() ) === null ) {
		index = 0 ;
	}

	startingIndex = index ;

	for ( ;; ) {
		index -- ;
		if ( index < 0 ) {
			if ( loop ) { index = this.children.length - 1 ; }
			else { index = 0 ; break ; }
		}

		focusAware = this.document.giveFocusTo_( this.children[ index ] , 'backCycle' ) ;

		// Exit if the focus was given to a focus-aware element or if we have done a full loop already
		if ( focusAware || startingIndex === index ) { break ; }
	}

	return this.children[ index ] ;
} ;



// Get all child element matching a x,y coordinate relative to the current element
Element.prototype.childrenAt = function( x , y , filter = null , matches = [] ) {
	var i , current ;

	// Search children, order by descending zIndex, because we want the top element first
	i = this.zChildren.length ;
	while ( i -- ) {
		current = this.zChildren[ i ] ;

		// Filter out hidden element now
		if ( current.hidden ) { continue ; }

		if (
			x >= current.outputX && x <= current.outputX + current.outputWidth - 1 &&
			y >= current.outputY && y <= current.outputY + current.outputHeight - 1
		) {
			// Bounding box match!

			// Check and add children of children first (depth-first)
			if ( current.isContainer ) {
				current.childrenAt( x - current.inputX , y - current.inputY , filter , matches ) ;
			}
			else {
				current.childrenAt( x , y , filter , matches ) ;
			}

			if ( ! filter || filter( current ) ) {
				matches.push( { element: current , x: x - current.outputX , y: y - current.outputY } ) ;
			}
		}
		else if ( ! current.isContainer ) {
			// If it is not a container, give a chance to its children to get selected
			current.childrenAt( x , y , filter , matches ) ;
		}
	}

	return matches ;
} ;



Element.prototype.saveCursor = function() {
	if ( this.inputDst ) {
		this.savedCursorX = this.inputDst.cx ;
		this.savedCursorY = this.inputDst.cy ;
	}
	else if ( this.outputDst ) {
		this.savedCursorX = this.outputDst.cx ;
		this.savedCursorY = this.outputDst.cy ;
	}

	return this ;
} ;



Element.prototype.restoreCursor = function() {
	if ( this.inputDst ) {
		this.inputDst.cx = this.savedCursorX ;
		this.inputDst.cy = this.savedCursorY ;
		this.inputDst.drawCursor() ;
	}
	else if ( this.outputDst ) {
		this.outputDst.cx = this.savedCursorX ;
		this.outputDst.cy = this.savedCursorY ;
		this.outputDst.drawCursor() ;
	}

	return this ;
} ;



Element.prototype.draw = function( isInitialInlineDraw = false ) {
	//console.error( "\n----------------------------\nCalling .draw() for" , this.debugId() , new Error( 'trace:' ) ) ;
	if ( ! this.document || this.hidden ) { return this ; }

	if ( ! isInitialInlineDraw ) {
		if ( this.restoreCursorAfterDraw ) { this.inlineTerm.saveCursor() ; }
		else if ( ! this.strictInline ) { this.saveCursor() ; }
	}

	this.descendantDraw() ;
	this.ascendantDraw() ;

	if ( ! isInitialInlineDraw ) {
		if ( this.restoreCursorAfterDraw ) { this.inlineTerm.restoreCursor() ; }
		else if ( ! this.strictInline ) { this.drawCursor() ; }
	}

	return this ;
} ;



// .draw() is used when drawing the current Element is enough: the Element has not moved, and has not been resized.
// If it has, then it is necessary to draw the closest ancestor which is a container.
// /!\ IS THIS METHOD WRONG? it should draw the parent container, but don't redraw any children of its children Container
// Option 'force' redraw even if the element is hidden, in fact it is used by the .hide() method to effectively hide the element on the parent container.
Element.prototype.redraw = 	// DEPRECATED name, use .outerDraw()
Element.prototype.outerDraw = function( force = false ) {
	if ( ! this.document || ( this.hidden && ! force ) ) { return this ; }

	var container = this.getParentContainer() ;

	if ( ! container ) { this.draw() ; }
	else { container.draw() ; }

	return this ;
} ;



// Hard to find a good name, .draw() or .outerDraw() depending on what have been updated
Element.prototype.updateDraw = function() {
	if ( this.needOuterDraw ) { this.outerDraw() ; }
	else { this.draw() ; }
	this.needOuterDraw = false ;
} ;



// Draw all the children
Element.prototype.descendantDraw = function( isSubcall ) {
	var i , iMax ;

	if ( this.hidden ) { return this ; }

	if ( this.preDrawSelf ) {
		this.preDrawSelf( ! isSubcall ) ;
	}

	// Draw children, order by ascending zIndex
	for ( i = 0 , iMax = this.zChildren.length ; i < iMax ; i ++ ) {
		this.zChildren[ i ].descendantDraw( true ) ;
	}

	if ( isSubcall && this.postDrawSelf ) {
		this.postDrawSelf( ! isSubcall ) ;
	}

	return this ;
} ;



// Post-draw from the current element through all the ancestor chain
Element.prototype.ascendantDraw = function() {
	var currentElement ;

	if ( this.postDrawSelf && ! this.hidden ) {
		this.postDrawSelf( true ) ;
	}

	currentElement = this ;

	while ( currentElement.parent && currentElement.outputDst !== currentElement.document.outputDst ) {
		currentElement = currentElement.parent ;

		if ( currentElement.outputDst !== currentElement.inputDst && currentElement.postDrawSelf && ! currentElement.hidden ) {
			currentElement.postDrawSelf( false ) ;
		}
	}

	return this ;
} ;



// Draw cursor from the current element through all the ancestor chain
Element.prototype.drawCursor = function() {
	var currentElement ;

	if ( this.drawSelfCursor && ! this.hidden ) {
		this.drawSelfCursor( true ) ;
	}

	currentElement = this ;

	while ( currentElement.outputDst !== currentElement.document.outputDst && currentElement.parent ) {
		currentElement = currentElement.parent ;

		if ( currentElement.drawSelfCursor && ! currentElement.hidden ) {
			currentElement.drawSelfCursor( false ) ;
		}
	}

	return this ;
} ;



// TODOC
Element.prototype.bindKey = function( key , action ) { this.keyBindings[ key ] = action ; } ;
// TODOC
Element.prototype.getKeyBinding = function( key ) { return this.keyBindings[ key ] ?? null ; } ;
// TODOC
Element.prototype.getKeyBindings = function( key ) { return Object.assign( {} , this.keyBindings ) ; } ;
// TODOC
Element.prototype.getActionBinding = function( action , ui = false ) {
	var keys = [] ;

	for ( let key in this.keyBindings ) {
		if ( this.keyBindings[ key ] === action ) {
			keys.push( ui ? misc.keyToUserInterfaceName( key ) : key ) ;
		}
	}

	return keys ;
} ;



// For inline widget, having eventually a document just for him, that fit its own size
Element.createInline = async function( term , Type , options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	options.inlineTerm = term ;
	//options.outputDst = term ;
	//options.eventSource = term ;

	var cursorPosition ,
		position = {
			x: options.outputX || options.x ,
			y: options.outputY || options.y
		} ;

	// Don't use 'delete', because options = Object.create( options ) -- doesn't work with inheritance
	options.x = options.y = options.outputX = options.outputY = 0 ;

	var element = new Type( options ) ;

	if ( position.x === undefined || position.y === undefined ) {
		if ( element.strictInline ) {
			// We do not want any asyncness for pure inline elements, and since we draw in inline mode, we don't care about it...
			// ... BUT we need a position anyway for the clipping purpose! It can't be 0 since we draw on the terminal and top-left is (1,1).
			position.x = position.y = 1 ;
		}
		else {
			cursorPosition = await term.getCursorLocation() ;

			if ( position.x === undefined ) {
				position.x = cursorPosition.x ;

				if ( cursorPosition.x > 1 && element.inlineNewLine ) {
					position.x = 1 ;
					if ( position.y === undefined ) { position.y = cursorPosition.y + 1 ; }
				}
			}

			if ( position.y === undefined ) { position.y = cursorPosition.y ; }
		}
	}

	if ( ! element.strictInline ) {
		let scrollY = position.y + element.outputHeight - term.height ;

		if ( scrollY > 0 ) {
			term.scrollUp( scrollY ) ;
			term.up( scrollY ) ;	// move the cursor up, so save/restore cursor could work
			position.y -= scrollY ;
		}
	}

	if ( element.inlineResizeToContent ) {
		element.resizeToContent() ;
	}

	var documentOptions = {
		internal: true ,
		inlineTerm: term ,
		strictInline: element.strictInline ,
		noInput: element.strictInline || ! element.needInput ,
		outputX: position.x ,
		outputY: position.y ,
		outputWidth: element.outputWidth ,
		outputHeight: element.outputHeight ,
		outputDst: term ,
		eventSource: term ,
		noDraw: true
	} ;

	var document = new termkit.Document( documentOptions ) ;

	document.attach( element ) ;

	// Should probably resize the container
	element.on( 'resize' , () => { throw new Error( 'not coded!' ) ; } ) ;

	element.draw( true ) ;
	term.styleReset() ;

	if ( element.staticInline ) { element.destroy( undefined , true ) ; }

	return element ;
} ;



// Default 'key' event management, suitable for almost all use-case, but could be derivated if needed
Element.prototype.onKey = function( key , trash , data ) {
	var action = this.keyBindings[ key ] ;
	//console.error( this.debugId() , "Key:" , key , "Actions:" , action , !! this.userActions?.[ action ] ) ; // action && this.userActions[ action ] ? "fn: " + this.userActions[ action ].toString() : '' ) ;

	if ( action ) {
		if ( action === 'meta' ) {
			if ( this.document ) {
				this.document.setMetaKeyPrefix( 'META' , 'CTRL' ) ;
			}
			return true ;	// Do not bubble up
		}
		else if ( this.userActions[ action ] ) {
			// Do not bubble up except if explicitly false
			return ( this.userActions[ action ].call( this , key , trash , data ) ?? true ) || undefined ;
		}
	}
	else if ( data && data.isCharacter ) {
		if ( this.userActions.character ) {
			// Do not bubble up except if explicitly false
			return ( this.userActions.character.call( this , key , trash , data ) ?? true ) || undefined ;
		}
	}
	else if ( this.userActions.specialKey ) {
		// Do not bubble up except if explicitly false
		return ( this.userActions.specialKey.call( this , key , trash , data ) ?? true ) || undefined ;
	}

	// Nothing found, bubble up
	return ;
} ;



// Should be redefined
Element.prototype.isContainer = false ;	// boolean, true if it's a container, having a different inputDst and outputDst and local coords
Element.prototype.forceContentArray = false ;	// boolean, true if content should be an array of string instead of a string
Element.prototype.noChildFocus = false ;	// boolean, true if the focus should not be transmitted to children of this Element
Element.prototype.computeBoundingBoxes = null ;	// function, bounding boxes for elements that can be drawn
Element.prototype.resizeOnContent = null ;	// function, if set, resize on content update, called by .setContent()
Element.prototype.preDrawSelf = null ;	// function, things to draw for the element before drawing its children
Element.prototype.postDrawSelf = null ;	// function, things to draw for the element after drawing its children
Element.prototype.drawSelfCursor = null ;	// function, draw the element cursor
Element.prototype.getValue = () => null ;	// function, get the value of the element if any...
Element.prototype.setValue = () => undefined ;	// function, set the value of the element if any...
Element.prototype.strictInlineSupport = false ;	// no support for strictInline mode by default
Element.prototype.staticInline = false ;	// boolean, true if the inline version is static and could be destroyed immediately after been drawn
Element.prototype.inlineCursorRestoreAfterDraw = false ;	// when set, save/restore cursor in inline mode (forced when strictInline is true)
Element.prototype.needInput = false ;	// no need for input by default (used to configure inline mode)
Element.prototype.outerDrag = false ;	// boolean, true if drag event are sent when out of bounds (e.g. useful for moving windows)

Element.prototype.keyBindings = {} ;	// object, store key bindings, the key is a Terminal Kit key code, the value is an user-action name
Element.prototype.userActions = {} ;	// object, the key is an user-action name, the value is a function... THIS IS INHERITED


},{"../misc.js":48,"../termkit.js":56,"nextgen-events":78,"string-kit":133}],26:[function(require,module,exports){
(function (process){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const LabeledInput = require( './LabeledInput.js' ) ;
const Button = require( './Button.js' ) ;



function Form( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	if ( ! options.outputWidth && ! options.width ) { options.outputWidth = 78 ; }

	Element.call( this , options ) ;

	this.submitValue = null ;

	this.inputsDef = options.inputs || [] ;
	this.labeledInputs = [] ;
	this.buttonsDef = options.buttons || [] ;
	this.buttons = [] ;
	this.focusChild = null ;
	this.onButtonSubmit = this.onButtonSubmit.bind( this ) ;
	this.onFocus = this.onFocus.bind( this ) ;

	// Global default attributes
	this.textAttr = options.textAttr || null ;
	this.voidAttr = options.voidAttr || options.emptyAttr || null ;
	this.labelFocusAttr = options.labelFocusAttr || null ;
	this.labelBlurAttr = options.labelBlurAttr || null ;
	this.buttonFocusAttr = options.buttonFocusAttr || null ;
	this.buttonBlurAttr = options.buttonBlurAttr || null ;
	this.turnedOnBlurAttr = options.turnedOnBlurAttr || null ;
	this.turnedOnFocusAttr = options.turnedOnFocusAttr || null ;
	this.turnedOffBlurAttr = options.turnedOffBlurAttr || null ;
	this.turnedOffFocusAttr = options.turnedOffFocusAttr || null ;

	if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; }
	if ( options.textInputKeyBindings ) { this.textInputKeyBindings = options.textInputKeyBindings ; }

	this.initChildren() ;

	this.on( 'key' , this.onKey ) ;
	this.on( 'focus' , this.onFocus ) ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'Form' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Form ;
Element.inherit( Form ) ;



Form.prototype.needInput = true ;



Form.prototype.keyBindings = {
	LEFT: 'previous' ,
	RIGHT: 'next' ,
	UP: 'previous' ,
	DOWN: 'next' ,
	ENTER: 'next' ,
	KP_ENTER: 'next' ,
	ALT_ENTER: 'next'
} ;



Form.prototype.textInputKeyBindings = {} ;
Form.prototype.selectInputKeyBindings = {} ;
Form.prototype.selectMultiInputKeyBindings = {} ;



// Create LabeledInput and Button automatically
Form.prototype.initChildren = function() {
	var labelMaxWidth = 0 ,
		offsetX = 0 , offsetY = 0 ,
		buttonsTextWidth = 0 , buttonSpacing = 0 ;

	this.inputsDef.forEach( def => {
		def.labelWidth = Element.computeContentWidth( def.label , def.labelHasMarkup ) ;
		if ( def.labelWidth > labelMaxWidth ) { labelMaxWidth = def.labelWidth ; }
	} ) ;

	this.inputsDef.forEach( ( def , index ) => {
		var height = 1 ,
			label = def.label + ' '.repeat( labelMaxWidth - def.labelWidth ) ;

		switch ( def.type ) {
			case 'select' :
				//def.type = 'select' ;
				//if ( def.height ) { height = 1 ; }

				this.labeledInputs[ index ] = new LabeledInput( {
					internal: true ,
					parent: this ,
					type: def.type ,
					key: def.key ,
					label: label ,
					content: def.content ,
					value: def.value ,
					items: def.items ,
					outputX: this.outputX ,
					outputY: this.outputY + offsetY ,
					outputWidth: def.outputWidth || def.width || this.outputWidth ,
					outputHeight: height ,
					labelFocusAttr: def.labelFocusAttr || this.labelFocusAttr ,
					labelBlurAttr: def.labelBlurAttr || this.labelBlurAttr ,
					buttonBlurAttr: def.buttonBlurAttr || this.buttonBlurAttr ,
					buttonFocusAttr: def.buttonFocusAttr || this.buttonFocusAttr ,
					buttonDisabledAttr: def.buttonDisabledAttr || this.buttonDisabledAttr ,
					buttonSubmittedAttr: def.buttonSubmittedAttr || this.buttonSubmittedAttr ,
					keyBindings: this.selectInputKeyBindings ,
					noDraw: true
				} ) ;

				break ;

			case 'select-multi' :
			case 'selectMulti' :
				//def.type = 'select' ;
				//if ( def.height ) { height = 1 ; }

				this.labeledInputs[ index ] = new LabeledInput( {
					internal: true ,
					parent: this ,
					type: def.type ,
					key: def.key ,
					label: label ,
					content: def.content ,
					value: def.value ,
					items: def.items ,
					outputX: this.outputX ,
					outputY: this.outputY + offsetY ,
					outputWidth: def.outputWidth || def.width || this.outputWidth ,
					outputHeight: height ,
					labelFocusAttr: def.labelFocusAttr || this.labelFocusAttr ,
					labelBlurAttr: def.labelBlurAttr || this.labelBlurAttr ,
					buttonBlurAttr: def.buttonBlurAttr || this.buttonBlurAttr ,
					buttonFocusAttr: def.buttonFocusAttr || this.buttonFocusAttr ,
					buttonDisabledAttr: def.buttonDisabledAttr || this.buttonDisabledAttr ,
					buttonSubmittedAttr: def.buttonSubmittedAttr || this.buttonSubmittedAttr ,
					turnedOnBlurAttr: def.turnedOnBlurAttr || this.turnedOnBlurAttr ,
					turnedOnFocusAttr: def.turnedOnFocusAttr || this.turnedOnFocusAttr ,
					turnedOffBlurAttr: def.turnedOffBlurAttr || this.turnedOffBlurAttr ,
					turnedOffFocusAttr: def.turnedOffFocusAttr || this.turnedOffFocusAttr ,
					keyBindings: this.selectInputKeyBindings ,
					noDraw: true
				} ) ;

				break ;

			case 'text' :
			default :
				def.type = 'text' ;
				if ( def.height ) { height = def.height ; }

				this.labeledInputs[ index ] = new LabeledInput( {
					internal: true ,
					parent: this ,
					type: def.type ,
					key: def.key ,
					label: label ,
					content: def.content ,
					outputX: this.outputX ,
					outputY: this.outputY + offsetY ,
					outputWidth: def.outputWidth || def.width || this.outputWidth ,
					outputHeight: height ,
					lineWrap: !! def.lineWrap ,
					wordWrap: !! def.wordWrap ,
					scrollable: !! def.scrollable ,
					vScrollBar: !! def.vScrollBar ,
					hScrollBar: !! def.hScrollBar ,
					hiddenContent: def.hiddenContent ,
					labelFocusAttr: def.labelFocusAttr || this.labelFocusAttr ,
					labelBlurAttr: def.labelBlurAttr || this.labelBlurAttr ,
					textAttr: def.textAttr || this.textAttr ,
					voidAttr: def.voidAttr || def.emptyAttr || this.voidAttr ,
					keyBindings: this.textInputKeyBindings ,
					allowNewLine: height > 1 ,
					noDraw: true
				} ) ;

				break ;
		}

		offsetY += height ;
	} ) ;


	// Submit Button part
	if ( ! this.buttonsDef.length ) {
		this.buttonsDef.push( {
			content: 'Submit' ,
			value: 'submit'
		} ) ;
	}

	this.buttonsDef.forEach( def => {
		def.contentWidth = Element.computeContentWidth( def.content , def.contentHasMarkup ) ;
		buttonsTextWidth += def.contentWidth ;
	} ) ;

	buttonSpacing = Math.floor( ( this.outputWidth - buttonsTextWidth ) / ( this.buttonsDef.length + 1 ) ) ;

	offsetX = buttonSpacing ;
	offsetY ++ ;

	this.buttonsDef.forEach( ( def , index ) => {
		this.buttons[ index ] = new Button( {
			internal: true ,
			parent: this ,
			content: def.content ,
			value: def.value ,
			outputX: this.outputX + offsetX ,
			outputY: this.outputY + offsetY ,
			focusAttr: def.focusAttr || this.buttonFocusAttr ,
			blurAttr: def.blurAttr || this.buttonBlurAttr ,
			noDraw: true
		} ) ;

		this.buttons[ index ].on( 'submit' , this.onButtonSubmit ) ;

		offsetX += def.contentWidth + buttonSpacing ;
	} ) ;
} ;



Form.prototype.getValue = function() {
	var fields = {} ;

	this.labeledInputs.forEach( labeledInput => {
		fields[ labeledInput.key ] = labeledInput.getValue() ;
	} ) ;

	return { submit: this.submitValue , fields } ;
} ;



Form.prototype.onFocus = function( focus , type ) {
	if ( type === 'cycle' || type === 'backCycle' ) { return ; }

	if ( focus ) {
		// Defer to the next tick to avoid recursive events producing wrong listener order
		process.nextTick( () => {
			if ( this.focusChild ) { this.document.giveFocusTo( this.focusChild , 'delegate' ) ; }
			else { this.focusChild = this.focusNextChild() ; }
		} ) ;
	}
} ;



Form.prototype.onButtonSubmit = function( buttonValue , action , button ) {
	this.submitValue = buttonValue ;
	this.emit( 'submit' , this.getValue() , action , this , button ) ;
} ;



const userActions = Form.prototype.userActions ;

userActions.previous = function() {
	this.focusChild = this.focusPreviousChild() ;
} ;

userActions.next = function() {
	this.focusChild = this.focusNextChild() ;
} ;


}).call(this)}).call(this,require('_process'))
},{"./Button.js":17,"./Element.js":25,"./LabeledInput.js":31,"_process":191}],27:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const InlineInput = require( './InlineInput.js' ) ;
const fileHelpers = require( '../fileHelpers.js' ) ;

const fs = require( 'fs' ) ;
const path = require( 'path' ) ;



/*
	An InlineInput that auto-complete filepath.
*/

function InlineFileInput( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	this.baseDir = options.baseDir ?? null ;
	this.resolvedBaseDir = null ;
	this.autoCompleteFileOptions = null ;

	this.accept =
		options.accept && typeof options.accept === 'object' ? options.accept :
		{ unexistant: true , file: true , directory: true } ;

	InlineInput.call( this , options ) ;

	this.autoComplete = this.fileAutoComplete.bind( this ) ;
	this.useAutoCompleteHint = options.useAutoCompleteHint ?? true ;
	this.useAutoCompleteMenu = options.useAutoCompleteMenu ?? true ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'InlineFileInput' && ! options.noDraw ) { this.draw() ; }

	this.initPromise = this.init() ;
}

module.exports = InlineFileInput ;
Element.inherit( InlineFileInput , InlineInput ) ;



InlineFileInput.prototype.init = async function() {
	if ( this.initPromise ) { return this.initPromise ; }

	this.resolvedBaseDir = await fileHelpers.resolveBaseDir( this.baseDir ) ;

	// Force directory, because we need them to navigate to files
	var accept = Object.assign( {} , this.accept ) ;
	accept.directory = true ;

	this.autoCompleteFileOptions = {
		accept ,
		baseDir: this.resolvedBaseDir
	} ;
} ;



InlineFileInput.prototype.fileAutoComplete = async function( inputString ) {
	await this.initPromise ;
	return fileHelpers.autoCompleteFile( inputString , this.autoCompleteFileOptions ) ;
} ;



InlineFileInput.prototype.submit = async function() {
	var filePath , stats ;

	if ( this.disabled || this.submitted || this.canceled ) { return ; }
	//this.submitted = true ;

	filePath = this.getValue() ;

	if ( ! filePath || typeof filePath !== 'string' ) {
		if ( ! this.noEmpty ) { this.emit( 'submit' , null , undefined , this ) ; }
		return ;
	}

	await this.initPromise ;

	filePath = path.resolve( path.isAbsolute( filePath ) ? filePath : this.resolvedBaseDir + filePath ) ;

	try {
		stats = await fs.promises.stat( filePath ) ;
	}
	catch ( error ) {
		if ( error.code === 'ENOENT' && this.accept.unexistant ) {
			this.emit( 'submit' , filePath , undefined , this ) ;
			return ;
		}

		if ( ! this.noEmpty ) { this.emit( 'submit' , null , undefined , this ) ; }
		return ;
	}

	if ( ! fileHelpers.statsFilter( stats , this.accept ) ) {
		if ( ! this.noEmpty ) { this.emit( 'submit' , null , undefined , this ) ; }
		return ;
	}

	this.emit( 'submit' , filePath , undefined , this ) ;
} ;


},{"../fileHelpers.js":43,"./Element.js":25,"./InlineInput.js":28,"fs":148,"path":190}],28:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const TextBox = require( './TextBox.js' ) ;
const EditableTextBox = require( './EditableTextBox.js' ) ;
const RowMenu = require( './RowMenu.js' ) ;

const Promise = require( 'seventh' ) ;
const string = require( 'string-kit' ) ;
const computeAutoCompleteArray = require( '../autoComplete.js' ) ;



/*
	This is the Document-model version of .inputField().
	Like an EditableTextBox, with a one-line hard-line-wrap TextBuffer, outputHeight start at 1 but can grow
	as more input is entered by the user, can auto-complete with or without menu, have history, and so on...
*/

/*
	Check-list of things that .inputField() has and InlineInput still don't:
		* Inline mode, capable of adding a new line at the end of the screen when it is needed
		* editing actions: deleteAllBefore, deleteAllAfter, deletePreviousWord, deleteNextWord
		* allow placeholder to be used as default (submitting without actually entering anything) when appropriate
		* disable echoing (no output and no cursor movements)
		* setting the cursor "offset" position beforehand
		* min/max length
		* Maybe (very low priority): support for the .inputField()'s token feature (tokenHook, tokenResetHook, tokenRegExp).
*/

function InlineInput( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	if ( options.value ) { options.content = options.value ; }

	// It is always 1 at the begining
	options.outputHeight = 1 ;

	// No scrolling
	options.scrollable = options.hasVScrollBar = options.hasHScrollBar = options.extraScrolling = false ;
	options.scrollX = options.scrollY = 0 ;

	// It always have line-wrapping on
	options.lineWrap = true ;

	this.onAutoCompleteMenuSubmit = this.onAutoCompleteMenuSubmit.bind( this ) ;
	this.onAutoCompleteMenuItemFocus = this.onAutoCompleteMenuItemFocus.bind( this ) ;
	this.onAutoCompleteMenuCancel = this.onAutoCompleteMenuCancel.bind( this ) ;

	this.promptTextBox = null ;

	if ( options.prompt ) {
		this.promptTextBox = new TextBox( Object.assign(
			{
				textAttr: options.textAttr
			} ,
			options.prompt ,
			{
				internal: true ,
				//parent: this ,
				outputX: options.outputX || options.x ,
				outputY: options.outputY || options.y ,
				outputWidth: options.outputWidth || options.width ,
				outputHeight: options.outputHeight || options.height ,
				lineWrap: options.lineWrap ,
				wordWrap: options.wordWrap || options.wordwrap
			}
		) ) ;

		// Drop void cells
		this.promptTextBox.textBuffer.setVoidAttr( null ) ;

		let size = this.promptTextBox.getContentSize() ;
		this.promptTextBox.setSizeAndPosition( size ) ;

		if ( size.height > 1 ) {
			options.outputY = ( options.outputY || options.y ) + size.height - 1 ;
			options.firstLineRightShift = this.promptTextBox.textBuffer.buffer[ this.promptTextBox.textBuffer.buffer.length - 1 ].length ;
		}
		else {
			options.firstLineRightShift = size.width ;
		}
	}

	EditableTextBox.call( this , options ) ;


	this.history = options.history ;
	this.contentArray = options.history ? [ ... options.history , this.content ] : [ this.content ] ;
	this.contentIndex = this.contentArray.length - 1 ;

	this.noEmpty = !! options.noEmpty ;	// if set, do not submit empty string

	this.disabled = !! options.disabled ;
	this.submitted = !! options.submitted ;
	this.cancelable = !! options.cancelable ;
	this.canceled = !! options.canceled ;

	this.autoComplete = options.autoComplete ;
	this.useAutoCompleteHint = !! ( this.autoComplete && ( options.useAutoCompleteHint || options.autoCompleteHint ) ) ;
	this.autoCompleteHintMinInput = options.autoCompleteHintMinInput || 1 ;	// number of input chars before starting to hint
	this.useAutoCompleteMenu = !! ( this.autoComplete && ( options.useAutoCompleteMenu || options.autoCompleteMenu ) ) ;
	this.autoCompleteMenu = null ;
	this.autoCompleteLeftPart = null ;
	this.autoCompleteRightPart = null ;
	this.autoCompleteCursorCell = null ;
	this.autoCompleteMenuPrefix = null ;
	this.autoCompleteMenuPostfix = null ;

	this.menuOptions = Object.assign( {} , this.defaultMenuOptions , options.menu ) ;

	this.placeholder = options.placeholder ;
	this.placeholderHasMarkup = options.placeholderHasMarkup ;

	if ( this.placeholder ) {
		this.setAltContent( this.placeholder , this.placeholderHasMarkup ) ;
	}

	if ( this.promptTextBox ) {
		this.attach( this.promptTextBox ) ;
	}

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'InlineInput' && ! options.noDraw ) { this.draw() ; }
}

module.exports = InlineInput ;
Element.inherit( InlineInput , EditableTextBox ) ;



// Has a fallback textBuffer for hint/placeholder
InlineInput.prototype.useAltTextBuffer = true ;



InlineInput.prototype.defaultMenuOptions = {
	buttonBlurAttr: { bgColor: 'default' , color: 'default' } ,
	buttonFocusAttr: { bgColor: 'green' , color: 'blue' , dim: true } ,
	buttonDisabledAttr: { bgColor: 'white' , color: 'brightBlack' } ,
	buttonSubmittedAttr: { bgColor: 'brightWhite' , color: 'brightBlack' } ,
	buttonSeparatorAttr: { bgColor: 'default' } ,
	backgroundAttr: { bgColor: 'default' } ,
	//leftPadding: ' ' , rightPadding: ' ' ,
	justify: true ,
	keyBindings: Object.assign( {} , RowMenu.prototype.keyBindings , {
		TAB: 'next' ,
		SHIFT_TAB: 'previous'
	} )
} ;



InlineInput.prototype.keyBindings = {
	CTRL_K: 'meta' ,
	ENTER: 'submit' ,
	KP_ENTER: 'submit' ,
	ESCAPE: 'cancel' ,
	TAB: 'autoComplete' ,
	CTRL_R: 'historyAutoComplete' ,
	UP: 'historyPrevious' ,
	DOWN: 'historyNext' ,
	BACKSPACE: 'backDelete' ,
	DELETE: 'delete' ,
	LEFT: 'backward' ,
	RIGHT: 'forward' ,
	CTRL_LEFT: 'startOfWord' ,
	CTRL_RIGHT: 'endOfWord' ,
	HOME: 'startOfLine' ,
	END: 'endOfLine' ,
	CTRL_B: 'startOfSelection' ,
	CTRL_E: 'endOfSelection' ,

	// T for Transfer
	//CTRL_T: 'moveSelection' ,		// TODO
	ALT_T: 'copyToDocumentClipboard' ,
	META_T: 'copyToSystemClipboard' ,
	// P for Paste / Put
	CTRL_P: 'pasteSelection' ,
	ALT_P: 'pasteDocumentClipboard' ,
	META_P: 'pasteSystemClipboard' ,
	// D for Delete
	//CTRL_D: 'deleteSelection' ,	// TODO
	ALT_D: 'clearDocumentClipboard' ,
	META_D: 'clearSystemClipboard'
} ;



InlineInput.prototype.insert = function( str ) {
	this.textBuffer.insert( str , this.textAttr ) ;
	this.textBuffer.runStateMachine() ;
	if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; } // async
	else { this.autoResizeAndDraw() ; }
} ;



InlineInput.prototype.preDrawSelf = function() {
	/*
	if ( this.promptTextBuffer ) {
		// It's best to force the dst now, because it avoids to set textBuffer.dst everytime it changes,
		// and it could be changed by userland (so hard to keep it in sync without setters/getters)
		this.promptTextBuffer.draw( { dst: this.outputDst } ) ;
	}
	//*/

	EditableTextBox.prototype.preDrawSelf.call( this ) ;
} ;



InlineInput.prototype.autoResizeAndDraw = function( onlyDrawCursor = false ) {
	var height = Math.max( this.textBuffer.buffer.length , ( this.altTextBuffer && this.altTextBuffer.buffer.length ) || 0 ) ;

	if ( height > this.outputHeight ) {
		this.setSizeAndPosition( { outputHeight: height } ) ;
	}

	if ( ! onlyDrawCursor ) {
		this.draw() ;
	}
	else {
		this.drawCursor() ;
	}
} ;



InlineInput.prototype.autoResizeAndDrawCursor = function() {
	return this.autoResizeAndDraw( true ) ;
} ;



InlineInput.prototype.runAutoCompleteHint = async function( autoComplete ) {
	if ( this.textBuffer.cy === 0 && this.textBuffer.cx < this.autoCompleteHintMinInput ) {
		// Not enough input for starting to hint
		this.altTextBuffer.setText( '' ) ;
		this.autoResizeAndDraw() ;
		return ;
	}

	var autoCompleted ;

	var [ leftPart , rightPart ] = this.textBuffer.getCursorSplittedText() ;

	if ( rightPart ) {
		this.altTextBuffer.setText( '' ) ;
		this.autoResizeAndDraw() ;
		return ;
	}

	if ( Array.isArray( autoComplete ) ) {
		autoCompleted = computeAutoCompleteArray( autoComplete , leftPart , false ) ;
	}
	else if ( typeof autoComplete === 'function' ) {
		autoCompleted = await autoComplete( leftPart , false ) ;
	}
	else {
		this.altTextBuffer.setText( '' ) ;
		this.autoResizeAndDraw() ;
		return ;
	}

	if ( Array.isArray( autoCompleted ) ) {
		if ( ! autoCompleted.length || autoCompleted.length > 1 ) {
			this.altTextBuffer.setText( '' ) ;
			this.autoResizeAndDraw() ;
			return ;
		}

		autoCompleted = ( autoCompleted.prefix ?? '' ) + autoCompleted[ 0 ] + ( autoCompleted.postfix ?? '' ) ;
	}

	if ( autoCompleted === leftPart ) {
		this.altTextBuffer.setText( '' ) ;
	}
	else {
		this.altTextBuffer.setText( autoCompleted ) ;
		//this.altTextBuffer.runStateMachine() ;
	}

	this.autoResizeAndDraw() ;
} ;



InlineInput.prototype.runAutoComplete = async function( autoComplete ) {
	var autoCompleted ;

	this.autoCompleteCursorCell = this.textBuffer.getCursorCell() ;
	[ this.autoCompleteLeftPart , this.autoCompleteRightPart ] = this.textBuffer.getCursorSplittedText() ;

	if ( Array.isArray( autoComplete ) ) {
		autoCompleted = computeAutoCompleteArray( autoComplete , this.autoCompleteLeftPart , this.useAutoCompleteMenu ) ;
	}
	else if ( typeof autoComplete === 'function' ) {
		autoCompleted = await autoComplete( this.autoCompleteLeftPart , this.useAutoCompleteMenu ) ;
	}
	else {
		return ;
	}

	if ( Array.isArray( autoCompleted ) ) {
		if ( ! autoCompleted.length ) { return ; }

		if ( this.useAutoCompleteMenu ) {
			this.runAutoCompleteMenu( autoCompleted ) ;
			return ;
		}

		autoCompleted = ( autoCompleted.prefix ?? '' ) + autoCompleted[ 0 ] + ( autoCompleted.postfix ?? '' ) ;
	}

	this.runAutoCompleted( autoCompleted ) ;
} ;



InlineInput.prototype.runAutoCompleted = async function( autoCompleted ) {
	if ( autoCompleted.startsWith( this.autoCompleteLeftPart ) ) {
		this.textBuffer.insert( autoCompleted.slice( this.autoCompleteLeftPart.length ) ) ;
		if ( ! this.textBuffer.updateCursorFromCell( this.autoCompleteCursorCell ) ) {
			this.textBuffer.moveToEndOfBuffer() ;
		}
	}
	else {
		this.textBuffer.setText( autoCompleted + this.autoCompleteRightPart ) ;
		this.textBuffer.moveToEndOfBuffer() ;
	}

	this.textBuffer.runStateMachine() ;
	this.autoResizeAndDraw() ;
} ;



InlineInput.prototype.runAutoCompleteMenu = async function( items ) {
	// No items, leave now...
	if ( ! items || ! items.length ) { return ; }

	if ( this.autoCompleteMenu ) {
		// Should never happen, but just in case...
		this.autoCompleteMenu.destroy() ;
	}

	// Make the ColumnMenu a child of the button, so focus cycle will work as expected
	this.autoCompleteMenu = new RowMenu( Object.assign( {} , this.menuOptions , {
		internal: true ,
		parent: this ,
		x: this.outputX ,
		y: this.outputY + this.outputHeight ,
		outputWidth: this.outputWidth ,
		items: items.map( item => ( { value: item , content: item } ) )
	} ) ) ;

	this.autoCompleteMenuPrefix = items.prefix ?? '' ;
	this.autoCompleteMenuPostfix = items.postfix ?? '' ;

	this.document.giveFocusTo( this.autoCompleteMenu ) ;

	this.autoCompleteMenu.once( 'submit' , this.onAutoCompleteMenuSubmit ) ;
	this.autoCompleteMenu.once( 'cancel' , this.onAutoCompleteMenuCancel ) ;
	this.autoCompleteMenu.on( 'itemFocus' , this.onAutoCompleteMenuItemFocus ) ;
} ;



InlineInput.prototype.onAutoCompleteMenuSubmit = function( selectedText ) {
	selectedText = this.autoCompleteMenuPrefix + selectedText + this.autoCompleteMenuPostfix ;

	this.autoCompleteMenu.destroy() ;
	this.autoCompleteMenu = null ;
	this.autoCompleteMenuPrefix = null ;
	this.autoCompleteMenuPostfix = null ;

	this.document.giveFocusTo( this ) ;
	this.runAutoCompleted( selectedText ) ;
} ;



InlineInput.prototype.onAutoCompleteMenuItemFocus = function( selectedText , focus ) {
	if ( ! focus || this.autoCompleteRightPart ) { return ; }
	selectedText = this.autoCompleteMenuPrefix + selectedText + this.autoCompleteMenuPostfix ;

	if ( selectedText === this.autoCompleteLeftPart ) {
		this.altTextBuffer.setText( '' ) ;
	}
	else {
		this.altTextBuffer.setText( selectedText ) ;
		//this.altTextBuffer.runStateMachine() ;
	}

	this.autoResizeAndDraw() ;
} ;



InlineInput.prototype.onAutoCompleteMenuCancel = function() {
	this.autoCompleteMenu.destroy() ;
	this.autoCompleteMenu = null ;
	this.document.giveFocusTo( this ) ;
} ;



// Can be derived (e.g. by InlineFileInput)
InlineInput.prototype.submit = function() {
	if ( this.disabled || this.submitted || this.canceled ) { return ; }

	var value = this.getValue() ;
	if ( this.noEmpty && ! value ) { return ; }

	//this.submitted = true ;
	this.emit( 'submit' , value , undefined , this ) ;
} ;



InlineInput.prototype.onKey = function( key , trash , data ) {
	if ( this.autoCompleteMenu ) {
		// If the autoCompleteMenu is on, force a cancel
		this.autoCompleteMenu.emit( 'cancel' ) ;
	}

	return Element.prototype.onKey.call( this , key , trash , data ) ;
} ;



const userActions = InlineInput.prototype.userActions ;

userActions.character = function( key , trash , data ) {
	if ( this.placeholder ) {
		// Remove the placeholder on the first user input
		this.placeholder = null ;
		this.setAltContent( '' , false , true ) ;
	}

	this.insert( key ) ;
} ;

userActions.submit = function() {
	this.submit() ;
} ;

userActions.cancel = function() {
	if ( ! this.cancelable || this.disabled || this.canceled ) { return ; }
	//this.canceled = true ;
	this.emit( 'cancel' , this ) ;
} ;

userActions.autoComplete = function() {
	if ( ! this.autoComplete ) { return ; }
	this.runAutoComplete( this.autoComplete ) ;
} ;

userActions.historyAutoComplete = function() {
	if ( ! this.autoComplete ) { return ; }
	this.runAutoComplete( this.history ) ;
} ;

userActions.historyPrevious = function() {
	if ( this.contentIndex <= 0 ) { return ; }
	this.contentArray[ this.contentIndex ] = this.getContent() ;
	this.contentIndex -- ;
	this.setContent( this.contentArray[ this.contentIndex ] ) ;
	this.textBuffer.runStateMachine() ;
	this.autoResizeAndDraw() ;
} ;

userActions.historyNext = function() {
	if ( this.contentIndex >= this.contentArray.length - 1 ) { return ; }
	this.contentArray[ this.contentIndex ] = this.getContent() ;
	this.contentIndex ++ ;
	this.setContent( this.contentArray[ this.contentIndex ] ) ;
	this.textBuffer.runStateMachine() ;
	this.autoResizeAndDraw() ;
} ;

userActions.backDelete = function() {
	this.textBuffer.backDelete() ;
	this.textBuffer.runStateMachine() ;

	if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; } // async
	else { this.autoResizeAndDraw() ; }
} ;

userActions.delete = function() {
	this.textBuffer.delete() ;
	this.textBuffer.runStateMachine() ;

	if ( this.useAutoCompleteHint ) { this.runAutoCompleteHint( this.autoComplete ) ; }	// async
	else { this.autoResizeAndDraw() ; }
} ;


},{"../autoComplete.js":7,"./EditableTextBox.js":24,"./Element.js":25,"./RowMenu.js":33,"./TextBox.js":38,"seventh":114,"string-kit":133}],29:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const TextBox = require( './TextBox.js' ) ;
const RowMenu = require( './RowMenu.js' ) ;

const Promise = require( 'seventh' ) ;
const string = require( 'string-kit' ) ;



function InlineMenu( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	if ( options.value ) { options.content = options.value ; }

	// It is always 1 at the begining
	options.outputHeight = 1 ;

	this.promptTextBox = null ;

	if ( options.prompt ) {
		this.promptTextBox = new TextBox( Object.assign(
			{
				textAttr: options.textAttr
			} ,
			options.prompt ,
			{
				internal: true ,
				//parent: this ,
				outputX: options.outputX || options.x ,
				outputY: options.outputY || options.y ,
				outputWidth: options.outputWidth || options.width ,
				outputHeight: options.outputHeight || options.height ,
				lineWrap: options.lineWrap ,
				wordWrap: options.wordWrap || options.wordwrap
			}
		) ) ;

		// Drop void cells
		this.promptTextBox.textBuffer.setVoidAttr( null ) ;

		let size = this.promptTextBox.getContentSize() ;
		this.promptTextBox.setSizeAndPosition( size ) ;

		if ( size.height > 1 ) {
			options.outputY = ( options.outputY || options.y ) + size.height - 1 ;
		}

		this.leftMargin = this.promptTextBox.outputWidth ;
	}

	RowMenu.call( this , options ) ;

	if ( this.promptTextBox ) {
		this.attach( this.promptTextBox ) ;
	}

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'InlineMenu' && ! options.noDraw ) { this.draw() ; }
}

module.exports = InlineMenu ;
Element.inherit( InlineMenu , RowMenu ) ;



// Pre-compute page and eventually create Buttons automatically
InlineMenu.prototype.initChildren = function( noInitPage = false ) {
	RowMenu.prototype.initChildren.call( this ) ;

	// Only initPage if we are not a superclass of the object
	if ( this.elementType === 'InlineMenu' && ! noInitPage ) { this.initPage() ; }
} ;


},{"./Element.js":25,"./RowMenu.js":33,"./TextBox.js":38,"seventh":114,"string-kit":133}],30:[function(require,module,exports){
(function (process){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const Text = require( './Text.js' ) ;
const Button = require( './Button.js' ) ;
const RowMenu = require( './RowMenu.js' ) ;
const ColumnMenu = require( './ColumnMenu.js' ) ;

const string = require( 'string-kit' ) ;



function Inspector( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	this.onPropertiesColumnMenuPageInit = this.onPropertiesColumnMenuPageInit.bind( this ) ;
	this.onPropertiesColumnMenuSubmit = this.onPropertiesColumnMenuSubmit.bind( this ) ;
	this.onBreadCrumbRowMenuSubmit = this.onBreadCrumbRowMenuSubmit.bind( this ) ;

	if ( ! options.outputWidth && ! options.width ) { options.outputWidth = 78 ; }

	Element.call( this , options ) ;

	//this.submitValue = null ;

	this.inspectedObject = options.inspectedObject ;

	// TMP?
	this.breadCrumbText = null ;
	this.breadCrumbRowMenu = null ;
	this.propertiesColumnMenu = null ;
	this.valueFieldTextList = [] ;
	this.inspectStack = [] ;

	this.buttonsDef = options.buttons || [] ;
	this.buttons = [] ;
	this.focusChild = null ;
	this.onButtonSubmit = this.onButtonSubmit.bind( this ) ;
	this.onFocus = this.onFocus.bind( this ) ;

	// Global default attributes
	this.textAttr = options.textAttr || null ;
	this.voidAttr = options.voidAttr || options.emptyAttr || null ;
	this.buttonFocusAttr = options.buttonFocusAttr || null ;
	this.buttonBlurAttr = options.buttonBlurAttr || null ;
	this.turnedOnBlurAttr = options.turnedOnBlurAttr || null ;
	this.turnedOnFocusAttr = options.turnedOnFocusAttr || null ;
	this.turnedOffBlurAttr = options.turnedOffBlurAttr || null ;
	this.turnedOffFocusAttr = options.turnedOffFocusAttr || null ;

	if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; }
	if ( options.textInputKeyBindings ) { this.textInputKeyBindings = options.textInputKeyBindings ; }

	this.on( 'key' , this.onKey ) ;
	this.on( 'focus' , this.onFocus ) ;

	this.initChildren() ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'Inspector' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Inspector ;
Element.inherit( Inspector ) ;



Inspector.prototype.needInput = true ;



Inspector.prototype.keyBindings = {
	LEFT: 'levelUp' ,
	BACKSPACE: 'levelUp'
} ;



Inspector.prototype.textInputKeyBindings = {} ;
Inspector.prototype.selectInputKeyBindings = {} ;
Inspector.prototype.selectMultiInputKeyBindings = {} ;



// Create Button automatically
Inspector.prototype.initChildren = function( noInitLevel = false ) {
	var offsetX = 0 , offsetY = 0 ,
		buttonsTextWidth = 0 , buttonSpacing = 0 ;

	// Submit Button part
	if ( ! this.buttonsDef.length ) {
		this.buttonsDef.push( {
			content: 'Submit' ,
			value: 'submit'
		} ) ;
	}

	this.buttonsDef.forEach( def => {
		def.contentWidth = Element.computeContentWidth( def.content , def.contentHasMarkup ) ;
		buttonsTextWidth += def.contentWidth ;
	} ) ;

	buttonSpacing = Math.floor( ( this.outputWidth - buttonsTextWidth ) / ( this.buttonsDef.length + 1 ) ) ;

	offsetX = buttonSpacing ;
	offsetY ++ ;

	/*
	this.buttonsDef.forEach( ( def , index ) => {
		this.buttons[ index ] = new Button( {
			internal: true ,
			parent: this ,
			content: def.content ,
			value: def.value ,
			outputX: this.outputX + offsetX ,
			outputY: this.outputY + offsetY ,
			focusAttr: def.focusAttr || this.buttonFocusAttr ,
			blurAttr: def.blurAttr || this.buttonBlurAttr ,
			noDraw: true
		} ) ;

		this.buttons[ index ].on( 'submit' , this.onButtonSubmit ) ;

		offsetX += def.contentWidth + buttonSpacing ;
	} ) ;
	*/

	this.inspectStack = [ { object: this.inspectedObject , key: '' } ] ;

	// Only initLevel if we are not a superclass of the object
	if ( this.elementType === 'Inspector' && ! noInitLevel ) { this.initLevel() ; }
} ;



// Init the UI for the current depth level, depending on the stack
Inspector.prototype.initLevel = function() {
	this.resetChildren() ;

	var current = this.inspectStack[ this.inspectStack.length - 1 ] ,
		subObject = current.object ,
		breadCrumbMenuItems = [] ,
		propertiesMenuItems = [] ,
		path = this.inspectStack.map( e => e.key ).join( '.' ) ;

	this.breadCrumbText = new Text( {
		internal: true ,
		parent: this ,
		x: this.outputX ,
		y: this.outputY ,
		attr: { bgColor: 'cyan' , color: 'white' , bold: true } ,
		content: 'Path> '
	} ) ;

	breadCrumbMenuItems =  this.inspectStack.map( ( stackItem , index ) => {
		var key = stackItem.key ;

		return {
			content: index ? key : '<root>' ,
			value: index
		} ;
	} ) ;

	this.breadCrumbRowMenu = new RowMenu( {
		internal: this ,
		parent: this ,
		x: this.breadCrumbText.outputX + this.breadCrumbText.outputWidth ,
		y: this.outputY ,
		width: this.outputWidth - this.breadCrumbText.outputWidth ,
		separator: '.' ,
		items: breadCrumbMenuItems
	} ) ;

	propertiesMenuItems = Object.keys( subObject ).map( key => {
		var value = subObject[ key ] ;

		var item = {
			content: key ,
			value: key
		} ;

		if ( ! value || typeof value !== 'object' ) {
			item.disabled = true ;
		}

		return item ;
	} ) ;

	this.propertiesColumnMenu = new ColumnMenu( {
		internal: this ,
		parent: this ,
		x: this.outputX ,
		y: this.outputY + 2 ,
		width: Math.round( this.outputWidth / 2.5 ) ,
		pageMaxHeight: this.outputHeight - 4 ,
		blurLeftPadding: '^;  ' ,
		focusLeftPadding: '^;^R> ' ,
		disabledLeftPadding: '^;  ' ,
		paddingHasMarkup: true ,
		buttonBlurAttr: { bgColor: '@dark-gray' , color: 'white' , bold: true } ,
		/*
		buttonKeyBindings: {
			ENTER: 'submit' ,
			BACKSPACE: 'submit' ,
			LEFT: 'submit'
		} ,
		buttonActionKeyBindings: {
			BACKSPACE: 'levelUp' ,
			LEFT: 'levelUp'
		} ,
		*/
		items: propertiesMenuItems
	} ) ;

	this.propertiesColumnMenu.on( 'submit' , this.onPropertiesColumnMenuSubmit ) ;
	this.propertiesColumnMenu.on( 'previousPage' , this.onPropertiesColumnMenuPageInit ) ;
	this.propertiesColumnMenu.on( 'nextPage' , this.onPropertiesColumnMenuPageInit ) ;
	this.onPropertiesColumnMenuPageInit( this.propertiesColumnMenu.page ) ;

	this.breadCrumbRowMenu.on( 'submit' , this.onBreadCrumbRowMenuSubmit ) ;

	if ( current.fromKey !== undefined ) {
		this.propertiesColumnMenu.focusValue( current.fromKey ) ;
	}
	else {
		this.document.giveFocusTo( this.propertiesColumnMenu ) ;
	}
} ;



Inspector.prototype.createValueFieldTexts = function() {
	var current = this.inspectStack[ this.inspectStack.length - 1 ] ,
		subObject = current.object ;

	for ( let valueFieldText of this.valueFieldTextList ) { valueFieldText.destroyNoRedraw() ; }
	this.valueFieldTextList.length = 0 ;

	for ( let button of this.propertiesColumnMenu.buttons ) {
		if ( button.def.internalRole ) { continue ; }

		let valueFieldTextContent , valueFieldTextAttr ;
		let value = subObject[ button.def.value ] ;

		if ( value && typeof value === 'object' ) {
			let proto = Object.getPrototypeOf( value ) ;

			if ( Array.isArray( value ) ) {
				valueFieldTextContent = proto?.constructor?.name ?? '<Array>' ;
				valueFieldTextContent =
					proto?.constructor?.name ? '<' + proto.constructor.name + '>' :
					valueFieldTextContent = '<unknown array> ' ;

				if ( value.length <= 10 ) {
					valueFieldTextContent += ' ' + string.format( "%[1]n" , value ) ;
				}
				else {
					valueFieldTextContent += ' [...]' ;
				}
			}
			else {
				valueFieldTextContent =
					! proto ? '<null>' :
					proto.constructor?.name ? '<' + proto.constructor.name + '>' :
					valueFieldTextContent = '<unknown object> ' ;

				if ( Object.keys( value ).length <= 10 ) {
					valueFieldTextContent += ' ' + string.format( "%[1]n" , value ) ;
				}
				else {
					valueFieldTextContent += ' {...}' ;
				}
			}

			valueFieldTextAttr = { bgColor: '@orange--' , color: '@lighter-gray' } ;
		}
		else if ( typeof value === 'boolean' || value === null || value === undefined ) {
			valueFieldTextContent = '' + value ;
			valueFieldTextAttr = { bgColor: 'blue' , color: 'brightMagenta' , bold: true } ;
		}
		else if ( typeof value === 'number' ) {
			valueFieldTextContent = '' + value ;
			valueFieldTextAttr = { bgColor: 'blue' , color: 'brightCyan' } ;
		}
		else if ( typeof value === 'string' ) {
			valueFieldTextContent = '' + value ;
			valueFieldTextAttr = { bgColor: 'blue' , color: 'white' } ;
		}
		else {
			valueFieldTextContent = '' + value ;
			valueFieldTextAttr = { bgColor: 'blue' , color: 'gray' } ;
		}

		let valueFieldText = new Text( {
			internal: true ,
			parent: this ,
			x: button.outputX + button.outputWidth + 2 ,
			y: button.outputY ,
			width: this.outputWidth - button.outputWidth - 2 ,
			attr: valueFieldTextAttr ,
			content: valueFieldTextContent ,
			contentEllipsis: '…' ,
			noDraw: true
		} ) ;

		this.valueFieldTextList.push( valueFieldText ) ;
	}

	this.redraw() ;
} ;



// Delete all children element
Inspector.prototype.resetChildren = function() {
	if ( ! this.valueFieldTextList.length && ! this.propertiesColumnMenu && ! this.breadCrumbText ) { return ; }

	for ( let valueFieldText of this.valueFieldTextList ) { valueFieldText.destroyNoRedraw() ; }
	this.valueFieldTextList.length = 0 ;

	if ( this.propertiesColumnMenu ) {
		this.propertiesColumnMenu.destroyNoRedraw() ;
		this.propertiesColumnMenu = null ;
	}

	if ( this.breadCrumbRowMenu ) {
		this.breadCrumbRowMenu.destroyNoRedraw() ;
		this.breadCrumbRowMenu = null ;
	}

	if ( this.breadCrumbText ) {
		this.breadCrumbText.destroyNoRedraw() ;
		this.breadCrumbText = null ;
	}

	this.redraw() ;
} ;



// Not very useful, but could be later
Inspector.prototype.getValue = function() { return this.inspectedObject ; } ;



Inspector.prototype.levelUp = function() {
	if ( this.inspectStack.length <= 1 ) { return ; }
	this.inspectStack.pop() ;
	this.initLevel() ;
} ;



Inspector.prototype.onPropertiesColumnMenuPageInit = function() {
	this.createValueFieldTexts() ;
} ;



Inspector.prototype.onPropertiesColumnMenuSubmit = function( buttonValue , action , menu , button ) {
	if ( action ) {
		if ( action === 'levelUp' ) {
			if ( this.inspectStack.length <= 1 ) { return ; }
			this.inspectStack.pop() ;
		}
	}
	else if ( button.internalRole === 'parent' ) {
		if ( this.inspectStack.length <= 1 ) { return ; }
		this.inspectStack.pop() ;
	}
	else {
		let current = this.inspectStack[ this.inspectStack.length - 1 ] ,
			subObject = current.object ;

		if ( subObject[ buttonValue ] && typeof subObject[ buttonValue ] === 'object' ) {
			current.fromKey = buttonValue ;
			this.inspectStack.push( { object: subObject[ buttonValue ] , key: buttonValue } ) ;
		}
	}

	this.initLevel() ;
} ;



Inspector.prototype.onBreadCrumbRowMenuSubmit = function( buttonValue , action , menu , button ) {
	if ( buttonValue === this.inspectStack.length - 1 ) { return ; }
	this.inspectStack.length = buttonValue + 1 ;
	this.initLevel() ;
} ;



Inspector.prototype.onFocus = function( focus , type ) {
	if ( type === 'cycle' || type === 'backCycle' ) { return ; }

	if ( focus ) {
		// Defer to the next tick to avoid recursive events producing wrong listener order
		process.nextTick( () => {
			if ( this.propertiesColumnMenu ) {
				this.document.giveFocusTo( this.propertiesColumnMenu , 'delegate' ) ;
			}
			else {
				this.focusChild = this.focusNextChild() ;
			}
		} ) ;
	}
} ;



Inspector.prototype.onButtonSubmit = function( buttonValue , action , button ) {
	// There is no submit button for instance, it will be used once we can edit values
	/*
		this.submitValue = buttonValue ;
		this.emit( 'submit' , this.getValue() , action , this , button ) ;
	*/
} ;



const userActions = Inspector.prototype.userActions ;

userActions.levelUp = function() {
	this.levelUp() ;
} ;


}).call(this)}).call(this,require('_process'))
},{"./Button.js":17,"./ColumnMenu.js":18,"./Element.js":25,"./RowMenu.js":33,"./Text.js":37,"_process":191,"string-kit":133}],31:[function(require,module,exports){
(function (process){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const Text = require( './Text.js' ) ;
const EditableTextBox = require( './EditableTextBox.js' ) ;
const SelectList = require( './SelectList.js' ) ;
const SelectListMulti = require( './SelectListMulti.js' ) ;

const string = require( 'string-kit' ) ;
//const autoComplete = require( './autoComplete.js' ) ;


// Labeled: american english, Labelled british english
// (to me, 'labelled' seems more natural, but there are 10 times more results on Google for 'labeled', so I will go for it)
function LabeledInput( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	Element.call( this , options ) ;

	// For text-input only
	this.hiddenContent = options.hiddenContent ;
	this.hasInputFocus = false ;

	// For SelectList, this apply temp zIndex manipulation for the children to this element
	this.interceptTempZIndex = true ;

	this.labelFocusAttr = options.labelFocusAttr || { bold: true } ;
	this.labelBlurAttr = options.labelBlurAttr || { dim: true } ;

	this.buttonBlurAttr = options.buttonBlurAttr || { bgColor: 'cyan' , color: 'white' , bold: true } ;
	this.buttonFocusAttr = options.buttonFocusAttr || { bgColor: 'brightCyan' , color: 'black' , bold: true } ;
	this.buttonDisabledAttr = options.buttonDisabledAttr || { bgColor: 'cyan' , color: 'gray' , bold: true } ;
	this.buttonSubmittedAttr = options.buttonSubmittedAttr || { bgColor: 'brightCyan' , color: 'brightWhite' , bold: true } ;
	this.turnedOnBlurAttr = options.turnedOnBlurAttr || { bgColor: 'cyan' } ;
	this.turnedOnFocusAttr = options.turnedOnFocusAttr || { bgColor: 'brightCyan' , color: 'gray' , bold: true } ;
	this.turnedOffBlurAttr = options.turnedOffBlurAttr || { bgColor: 'gray' , dim: true } ;
	this.turnedOffFocusAttr = options.turnedOffFocusAttr || { bgColor: 'white' , color: 'black' , bold: true } ;

	// TextBufffer needs computed attr, not object one
	this.textAttr = options.textAttr || { bgColor: 'blue' } ;
	this.voidAttr = options.voidAttr || options.emptyAttr || { bgColor: 'blue' } ;

	if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; }

	if ( this.label ) {
		this.labelText = new Text( {
			internal: true ,
			parent: this ,
			content: this.label ,
			x: this.outputX ,
			y: this.outputY ,
			height: 1 ,
			attr: this.labelBlurAttr ,
			leftPadding: this.labelBlurLeftPadding ,
			rightPadding: this.labelBlurRightPadding ,
			noDraw: true
		} ) ;
	}

	this.inputType = options.type || 'text' ;

	this.onFocus = this.onFocus.bind( this ) ;
	this.onClick = this.onClick.bind( this ) ;
	this.onInputSubmit = this.onInputSubmit.bind( this ) ;

	this.initInput( options ) ;
	this.updateStatus() ;

	this.on( 'key' , this.onKey ) ;
	this.on( 'focus' , this.onFocus ) ;
	this.on( 'click' , this.onClick ) ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'LabeledInput' && ! options.noDraw ) { this.draw() ; }
}

module.exports = LabeledInput ;
Element.inherit( LabeledInput ) ;



LabeledInput.prototype.needInput = true ;
LabeledInput.prototype.noChildFocus = true ;
LabeledInput.prototype.propagateZ = true ;



LabeledInput.prototype.keyBindings = {
	ENTER: 'submit' ,
	KP_ENTER: 'submit' ,
	ALT_ENTER: 'submit'
	//ESCAPE: 'cancel' ,
} ;



LabeledInput.prototype.editableTextBoxKeyBindings = {
	CTRL_K: 'meta' ,
	BACKSPACE: 'backDelete' ,
	DELETE: 'delete' ,
	LEFT: 'backward' ,
	RIGHT: 'forward' ,
	CTRL_LEFT: 'startOfWord' ,
	CTRL_RIGHT: 'endOfWord' ,
	HOME: 'startOfLine' ,
	END: 'endOfLine' ,
	CTRL_B: 'startOfSelection' ,
	CTRL_E: 'endOfSelection' ,

	// T for Transfer
	CTRL_T: 'moveSelection' ,
	ALT_T: 'copyToDocumentClipboard' ,
	META_T: 'copyToSystemClipboard' ,
	// P for Paste / Put
	CTRL_P: 'pasteSelection' ,
	ALT_P: 'pasteDocumentClipboard' ,
	META_P: 'pasteSystemClipboard' ,
	// D for Delete
	CTRL_D: 'deleteSelection' ,
	ALT_D: 'clearDocumentClipboard' ,
	META_D: 'clearSystemClipboard'
} ;



LabeledInput.prototype.multiLineEditableTextBoxKeyBindings = Object.assign( {} , LabeledInput.prototype.editableTextBoxKeyBindings , {
	ENTER: 'newLine' ,
	KP_ENTER: 'newLine' ,
	UP: 'up' ,
	DOWN: 'down' ,
	PAGE_UP: 'scrollUp' ,
	PAGE_DOWN: 'scrollDown' ,
	CTRL_B: 'startOfSelection' ,
	CTRL_E: 'endOfSelection' ,
	CTRL_K: 'meta' ,
	// We copy vi/vim here, that use 'y' for copy (yank) and 'p' for paste (put)
	CTRL_Y: 'copy' ,
	META_Y: 'copyClipboard' ,
	CTRL_P: 'paste' ,
	META_P: 'pasteClipboard'
} ) ;



LabeledInput.prototype.selectListKeyBindings = {
	UP: 'previous' ,
	DOWN: 'next' ,
	ENTER: 'submit' ,
	KP_ENTER: 'submit'
} ;



LabeledInput.prototype.selectListMultiKeyBindings = {
	UP: 'previous' ,
	DOWN: 'next' ,
	ENTER: 'submit' ,
	KP_ENTER: 'submit'
} ;



LabeledInput.prototype.initInput = function( options ) {
	switch ( this.inputType ) {
		case 'text' :
			this.initTextInput( options ) ;
			break ;
		case 'select' :
			this.initSelectInput( options ) ;
			break ;
		case 'selectMulti' :
			this.initSelectMultiInput( options ) ;
			break ;
		default :
			throw new Error( 'Unknown input type: ' + this.inputType ) ;
	}

	// Allow label highlight
	this.input.on( 'focus' , this.onChildFocus.bind( this ) ) ;
} ;



LabeledInput.prototype.initTextInput = function( options ) {
	if ( options.inputKeyBindings ) { this.inputKeyBindings = options.inputKeyBindings ; }
	else if ( options.allowNewLine ) { this.inputKeyBindings = this.multiLineEditableTextBoxKeyBindings ; }
	else { this.inputKeyBindings = this.editableTextBoxKeyBindings ; }

	this.input = new EditableTextBox( {
		internal: true ,
		parent: this ,
		content: options.content ,
		value: options.value ,
		x: this.outputX + ( this.labelText ? this.labelText.outputWidth : 0 ) ,
		y: this.outputY ,
		width: this.outputWidth - ( this.labelText ? this.labelText.outputWidth : 0 ) ,
		height: this.outputHeight ,
		lineWrap: !! options.lineWrap ,
		wordWrap: !! options.wordWrap ,
		scrollable: !! options.scrollable ,
		vScrollBar: !! options.vScrollBar ,
		hScrollBar: !! options.hScrollBar ,
		hiddenContent: this.hiddenContent ,
		textAttr: this.textAttr ,
		voidAttr: this.voidAttr ,
		keyBindings: this.inputKeyBindings ,
		noDraw: true ,
		autoWidth: options.autoWidth ,
		autoHeight: options.autoHeight
	} ) ;
} ;



LabeledInput.prototype.initSelectInput = function( options ) {
	if ( options.inputKeyBindings ) { this.inputKeyBindings = options.inputKeyBindings ; }
	else { this.inputKeyBindings = this.selectListKeyBindings ; }

	this.input = new SelectList( {
		internal: true ,
		parent: this ,
		content: options.content ,
		value: options.value ,
		x: this.outputX + ( this.labelText ? this.labelText.outputWidth : 0 ) ,
		y: this.outputY ,
		width: this.outputWidth - ( this.labelText ? this.labelText.outputWidth : 0 ) ,
		items: options.items ,
		buttonBlurAttr: this.buttonBlurAttr ,
		buttonFocusAttr: this.buttonFocusAttr ,
		buttonDisabledAttr: this.buttonDisabledAttr ,
		buttonSubmittedAttr: this.buttonSubmittedAttr ,
		keyBindings: this.inputKeyBindings ,
		noDraw: true
	} ) ;

	this.input.on( 'submit' , this.onInputSubmit ) ;
} ;



LabeledInput.prototype.initSelectMultiInput = function( options ) {
	if ( options.inputKeyBindings ) { this.inputKeyBindings = options.inputKeyBindings ; }
	else { this.inputKeyBindings = this.selectListMultiKeyBindings ; }

	this.input = new SelectListMulti( {
		internal: true ,
		parent: this ,
		content: options.content ,
		value: options.value ,
		x: this.outputX + ( this.labelText ? this.labelText.outputWidth : 0 ) ,
		y: this.outputY ,
		width: this.outputWidth - ( this.labelText ? this.labelText.outputWidth : 0 ) ,
		items: options.items ,
		buttonBlurAttr: this.buttonBlurAttr ,
		buttonFocusAttr: this.buttonFocusAttr ,
		buttonDisabledAttr: this.buttonDisabledAttr ,
		buttonSubmittedAttr: this.buttonSubmittedAttr ,
		turnedOnBlurAttr: this.turnedOnBlurAttr ,
		turnedOnFocusAttr: this.turnedOnFocusAttr ,
		turnedOffBlurAttr: this.turnedOffBlurAttr ,
		turnedOffFocusAttr: this.turnedOffFocusAttr ,
		keyBindings: this.inputKeyBindings ,
		noDraw: true
	} ) ;

	this.input.on( 'submit' , this.onInputSubmit ) ;
} ;



LabeledInput.prototype.updateStatus = function() {
	/*
	if ( this.disabled ) {
		this.labelText.attr = this.labelDisabledAttr ;
		this.labelText.leftPadding = this.labelDisabledLeftPadding ;
		this.labelText.rightPadding = this.labelDisabledRightPadding ;
	}
	else if ( this.submitted ) {
		this.labelText.attr = this.labelSubmittedAttr ;
		this.labelText.leftPadding = this.labelSubmittedLeftPadding ;
		this.labelText.rightPadding = this.labelSubmittedRightPadding ;
	}
	else */
	if ( this.hasFocus || this.hasInputFocus ) {
		if ( this.labelText ) {
			this.labelText.attr = this.labelFocusAttr ;
			this.labelText.leftPadding = this.labelFocusLeftPadding ;
			this.labelText.rightPadding = this.labelFocusRightPadding ;
		}
	}
	else if ( this.labelText ) {
		this.labelText.attr = this.labelBlurAttr ;
		this.labelText.leftPadding = this.labelBlurLeftPadding ;
		this.labelText.rightPadding = this.labelBlurRightPadding ;
	}
} ;



// Directly linked to the EditableTextBox
LabeledInput.prototype.getValue = function() { return this.input.getValue() ; } ;
LabeledInput.prototype.setValue = function( value , dontDraw ) { return this.input.setValue( value , dontDraw ) ; } ;
LabeledInput.prototype.getContent = function() { return this.input.getContent() ; } ;
LabeledInput.prototype.setContent = function( content , hasMarkup , dontDraw ) { return this.input.setContent( content , hasMarkup , dontDraw ) ; } ;



LabeledInput.prototype.drawSelfCursor = function() {
	if ( this.input.drawSelfCursor ) { this.input.drawSelfCursor() ; }
} ;



LabeledInput.prototype.onKey = function( key , altKeys , data ) {
	// Give full priority to the child input
	if ( this.input.emit( 'key' , key , altKeys , data ).interrupt ) { return true ; }

	return Element.prototype.onKey.call( this , key , altKeys , data ) ;
} ;



LabeledInput.prototype.onInputSubmit = function( data ) {
	this.emit( 'submit' , this.getValue() , undefined , this ) ;
} ;



LabeledInput.prototype.onFocus = function( focus , type ) {
	if ( type === 'delegate' ) { return ; }

	if ( focus && type !== 'backCycle' && this.input ) {
		// Defer to the next tick to avoid recursive events producing wrong listener order
		process.nextTick( () => {
			this.document.giveFocusTo( this.input , 'delegate' ) ;
		} ) ;
	}
	else {
		// This is done by .onChildFocus() if there is an attached input
		this.updateStatus() ;
		//this.draw() ;
		if ( this.labelText ) { this.labelText.draw() ; }
	}
} ;



LabeledInput.prototype.onChildFocus = function( focus , type ) {
	this.hasInputFocus = focus ;
	this.updateStatus() ;
	if ( this.labelText ) { this.labelText.draw() ; }
} ;



LabeledInput.prototype.onClick = function( data ) {
	this.document.giveFocusTo( this , 'select' ) ;
} ;



const userActions = LabeledInput.prototype.userActions ;

userActions.submit = function() {
	this.emit( 'submit' , this.getValue() , undefined , this ) ;
} ;


}).call(this)}).call(this,require('_process'))
},{"./EditableTextBox.js":24,"./Element.js":25,"./SelectList.js":34,"./SelectListMulti.js":35,"./Text.js":37,"_process":191,"string-kit":133}],32:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const Container = require( './Container.js' ) ;
const boxesChars = require( '../spChars.js' ).box ;



function Layout( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	Element.call( this , options ) ;

	this.onParentResize = this.onParentResize.bind( this ) ;

	this.layoutDef = options.layout ;
	this.computed = {} ;
	this.boxesContainer = {} ;
	this.boxChars = boxesChars.light ;

	if ( options.boxChars ) {
		if ( typeof options.boxChars === 'object' ) {
			this.boxChars = options.boxChars ;
		}
		else if ( typeof options.boxChars === 'string' && boxesChars[ options.boxChars ] ) {
			this.boxChars = boxesChars[ options.boxChars ] ;
		}
	}

	this.on( 'parentResize' , this.onParentResize ) ;

	this.computeBoundingBoxes() ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'Layout' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Layout ;
Element.inherit( Layout ) ;



Layout.prototype.computeBoundingBoxes = function() {
	var computed = this.computed = {} ;

	var layoutDef = this.layoutDef ;

	var parent = {
		width_: this.outputDst.width ,
		height_: this.outputDst.height ,
		dx_: this.outputDst.width - 1 ,
		dy_: this.outputDst.height - 1 ,
		xmin_: 0 ,
		ymin_: 0
	} ;

	var inProgress = {
		offsetX: ( this.layoutDef.x ) || 0 ,
		offsetY: ( this.layoutDef.y ) || 0 ,
		remainingDx: parent.dx_ ,
		remainingDy: parent.dy_
	} ;

	this.computeBoundingBoxes_( layoutDef , computed , parent , inProgress ) ;
} ;



Layout.prototype.computeBoundingBoxes_ = function( layoutDef , computed , parent , inProgress ) {
	var i , nextInProgress , hasChild = false ;

	//console.error( "\n\nlayoutDef #" + layoutDef.id + ':\n' , computed ) ;

	this.computeDxDy( layoutDef , computed , parent , inProgress ) ;

	//console.error( "\n\nlayoutDef #" + layoutDef.id + ':\n' , computed ) ;

	computed.xmin_ = parent.xmin_ + inProgress.offsetX ;
	computed.xmax_ = computed.xmin_ + computed.dx_ ;
	computed.ymin_ = parent.ymin_ + inProgress.offsetY ;
	computed.ymax_ = computed.ymin_ + computed.dy_ ;

	//console.error( "\n\nlayoutDef #" + layoutDef.id + ':\n' , computed ) ;

	// Check if it goes out of its parent
	if ( computed.xmax_ > parent.xmax_ ) {
		computed.xmax_ = parent.xmax_ ;
		computed.dx_ = computed.xmax_ - computed.xmin_ ;
	}

	if ( computed.ymax_ > parent.ymax_ ) {
		computed.ymax_ = parent.ymax_ ;
		computed.dy_ = computed.ymax_ - computed.ymin_ ;
	}

	// Width and height are not used internally, but provided for userland
	computed.width_ = computed.dx_ + 1 ;
	computed.height_ = computed.dy_ + 1 ;

	computed.columns = [] ;
	computed.rows = [] ;

	//console.error( "\n\nlayoutDef #" + layoutDef.id + ':\n' , computed ) ;

	nextInProgress = {
		offsetX: 0 ,
		offsetY: 0 ,
		remainingDx: computed.dx_ ,
		remainingDy: computed.dy_ ,
		autoDxCount: 0 ,
		autoDyCount: 0
	} ;

	if ( layoutDef.columns && layoutDef.columns.length ) {
		// First pass
		for ( i = 0 ; i < layoutDef.columns.length ; i ++ ) {
			computed.columns[ i ] = {} ;
			this.computeDxDy( layoutDef.columns[ i ] , computed.columns[ i ] , computed , nextInProgress , true ) ;

			if ( computed.columns[ i ].dx_ !== undefined ) { nextInProgress.remainingDx -= computed.columns[ i ].dx_ ; }
			else { nextInProgress.autoDxCount ++ ; }
		}

		for ( i = 0 ; i < layoutDef.columns.length ; i ++ ) {
			this.computeBoundingBoxes_( layoutDef.columns[ i ] , computed.columns[ i ] , computed , nextInProgress ) ;
			nextInProgress.offsetX = computed.columns[ i ].xmax_ - computed.xmin_ ;
		}

		hasChild = true ;
	}
	else if ( layoutDef.rows && layoutDef.rows.length ) {
		// First pass
		for ( i = 0 ; i < layoutDef.rows.length ; i ++ ) {
			computed.rows[ i ] = {} ;
			this.computeDxDy( layoutDef.rows[ i ] , computed.rows[ i ] , computed , nextInProgress , true ) ;

			if ( computed.rows[ i ].dy_ !== undefined ) { nextInProgress.remainingDy -= computed.rows[ i ].dy_ ; }
			else { nextInProgress.autoDyCount ++ ; }
		}

		for ( i = 0 ; i < layoutDef.rows.length ; i ++ ) {
			this.computeBoundingBoxes_( layoutDef.rows[ i ] , computed.rows[ i ] , computed , nextInProgress ) ;
			nextInProgress.offsetY = computed.rows[ i ].ymax_ - computed.ymin_ ;
		}

		hasChild = true ;
	}

	computed.width_ = computed.dx_ + 1 ;
	computed.height_ = computed.dy_ + 1 ;

	this.round( computed ) ;
	//console.error( "\n\nfinal #" + layoutDef.id + ':\n' , computed ) ;

	// Container surfaces are only created for "leaf" boxes, i.e. boxes that don't have child
	if ( ! hasChild ) {
		if ( this.boxesContainer[ layoutDef.id ] ) {
			if ( this.boxesContainer[ layoutDef.id ].width !== computed.width - 2 || this.boxesContainer[ layoutDef.id ].height !== computed.height - 2 ) {
				this.boxesContainer[ layoutDef.id ].resize( {
					x: 0 ,
					y: 0 ,
					width: computed.width - 2 ,
					height: computed.height - 2
				} ) ;
			}

			this.boxesContainer[ layoutDef.id ].moveTo( computed.xmin + 1 , computed.ymin + 1 , true ) ;
		}
		else {
			var container = new Container( {
				internal: true ,
				id: layoutDef.id ,
				parent: this ,
				outputDst: this.outputDst ,
				outputX: computed.xmin + 1 ,
				outputY: computed.ymin + 1 ,
				outputWidth: computed.width - 2 ,
				outputHeight: computed.height - 2
			} ) ;

			layoutDef.id = container.id ;
			this.boxesContainer[ layoutDef.id ] = container ;
		}
	}
} ;



Layout.prototype.computeDxDy = function( layoutDef , computed , parent , inProgress , firstPass ) {
	//console.error( ">>>>>>>>>> #" + layoutDef.id + ' firstPass: ' , !! firstPass ) ;

	// Dx
	if ( firstPass || computed.dx_ === undefined ) {
		if ( layoutDef.width !== undefined ) {
			computed.dx_ = Math.max( 0 , Math.min( parent.dx_ , layoutDef.width - 1 ) ) ;
		}
		else if ( layoutDef.widthPercent !== undefined ) {
			computed.dx_ = Math.max( 0 , Math.min( parent.dx_ , parent.dx_ * layoutDef.widthPercent / 100 ) ) ;
		}
		else if ( ! firstPass ) {
			//console.error( ">>>>>>>>>> #" + layoutDef.id + ' remaining dx: ' , inProgress.remainingDx , '/' , inProgress.autoDxCount , ' --- ' , inProgress ) ;
			computed.dx_ = Math.max( 0 , inProgress.remainingDx / ( inProgress.autoDxCount || 1 ) ) ;
			//console.error( ">>>>>>>>>> #" + layoutDef.id + ' computed dx: ' , computed.dx_ ) ;
		}
	}

	// Dy
	if ( firstPass || computed.dy_ === undefined ) {
		if ( layoutDef.height !== undefined ) {
			computed.dy_ = Math.max( 0 , Math.min( parent.dy_ , layoutDef.height - 1 ) ) ;
		}
		else if ( layoutDef.heightPercent !== undefined ) {
			computed.dy_ = Math.max( 0 , Math.min( parent.dy_ , parent.dy_ * layoutDef.heightPercent / 100 ) ) ;
		}
		else if ( ! firstPass ) {
			computed.dy_ = Math.max( 0 , inProgress.remainingDy / ( inProgress.autoDyCount || 1 ) ) ;
		}
	}
} ;



Layout.prototype.round = function( computed ) {
	computed.xmin = Math.round( computed.xmin_ ) ;
	computed.xmax = Math.round( computed.xmax_ ) ;
	computed.ymin = Math.round( computed.ymin_ ) ;
	computed.ymax = Math.round( computed.ymax_ ) ;

	computed.dx = computed.xmax - computed.xmin ;
	computed.dy = computed.ymax - computed.ymin ;
	computed.width = computed.dx + 1 ;
	computed.height = computed.dy + 1 ;
} ;



Layout.prototype.preDrawSelf = function() {
	var y , tees = {} ;

	//this.computeBoundingBoxes() ;

	// Draw the top border
	this.outputDst.put(
		{ x: this.computed.xmin , y: this.computed.ymin } ,
		this.boxChars.topLeft + this.boxChars.horizontal.repeat( this.computed.dx - 1 ) + this.boxChars.topRight
	) ;

	// Draw the bottom border
	this.outputDst.put(
		{ x: this.computed.xmin , y: this.computed.ymax } ,
		this.boxChars.bottomLeft + this.boxChars.horizontal.repeat( this.computed.dx - 1 ) + this.boxChars.bottomRight
	) ;

	// Draw the left and right border
	for ( y = this.computed.ymin + 1 ; y < this.computed.ymax ; y ++ ) {
		this.outputDst.put( { x: this.computed.xmin , y: y } , this.boxChars.vertical ) ;
		this.outputDst.put( { x: this.computed.xmax , y: y } , this.boxChars.vertical ) ;
	}

	this.drawRecursive( this.computed , tees ) ;
} ;



Layout.prototype.drawRecursive = function( computed , tees ) {
	var i ;

	if ( computed.columns.length ) {
		for ( i = 0 ; i < computed.columns.length ; i ++ ) {
			this.drawColumn( computed.columns[ i ] , tees , i === computed.columns.length - 1 ) ;
		}
	}
	else if ( computed.rows.length ) {
		for ( i = 0 ; i < computed.rows.length ; i ++ ) {
			this.drawRow( computed.rows[ i ] , tees , i === computed.rows.length - 1 ) ;
		}
	}
} ;



Layout.prototype.drawColumn = function( computed , tees , last ) {
	var y ;

	if ( ! last ) {
		// Draw Tee-junction
		this.drawTee( computed.xmax , computed.ymin , 'top' , tees ) ;
		this.drawTee( computed.xmax , computed.ymax , 'bottom' , tees ) ;

		// Draw the right border
		for ( y = computed.ymin + 1 ; y < computed.ymax ; y ++ ) {
			this.outputDst.put( { x: computed.xmax , y: y } , this.boxChars.vertical ) ;
		}
	}

	this.drawRecursive( computed , tees ) ;
} ;



Layout.prototype.drawTee = function( x , y , type , tees ) {
	var key = x + ':' + y ;

	if ( ! tees[ key ] ) {
		this.outputDst.put( { x: x , y: y } , this.boxChars[ type + 'Tee' ] ) ;
		tees[ key ] = type ;
	}
	else if ( tees[ key ] !== type ) {
		this.outputDst.put( { x: x , y: y } , this.boxChars.cross ) ;
	}
} ;



Layout.prototype.drawRow = function( computed , tees , last ) {
	if ( ! last ) {
		// Draw Tee-junction
		this.drawTee( computed.xmin , computed.ymax , 'left' , tees ) ;
		this.drawTee( computed.xmax , computed.ymax , 'right' , tees ) ;

		// Draw the bottom border
		this.outputDst.put( { x: computed.xmin + 1 , y: computed.ymax } , this.boxChars.horizontal.repeat( computed.dx - 1 ) ) ;
	}

	this.drawRecursive( computed , tees ) ;
} ;



Layout.prototype.onParentResize = function() {
	this.computeBoundingBoxes() ;
} ;


},{"../spChars.js":54,"./Container.js":21,"./Element.js":25}],33:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const BaseMenu = require( './BaseMenu.js' ) ;
const Button = require( './Button.js' ) ;
const ToggleButton = require( './ToggleButton.js' ) ;

const misc = require( '../misc.js' ) ;
const string = require( 'string-kit' ) ;



// Inherit from BaseMenu for common methods

function RowMenu( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	if ( ! options.outputWidth && ! options.width ) {
		options.outputWidth = Math.min( options.parent.inputWidth , options.parent.outputWidth ) ;
	}

	this.buttonPaddingWidth = 0 ;
	this.buttonSymbolWidth = 0 ;
	this.pageItemsDef = [] ;

	BaseMenu.call( this , options ) ;

	this.justify = !! options.justify ;
	this.leftMargin = this.leftMargin ?? 0 ;	// useful for InlineMenu: it's the place where the prompt is put

	this.separator = options.separator || options.buttonSeparator || ' ' ;
	this.separatorHasMarkup = !! ( options.separatorHasMarkup || options.buttonSeparatorHasMarkup ) ;
	this.separatorAttr = Object.assign( {} , this.backgroundAttr , options.separatorAttr || options.buttonSeparatorAttr ) ;
	this.separatorWidth = 0 ;	// Computed later

	this.initChildren() ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'RowMenu' && ! options.noDraw ) { this.draw() ; }
}

module.exports = RowMenu ;
Element.inherit( RowMenu , BaseMenu ) ;



RowMenu.prototype.inlineNewLine = true ;
RowMenu.prototype.ButtonClass = Button ;



RowMenu.prototype.defaultOptions = {
	buttonBlurAttr: { bgColor: 'white' , color: 'black' } ,
	buttonFocusAttr: { bgColor: 'green' , color: 'blue' , dim: true } ,
	buttonDisabledAttr: { bgColor: 'white' , color: 'brightBlack' } ,
	buttonSubmittedAttr: { bgColor: 'brightWhite' , color: 'brightBlack' }
} ;



RowMenu.prototype.keyBindings = {
	LEFT: 'previous' ,
	RIGHT: 'next' ,
	PAGE_UP: 'previousPage' ,
	PAGE_DOWN: 'nextPage' ,
	HOME: 'firstPage' ,
	END: 'lastPage' ,
	//ENTER: 'submit' ,
	//KP_ENTER: 'submit' ,
	ALT_ENTER: 'submit'
} ;



RowMenu.prototype.buttonKeyBindings = {
	ENTER: 'submit' ,
	KP_ENTER: 'submit'
} ;



RowMenu.prototype.toggleButtonKeyBindings = {
	ENTER: 'toggle' ,
	KP_ENTER: 'toggle'
} ;



// Pre-compute page and eventually create Buttons automatically
RowMenu.prototype.initChildren = function( noInitPage = false ) {
	if ( ! this.itemsDef.length ) { return ; }

	this.buttonPaddingWidth =
		Math.max(
			Element.computeContentWidth( this.blurLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.focusLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.disabledLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.submittedLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOnFocusLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOffFocusLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOnBlurLeftPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOffBlurLeftPadding , this.paddingHasMarkup )
		) + Math.max(
			Element.computeContentWidth( this.blurRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.focusRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.disabledRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.submittedRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOnFocusRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOffFocusRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOnBlurRightPadding , this.paddingHasMarkup ) ,
			Element.computeContentWidth( this.turnedOffBlurRightPadding , this.paddingHasMarkup )
		) ;

	if ( this.buttonPaddingWidth > this.outputWidth - this.leftMargin ) {
		// The padding itself is bigger than the width... so what should we do?
		return ;
	}

	var ellipsisWidth = Element.computeContentWidth( this.contentEllipsis , false ) ;
	this.separatorWidth = Element.computeContentWidth( this.separator , this.separatorHasMarkup ) ;


	this.previousPageDef = Object.assign( { content: '◀' , internalRole: 'previousPage' } , this.previousPageDef ) ;
	this.previousPageDef.contentHasMarkup = this.previousPageDef.contentHasMarkup || this.previousPageDef.markup ;
	this.previousPageDef.width = this.buttonPaddingWidth + Element.computeContentWidth( this.previousPageDef.content , this.previousPageDef.contentHasMarkup ) ;
	this.previousPageDef.buttonContent = this.previousPageDef.content ;

	this.nextPageDef = Object.assign( { content: '▶' , internalRole: 'nextPage' } , this.nextPageDef ) ;
	this.nextPageDef.contentHasMarkup = this.nextPageDef.contentHasMarkup || this.nextPageDef.markup ;
	this.nextPageDef.width = this.buttonPaddingWidth + Element.computeContentWidth( this.nextPageDef.content , this.nextPageDef.contentHasMarkup ) ;
	this.nextPageDef.buttonContent = this.nextPageDef.content ;


	var page = 0 , pageWidth = 0 , pageItemCount = 0 ;

	this.itemsDef.forEach( ( def , index ) => {
		def.buttonContent = def.content ;
		def.contentHasMarkup = def.contentHasMarkup || def.markup ;

		var contentWidth = Element.computeContentWidth( def.content , def.contentHasMarkup ) ,
			isLastItem = index === this.itemsDef.length - 1 ;

		def.width = contentWidth + this.buttonPaddingWidth + this.buttonSymbolWidth ;

		var overflow = pageWidth + def.width
			+ ( pageItemCount ? this.separatorWidth : 0 )
			+ ( isLastItem ? 0 : this.nextPageDef.width + this.separatorWidth )
			- this.outputWidth - this.leftMargin ;

		//console.error( "overflow",overflow,pageWidth,def.width,isLastItem,this.nextPageDef.width,this.separatorWidth,this.outputWidth,this.leftMargin);
		if ( overflow > 0 ) {
			if ( pageItemCount ) {
				page ++ ;
				pageItemCount = 0 ;
				pageWidth = this.previousPageDef.width + this.separatorWidth ;

				overflow = pageWidth + def.width
					+ ( isLastItem ? 0 : this.nextPageDef.width + this.separatorWidth )
					- this.outputWidth - this.leftMargin ;
			}

			if ( overflow > 0 ) {
				def.buttonContent = Element.truncateContent( def.content , contentWidth - overflow - ellipsisWidth , def.contentHasMarkup ) + this.contentEllipsis ;
				contentWidth = Element.computeContentWidth( def.buttonContent , def.contentHasMarkup ) ;
			}
		}

		def.page = page ;
		pageWidth += def.width + ( pageItemCount ? this.separatorWidth : 0 ) ;
		pageItemCount ++ ;

		if ( ! this.pageItemsDef[ page ] ) { this.pageItemsDef[ page ] = [] ; }
		this.pageItemsDef[ page ].push( def ) ;
	} ) ;

	this.maxPage = page ;

	// Force at least an empty page
	if ( ! this.pageItemsDef.length ) { this.pageItemsDef.push( [] ) ; }

	this.pageItemsDef.forEach( ( pageDef , index ) => {
		if ( index ) { pageDef.unshift( this.previousPageDef ) ; }
		if ( index < this.pageItemsDef.length - 1 ) { pageDef.push( this.nextPageDef ) ; }
		pageDef.buttonsWidth = pageDef.reduce( ( acc , def ) => acc + def.width , 0 ) ;
		pageDef.buttonsAndSeparatorsWidth = pageDef.buttonsWidth + ( pageDef.length - 1 ) * this.separatorWidth ;
		pageDef.justifyWidth = Math.max( 0 ,
			this.justify ? ( this.outputWidth - this.leftMargin - pageDef.buttonsAndSeparatorsWidth ) / ( pageDef.length - 1 )
			: 0
		) ;
		//console.error( '\n>>> ' , pageDef.buttonsWidth,pageDef.buttonsAndSeparatorsWidth,pageDef.justifyWidth) ;
	} ) ;

	// Only initPage if we are not a superclass of the object
	if ( this.elementType === 'RowMenu' && ! noInitPage ) { this.initPage() ; }
} ;



RowMenu.prototype.initPage = function( page = this.page ) {
	var pageDef = this.pageItemsDef[ page ] ,
		justifyWidthError = 0 ,
		buttonOffsetX = this.leftMargin ,
		buttonOffsetY = 0 ;

	if ( ! pageDef ) { return ; }

	this.buttons.forEach( button => button.destroy( false , true ) ) ;
	this.buttons.length = 0 ;
	this.hotkeyToButtonIndex.clear() ;

	//console.error( "pageDef.justifyWidth" , pageDef.justifyWidth ) ;

	pageDef.forEach( ( def , index ) => {
		var ButtonConstructor , isToggle , key , value , blurAttr ;

		ButtonConstructor = def.internalRole ? Button : this.ButtonClass ;
		isToggle = ButtonConstructor === ToggleButton || ButtonConstructor.prototype instanceof ToggleButton ;

		key = def.key ;		// For ToggleButton
		value = this.childUseParentKeyValue && key && this.value && typeof this.value === 'object' ? this.value[ key ] : def.value ;

		if ( index % 2 ) {
			// Odd
			blurAttr = def.blurAttr || this.buttonBlurAttr ;
		}
		else {
			// Even
			blurAttr = def.evenBlurAttr || def.blurAttr || this.buttonEvenBlurAttr || this.buttonBlurAttr ;
		}

		this.buttons[ index ] = new ButtonConstructor( {
			internal: true ,
			parent: this ,
			childId: index ,
			internalRole: def.internalRole ,
			content: def.buttonContent ,
			contentHasMarkup: def.contentHasMarkup ,
			disabled: def.disabled ,
			def ,
			key ,
			value ,
			outputX: this.outputX + buttonOffsetX ,
			outputY: this.outputY + buttonOffsetY ,

			blurAttr ,
			focusAttr: def.focusAttr || this.buttonFocusAttr ,
			disabledAttr: def.disabledAttr || this.buttonDisabledAttr ,
			submittedAttr: def.submittedAttr || this.buttonSubmittedAttr ,
			turnedOnFocusAttr: def.turnedOnFocusAttr || this.buttonTurnedOnFocusAttr ,
			turnedOffFocusAttr: def.turnedOffFocusAttr || this.buttonTurnedOffFocusAttr ,
			turnedOnBlurAttr: def.turnedOnBlurAttr || this.buttonTurnedOnBlurAttr ,
			turnedOffBlurAttr: def.turnedOffBlurAttr || this.buttonTurnedOffBlurAttr ,

			blurLeftPadding: this.blurLeftPadding ,
			blurRightPadding: this.blurRightPadding ,
			focusLeftPadding: this.focusLeftPadding ,
			focusRightPadding: this.focusRightPadding ,
			disabledLeftPadding: this.disabledLeftPadding ,
			disabledRightPadding: this.disabledRightPadding ,
			submittedLeftPadding: this.submittedLeftPadding ,
			submittedRightPadding: this.submittedRightPadding ,

			turnedOnFocusLeftPadding: this.turnedOnFocusLeftPadding ,
			turnedOnFocusRightPadding: this.turnedOnFocusRightPadding ,
			turnedOffFocusLeftPadding: this.turnedOffFocusLeftPadding ,
			turnedOffFocusRightPadding: this.turnedOffFocusRightPadding ,
			turnedOnBlurLeftPadding: this.turnedOnBlurLeftPadding ,
			turnedOnBlurRightPadding: this.turnedOnBlurRightPadding ,
			turnedOffBlurLeftPadding: this.turnedOffBlurLeftPadding ,
			turnedOffBlurRightPadding: this.turnedOffBlurRightPadding ,

			paddingHasMarkup: this.paddingHasMarkup ,

			keyBindings: isToggle ? this.toggleButtonKeyBindings : this.buttonKeyBindings ,
			actionKeyBindings: isToggle ? this.toggleButtonActionKeyBindings : this.buttonActionKeyBindings ,
			shortcuts: def.shortcuts ,

			noDraw: true
		} ) ;

		this.buttons[ index ].on( 'submit' , this.onButtonSubmit ) ;
		this.buttons[ index ].on( 'focus' , this.onButtonFocus ) ;
		this.buttons[ index ].on( 'blinked' , this.onButtonBlinked ) ;

		if ( def.hotkey ) {
			if ( Array.isArray( def.hotkey ) ) {
				def.hotkey.forEach( hotkey => this.hotkeyToButtonIndex.set( hotkey , index ) ) ;
			}
			else {
				this.hotkeyToButtonIndex.set( def.hotkey , index ) ;
			}
		}

		if ( isToggle ) {
			this.buttons[ index ].on( 'toggle' , this.onButtonToggle ) ;
		}

		var justifyWidthFloat = pageDef.justifyWidth + justifyWidthError ;
		var justifyWidth = Math.round( justifyWidthFloat ) ;
		justifyWidthError = justifyWidthFloat - justifyWidth ;

		buttonOffsetX += this.buttons[ index ].outputWidth + this.separatorWidth + justifyWidth ;
	} ) ;

	// Set outputWidth to the correct value
	//if ( buttonOffsetX < this.outputWidth ) { this.needOuterDraw = true ; }
	//this.pageWidth = buttonOffsetX ;
	//this.outputWidth = buttonOffsetY ;
} ;



RowMenu.prototype.preDrawSelf = function() {
	//console.error( string.format( "Call preDrawSelf(), page %i" , this.page ));
	this.outputDst.put( { x: this.outputX + this.leftMargin , y: this.outputY , attr: this.backgroundAttr } , ' '.repeat( this.outputWidth - this.leftMargin ) ) ;

	if ( this.separator ) {
		let index , button , nextButton ;
		for ( index = 0 ; index < this.buttons.length - 1 ; index ++ ) {
			button = this.buttons[ index ] ;
			nextButton = this.buttons[ index + 1 ] ;
			this.outputDst.put( {
				x: Math.round( button.outputX + button.outputWidth + nextButton.outputX ) / 2 ,
				y: this.outputY ,
				attr: this.separatorAttr
			} ,
			this.separator
			) ;
			//console.error( string.format( "Add one at %i" , button.outputX + button.outputWidth + Math.round( this.pageItemsDef[ this.page ].justifyWidth / 2 )));
		}
		//console.error( string.format( "%Y" , this.buttons[this.buttons.length-1].content ));
	}
} ;


},{"../misc.js":48,"./BaseMenu.js":15,"./Button.js":17,"./Element.js":25,"./ToggleButton.js":40,"string-kit":133}],34:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const BaseMenu = require( './BaseMenu.js' ) ;
const ColumnMenu = require( './ColumnMenu.js' ) ;
const Button = require( './Button.js' ) ;



// Inherit from ColumnMenu for common methods

function SelectList( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	if ( ! options.master || typeof options.master !== 'object' ) {
		options.master = Object.assign( {} , this.defaultOptions.master ) ;
	}
	else {
		options.master = Object.assign( {} , this.defaultOptions.master , options.master ) ;
	}

	if ( options.content ) {
		options.master.content = options.content ;
	}

	if ( ! options.separator || typeof options.separator !== 'object' ) {
		options.separator = Object.assign( {} , this.defaultOptions.separator ) ;
	}
	else {
		options.separator = Object.assign( {} , this.defaultOptions.separator , options.separator ) ;
	}

	ColumnMenu.call( this , options ) ;

	this.showMenu = false ;
	this.zIndexRef = this.zIndex ;	// Back-up for zIndex

	if ( options.value !== undefined && this.setValue( options.value , true ) ) {
		// .initPage() is called by .setValue() only when a correct value was found
		this.toggle( this.showMenu , options.noDraw ) ;
	}
	else {
		this.initPage() ;
		this.toggle( this.showMenu , options.noDraw ) ;
	}


	this.onClickOut = this.onClickOut.bind( this ) ;

	this.on( 'clickOut' , this.onClickOut ) ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'SelectList' && ! options.noDraw ) { this.draw() ; }
}

module.exports = SelectList ;
Element.inherit( SelectList , ColumnMenu ) ;



SelectList.prototype.defaultOptions = {
	buttonBlurAttr: { bgColor: 'gray' , color: 'white' , bold: true } ,
	buttonFocusAttr: { bgColor: 'white' , color: 'black' , bold: true } ,
	buttonDisabledAttr: {
		bgColor: 'gray' , color: 'white' , bold: true , dim: true
	} ,
	buttonSubmittedAttr: { bgColor: 'gray' , color: 'brightWhite' , bold: true } ,
	turnedOnBlurAttr: { bgColor: 'cyan' } ,
	turnedOnFocusAttr: { bgColor: 'brightCyan' , color: 'gray' , bold: true } ,
	turnedOffBlurAttr: { bgColor: 'gray' , dim: true } ,
	turnedOffFocusAttr: { bgColor: 'white' , color: 'black' , bold: true } ,

	master: {
		content: 'select-list' ,
		symbol: '▼' ,
		internalRole: 'toggle'
	} ,
	separator: {
		content: '-' ,
		contentRepeat: true ,
		internalRole: 'separator'
	}
} ;



SelectList.prototype.toggle = function( showMenu = null , noDraw = false ) {
	var i , iMax ;

	if ( showMenu === null ) { this.showMenu = ! this.showMenu ; }
	else { this.showMenu = !! showMenu ; }

	for ( i = 1 , iMax = this.buttons.length ; i < iMax ; i ++ ) {
		this.buttons[ i ].hidden = ! this.showMenu ;
	}

	// Adjust outputHeight, to avoid the list to be clickable when reduced
	this.outputHeight = this.showMenu ? this.pageHeight : 1 ;

	if ( this.showMenu ) { this.topZ() ; }
	else { this.restoreZ() ; }

	// Return now if noDraw is set, bypassing both drawing and focus
	if ( noDraw ) { return ; }

	if ( showMenu ) {
		for ( i = 1 , iMax = this.buttons.length ; i < iMax ; i ++ ) {
			if ( this.buttons[ i ].value === this.value ) {
				this.document.giveFocusTo( this.buttons[ i ] ) ;
				break ;
			}
		}
	}
	else {
		this.document.giveFocusTo( this.buttons[ 0 ] ) ;
	}

	this.outerDraw() ;
} ;



// selectOnly: don't draw, don't toggle
SelectList.prototype.select = function( button , selectOnly ) {
	// /!\ Warning! button can be a button (called from .onButtonSubmit()) or a buttonDef (called from .setValue()) /!\
	// This is nasty and should be fixed!
	// Ideally, a button should retain a 'def' pointer
	var width ,
		extraWidth = 1 + this.buttonSymbolWidth ,
		buttonContent = button.content ;

	if ( Array.isArray( buttonContent ) ) { buttonContent = buttonContent[ 0 ] || '' ; }

	width = Element.computeContentWidth( buttonContent , button.contentHasMarkup ) + this.buttonPaddingWidth + this.buttonSymbolWidth ;

	if ( width > this.buttonsMaxWidth ) {
		// This is the normal case here...
		this.masterDef.buttonContent =
			Element.truncateContent( buttonContent , this.buttonsMaxWidth - this.buttonSymbolWidth , button.contentHasMarkup )
			+ ' ' + this.masterDef.symbol ;
	}
	else if ( width < this.buttonsMaxWidth ) {
		this.masterDef.buttonContent = buttonContent + ' '.repeat( this.buttonsMaxWidth - width ) + ' ' + this.masterDef.symbol ;
	}
	else {
		this.masterDef.buttonContent = buttonContent + ' ' + this.masterDef.symbol ;
	}

	this.masterDef.contentHasMarkup = button.contentHasMarkup ;
	this.masterDef.width = this.buttonsMaxWidth ;
	this.value = this.masterDef.value = button.value ;

	// Only when it's a buttonDef ATM:
	if ( button.page !== undefined ) { this.page = button.page ; }

	this.initPage() ;

	if ( selectOnly ) { return ; }

	this.toggle( false ) ; // , noDraw ) ;
} ;



SelectList.prototype.onButtonSubmit = function( buttonValue , action , button ) {
	switch ( button.internalRole ) {
		case 'previousPage' :
			this.previousPage() ;
			break ;
		case 'nextPage' :
			this.nextPage() ;
			break ;
		case 'toggle' :
			this.toggle() ;
			break ;
		default :
			this.select( button ) ;
			this.emit( 'submit' , buttonValue , action , this , button ) ;
	}
} ;



// .setValue() will also select the first matching item
SelectList.prototype.setValue = function( value , noDraw ) {
	var buttonDef = this.itemsDef.find( b => b.value === value ) ;
	if ( ! buttonDef ) { return false ; }
	this.select( buttonDef , noDraw ) ;
	return true ;
} ;



SelectList.prototype.onClickOut = function() { this.toggle( false ) ; } ;
SelectList.prototype.getValue = function() { return this.value ; } ;


},{"./BaseMenu.js":15,"./Button.js":17,"./ColumnMenu.js":18,"./Element.js":25}],35:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const BaseMenu = require( './BaseMenu.js' ) ;
const ColumnMenuMulti = require( './ColumnMenuMulti.js' ) ;
const Button = require( './Button.js' ) ;



// Inherit from ColumnMenuMulti for common methods

function SelectListMulti( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	if ( ! options.master || typeof options.master !== 'object' ) {
		options.master = Object.assign( {} , this.defaultOptions.master ) ;
	}
	else {
		options.master = Object.assign( {} , this.defaultOptions.master , options.master ) ;
	}

	if ( options.content ) {
		options.master.content = options.content ;
	}

	if ( ! options.separator || typeof options.separator !== 'object' ) {
		options.separator = Object.assign( {} , this.defaultOptions.separator ) ;
	}
	else {
		options.separator = Object.assign( {} , this.defaultOptions.separator , options.separator ) ;
	}

	ColumnMenuMulti.call( this , options ) ;

	this.showMenu = false ;
	this.zIndexRef = this.zIndex ;	// Back-up for zIndex

	this.initPage() ;
	this.toggle( this.showMenu , options.noDraw ) ;

	this.onClickOut = this.onClickOut.bind( this ) ;

	this.on( 'clickOut' , this.onClickOut ) ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'SelectListMulti' && ! options.noDraw ) { this.draw() ; }
}

module.exports = SelectListMulti ;
Element.inherit( SelectListMulti , ColumnMenuMulti ) ;



SelectListMulti.prototype.defaultOptions = {
	buttonBlurAttr: { bgColor: 'gray' , color: 'white' , bold: true } ,
	buttonFocusAttr: { bgColor: 'white' , color: 'black' , bold: true } ,
	buttonDisabledAttr: {
		bgColor: 'gray' , color: 'white' , bold: true , dim: true
	} ,
	buttonSubmittedAttr: { bgColor: 'gray' , color: 'brightWhite' , bold: true } ,
	turnedOnBlurAttr: { bgColor: 'cyan' } ,
	turnedOnFocusAttr: { bgColor: 'brightCyan' , color: 'gray' , bold: true } ,
	turnedOffBlurAttr: { bgColor: 'gray' , dim: true } ,
	turnedOffFocusAttr: { bgColor: 'white' , color: 'black' , bold: true } ,

	master: {
		content: 'select-list-multi' ,
		symbol: '▼' ,
		internalRole: 'toggle'
	} ,
	separator: {
		content: '-' ,
		contentRepeat: true ,
		internalRole: 'separator'
	}
} ;



SelectListMulti.prototype.toggle = function( showMenu = null , noDraw = false ) {
	var i , iMax ;

	if ( showMenu === null ) { this.showMenu = ! this.showMenu ; }
	else { this.showMenu = !! showMenu ; }

	for ( i = 1 , iMax = this.buttons.length ; i < iMax ; i ++ ) {
		this.buttons[ i ].hidden = ! this.showMenu ;
	}

	// Adjust outputHeight, to avoid the list to be clickable when reduced
	this.outputHeight = this.showMenu ? this.pageHeight : 1 ;

	if ( this.showMenu ) { this.topZ() ; }
	else { this.restoreZ() ; }

	// Return now if noDraw is set, bypassing both drawing and focus
	if ( noDraw ) { return ; }

	this.document.giveFocusTo( this.buttons[ 0 ] ) ;

	this.outerDraw() ;
} ;



SelectListMulti.prototype.onButtonSubmit = function( buttonValue , action , button ) {
	switch ( button.internalRole ) {
		case 'previousPage' :
			this.previousPage() ;
			break ;
		case 'nextPage' :
			this.nextPage() ;
			break ;
		case 'toggle' :
			this.toggle() ;

			// Submit when reducing
			if ( ! this.showMenu ) {
				this.emit( 'submit' , this.value , action , this , button ) ;
			}
			break ;
		default :
			this.emit( 'submit' , this.value , action , this , button ) ;
	}
} ;



SelectListMulti.prototype.onClickOut = function() {
	if ( this.showMenu ) {
		this.toggle( false ) ;
		// We also submit when the menu is closed on click out
		this.emit( 'submit' , this.value , undefined , this ) ;
	}
} ;

SelectListMulti.prototype.getValue = function() { return this.value ; } ;


},{"./BaseMenu.js":15,"./Button.js":17,"./ColumnMenuMulti.js":20,"./Element.js":25}],36:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const Button = require( './Button.js' ) ;

// Default transfer function
const IDENTITY = v => v ;



function Slider( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	Element.call( this , options ) ;

	this.onClick = this.onClick.bind( this ) ;
	this.onDrag = this.onDrag.bind( this ) ;
	this.onWheel = this.onWheel.bind( this ) ;
	this.onButtonSubmit = this.onButtonSubmit.bind( this ) ;

	this.isVertical = !! options.isVertical ;
	this.slideRate = 0 ;
	this.handleOffset = 0 ;

	this.rateToValue = typeof options.rateToValue === 'function' ? options.rateToValue : IDENTITY ;
	this.valueToRate = typeof options.valueToRate === 'function' ? options.valueToRate : IDENTITY ;

	this.buttonBlurAttr = options.buttonBlurAttr || { bgColor: 'black' , color: 'white' , bold: true } ;
	this.buttonFocusAttr = options.buttonFocusAttr || { bgColor: 'white' , color: 'black' , bold: true } ;
	this.buttonSubmittedAttr = options.buttonSubmittedAttr || { bgColor: 'gray' , color: 'brightWhite' , bold: true } ;

	this.backwardSymbol = options.backwardSymbol || ( this.isVertical ? '▲' : '◀' ) ;
	this.forwardSymbol = options.forwardSymbol || ( this.isVertical ? '▼' : '▶' ) ;

	this.handleAttr = options.handleAttr || { bgColor: 'brightWhite' , color: 'black' } ;
	this.handleSymbol = options.handleSymbol || '◆' ;

	this.barAttr = options.barAttr || { bgColor: 'gray' , color: 'brightWhite' } ;
	this.barSymbol = options.barSymbol || ' ' ;

	this.backwardButton = this.forwardButton = null ;

	this.on( 'click' , this.onClick ) ;
	this.on( 'drag' , this.onDrag ) ;
	this.on( 'wheel' , this.onWheel ) ;

	this.initChildren() ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'Slider' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Slider ;
Element.inherit( Slider ) ;



Slider.prototype.needInput = true ;
Slider.prototype.outerDrag = true ;



// Unused ATM: no onKey registered
Slider.prototype.keyBindings = {
	UP: 'backward' ,
	DOWN: 'forward' ,
	LEFT: 'backward' ,
	RIGHT: 'forward' ,
	PAGE_UP: 'backward' ,
	PAGE_DOWN: 'forward' ,
	' ': 'forward' ,
	HOME: 'start' ,
	END: 'end'
} ;



Slider.prototype.buttonKeyBindings = {
	ENTER: 'submit' ,
	KP_ENTER: 'submit'
} ;



// Create Buttons automatically
Slider.prototype.initChildren = function() {
	this.backwardButton = new Button( {
		internal: true ,
		parent: this ,
		internalRole: 'backward' ,
		content: this.backwardSymbol ,
		outputX: this.outputX ,
		outputY: this.outputY ,
		blurAttr: this.buttonBlurAttr ,
		focusAttr: this.buttonFocusAttr ,
		//disabledAttr: this.buttonDisabledAttr ,
		submittedAttr: this.buttonSubmittedAttr ,
		keyBindings: this.buttonKeyBindings ,
		//shortcuts: def.shortcuts ,
		noDraw: true
	} ) ;

	this.backwardButton.on( 'submit' , this.onButtonSubmit ) ;

	this.forwardButton = new Button( {
		internal: true ,
		parent: this ,
		internalRole: 'forward' ,
		content: this.forwardSymbol ,
		outputX: this.isVertical ? this.outputX : this.outputX + this.outputWidth - 1 ,
		outputY: this.isVertical ? this.outputY + this.outputHeight - 1 : this.outputY ,
		blurAttr: this.buttonBlurAttr ,
		focusAttr: this.buttonFocusAttr ,
		//disabledAttr: this.buttonDisabledAttr ,
		submittedAttr: this.buttonSubmittedAttr ,
		keyBindings: this.buttonKeyBindings ,
		//shortcuts: def.shortcuts ,
		noDraw: true
	} ) ;

	this.forwardButton.on( 'submit' , this.onButtonSubmit ) ;

	this.computeHandleOffset() ;
} ;



Slider.prototype.setSizeAndPosition = function( options ) {
	this.outputX =
		options.outputX !== undefined ? options.outputX :
		options.x !== undefined ? options.x :
		this.outputX || 0 ;
	this.outputY =
		options.outputY !== undefined ? options.outputY :
		options.y !== undefined ? options.y :
		this.outputY || 0 ;
	this.outputWidth =
		options.outputWidth !== undefined ? options.outputWidth :
		options.width !== undefined ? options.width :
		this.outputWidth || 1 ;
	this.outputHeight =
		options.outputHeight !== undefined ? options.outputHeight :
		options.height !== undefined ? options.height :
		this.outputHeight || 1 ;

	this.backwardButton.outputX = this.outputX ;
	this.backwardButton.outputY = this.outputY ;
	this.forwardButton.outputX = this.isVertical ? this.outputX : this.outputX + this.outputWidth - 1 ;
	this.forwardButton.outputY = this.isVertical ? this.outputY + this.outputHeight - 1 : this.outputY ;
} ;



Slider.prototype.preDrawSelf = function() {
	return this.isVertical ? this.preDrawSelfVertical() : this.preDrawSelfHorizontal() ;
} ;



Slider.prototype.preDrawSelfVertical = function() {
	var offset = 0 ,
		y = this.outputY + 1 ,
		yMax = this.outputY + this.outputHeight - 2 ;

	for ( ; y <= yMax ; y ++ , offset ++ ) {
		if ( offset === this.handleOffset ) {
			this.outputDst.put( { x: this.outputX , y , attr: this.handleAttr } , this.handleSymbol ) ;
		}
		else {
			this.outputDst.put( { x: this.outputX , y , attr: this.barAttr } , this.barSymbol ) ;
		}
	}
} ;



Slider.prototype.preDrawSelfHorizontal = function() {
	var offset = 0 ,
		x = this.outputX + 1 ,
		xMax = this.outputX + this.outputWidth - 2 ;

	for ( ; x <= xMax ; x ++ , offset ++ ) {
		if ( offset === this.handleOffset ) {
			this.outputDst.put( { x , y: this.outputY , attr: this.handleAttr } , this.handleSymbol ) ;
		}
		else {
			this.outputDst.put( { x , y: this.outputY , attr: this.barAttr } , this.barSymbol ) ;
		}
	}
} ;



// No need to draw the cursor, however, when drawing, it is necessary to move it (not draw) at the handle position,
// in case no other widget would draw the cursor, it avoid the cursor to be hanging at the bottom and one cell off
// to the right of the slider, which is pretty annoying...
Slider.prototype.postDrawSelf = function() {
	if ( this.isVertical ) {
		this.outputDst.moveTo( this.outputX , this.outputY + this.handleOffset + 1 ) ;
	}
	else {
		this.outputDst.moveTo( this.outputX + this.handleOffset + 1 , this.outputY ) ;
	}
} ;

/*
Slider.prototype.drawSelfCursor = function() {
	// Move the cursor back to the handle
	this.outputDst.moveTo( this.outputX , this.outputY + this.handleOffset + 1 ) ;
	this.outputDst.drawCursor() ;
} ;
*/



// Compute the handle y position from the slideRate value
Slider.prototype.computeHandleOffset = function() {
	var delta = ( this.isVertical ? this.outputHeight : this.outputWidth ) - 3 ;	// minus the two buttons
	this.handleOffset = Math.round( delta * this.slideRate ) ;
} ;



// Set the handle position and compute the slideRate
Slider.prototype.setHandleOffset = function( offset , internalAndNoDraw = false ) {
	var delta = ( this.isVertical ? this.outputHeight : this.outputWidth ) - 3 ;	// minus the two buttons

	this.handleOffset = Math.max( 0 , Math.min( delta , Math.round( offset || 0 ) ) ) ;
	this.slideRate = Math.max( 0 , Math.min( 1 , this.handleOffset / delta || 0 ) ) ;

	if ( ! internalAndNoDraw ) {
		this.emit( 'slide' , this.getValue() ) ;
		this.draw() ;
	}
} ;



Slider.prototype.setSlideRate = function( rate , internalAndNoDraw = false ) {
	this.slideRate = Math.max( 0 , Math.min( 1 , rate || 0 ) ) ;
	this.computeHandleOffset() ;

	if ( ! internalAndNoDraw ) {
		this.emit( 'slide' , this.getValue() ) ;
		this.draw() ;
	}
} ;



Slider.prototype.getValue = function() {
	return this.rateToValue( this.slideRate ) ;
} ;



Slider.prototype.setValue = function( value , internalAndNoDraw ) {
	return this.setSlideRate( this.valueToRate( value ) , internalAndNoDraw ) ;
} ;



Slider.prototype.getHandleOffset = function() { return this.handleOffset ; } ;
Slider.prototype.getSlideRate = function() { return this.slideRate ; } ;



Slider.prototype.onButtonSubmit = function( buttonValue , action , button ) {
	switch ( button.internalRole ) {
		case 'backward' :
			this.emit( 'slideStep' , -1 ) ;
			break ;
		case 'forward' :
			this.emit( 'slideStep' , 1 ) ;
			break ;
	}
} ;



Slider.prototype.onClick = function( data ) {
	if ( ! this.hasFocus ) { this.document.giveFocusTo( this , 'select' ) ; }
	this.setHandleOffset( ( this.isVertical ? data.y : data.x ) - 1 ) ;
} ;



Slider.prototype.onDrag = function( data ) {
	this.setHandleOffset( ( this.isVertical ? data.y : data.x ) - 1 ) ;
} ;



Slider.prototype.onWheel = function( data ) {
	this.emit( 'slideStep' , data.yDirection ) ;
} ;


},{"./Button.js":17,"./Element.js":25}],37:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;



function Text( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	this.attr = options.attr || { bgColor: 'brightBlack' } ;

	this.leftPadding = options.leftPadding || '' ;
	this.rightPadding = options.rightPadding || '' ;
	this.paddingHasMarkup = !! options.paddingHasMarkup ;
	this.leftPaddingWidth = 0 ;
	this.rightPaddingWidth = 0 ;

	if ( ! Array.isArray( options.content ) ) { options.content = [ options.content || '' ] ; }

	// Usually done by the Element's constructor, but it's required now
	// Also check that sub-class hasn't defined it yet...
	this.content = '' ;	// set by .setContent()
	this.contentHasMarkup = options.contentHasMarkup || false ;	// can be true/false or 'ansi' or 'legacyAnsi'
	this.contentEllipsis = options.contentEllipsis === true ? '…' : options.contentEllipsis || '' ;

	this.contentAdaptativeWidth = options.contentAdaptativeWidth ?? ! options.width ;
	this.contentAdaptativeHeight = options.contentAdaptativeHeight ?? ! options.height ;

	this.innerWidth = 0 ;	// content width + padding width

	// We need to compute that now
	if ( this.setContent === Text.prototype.setContent ) {
		this.setContent( options.content , this.contentHasMarkup , true , true ) ;
	}

	// Forced or adaptative windth/height?
	if ( ! options.width ) {
		options.width = this.innerWidth ;
		options.contentAdaptativeWidth = true ;
	}

	if ( ! options.height ) {
		options.height = this.contentHeight ;
		options.contentAdaptativeHeight = true ;
	}

	Element.call( this , options ) ;

	if ( this.elementType === 'Text' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Text ;
Element.inherit( Text ) ;



Text.prototype.forceContentArray = true ;



Text.prototype.setContent = function( content , hasMarkup , dontDraw = false , dontResize = false ) {
	if ( this.forceContentArray && ! Array.isArray( content ) ) { content = [ content || '' ] ; }

	var oldOutputWidth = this.outputWidth ,
		oldOutputHeight = this.outputHeight ;

	this.content = content ;
	this.contentHasMarkup = hasMarkup ;

	this.computeRequiredWidth() ;
	this.computeRequiredHeight() ;

	if ( ! dontResize && this.resizeOnContent ) { this.resizeOnContent() ; }

	if ( ! dontDraw ) {
		if ( this.outputWidth < oldOutputWidth || this.outputHeight < oldOutputHeight ) {
			this.outerDraw() ;
		}
		else {
			this.draw() ;
		}
	}
} ;



Text.prototype.computeRequiredWidth = function() {
	this.leftPaddingWidth = Element.computeContentWidth( this.leftPadding , this.paddingHasMarkup ) ;
	this.rightPaddingWidth = Element.computeContentWidth( this.rightPadding , this.paddingHasMarkup ) ;
	this.contentWidth =
		this.animation ? Math.max( ... this.animation.map( e => Element.computeContentWidth( e , this.contentHasMarkup ) ) ) :
		Element.computeContentWidth( this.content , this.contentHasMarkup ) || 1 ;
	this.innerWidth = this.leftPaddingWidth + this.rightPaddingWidth + this.contentWidth ;

	return this.innerWidth ;
} ;



Text.prototype.computeRequiredHeight = function() {
	this.contentHeight =
		this.animation ? Math.max( ... this.animation.map( e => e.length ) ) :
		this.content.length ;

	return this.contentHeight ;
} ;



Text.prototype.resizeOnContent = function() {
	if ( this.contentAdaptativeWidth ) {
		this.outputWidth = this.innerWidth ;
	}

	if ( this.contentAdaptativeHeight ) {
		this.outputHeight = this.contentHeight ;
	}
} ;



Text.prototype.postDrawSelf = function() {
	if ( ! this.outputDst ) { return this ; }

	var resumeAttr ;

	// This is the gap caused by an output width larger than its actual content + padding
	var outputWidthGap = this.outputWidth - ( this.leftPaddingWidth + this.rightPaddingWidth + this.contentWidth ) ;

	var contentClip = {
		x: this.outputX + this.leftPaddingWidth ,
		y: this.outputY ,
		// NOT contentWidth, it could exceed the actual outputWidth
		width: this.outputWidth - this.leftPaddingWidth - this.rightPaddingWidth ,
		height: this.outputHeight
	} ;

	var elementClip = {
		x: this.outputX ,
		y: this.outputY ,
		width: this.outputWidth ,
		height: this.outputHeight
	} ;

	for ( let lineNumber = 0 ; lineNumber < this.outputHeight ; lineNumber ++ ) {
		let contentLine = this.content[ lineNumber ] || '' ;

		// Write the left padding
		if ( this.leftPadding ) {
			this.outputDst.put(
				{
					x: this.outputX ,
					y: this.outputY + lineNumber ,
					attr: this.attr ,
					markup: this.paddingHasMarkup ,
					clip: elementClip
				} ,
				this.leftPadding
			) ;

			let leftPaddingGap = this.outputX + this.leftPaddingWidth - this.outputDst.cx ;
			if ( leftPaddingGap > 0 ) {
				this.outputDst.put( { attr: this.attr } , ' '.repeat( leftPaddingGap ) ) ;
			}
		}

		// Write the content
		resumeAttr = this.outputDst.put(
			{
				x: this.outputX + this.leftPaddingWidth ,
				y: this.outputY + lineNumber ,
				attr: this.attr ,
				resumeAttr: resumeAttr ,
				markup: this.contentHasMarkup ,
				clip: contentClip ,
				clipChar: this.contentEllipsis
			} ,
			contentLine
		) ;

		// We add the output width gap to the content gap
		let contentGap = this.outputX + this.leftPaddingWidth + this.contentWidth + outputWidthGap - this.outputDst.cx ;
		if ( contentGap > 0 ) {
			this.outputDst.put( { attr: this.attr } , ' '.repeat( contentGap ) ) ;
		}

		// Write the right padding
		if ( this.rightPadding ) {
			this.outputDst.put(
				{
					x: this.outputX + this.outputWidth - this.rightPaddingWidth ,
					y: this.outputY + lineNumber ,
					attr: this.attr ,
					markup: this.paddingHasMarkup ,
					clip: elementClip
				} ,
				this.rightPadding
			) ;

			let rightPaddingGap = this.outputX + this.leftPaddingWidth + this.contentWidth + outputWidthGap + this.rightPaddingWidth - this.outputDst.cx ;
			if ( rightPaddingGap > 0 ) {
				this.outputDst.put( { attr: this.attr } , ' '.repeat( rightPaddingGap ) ) ;
			}
		}
	}
} ;


},{"./Element.js":25}],38:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const Slider = require( './Slider.js' ) ;

const ScreenBuffer = require( '../ScreenBuffer.js' ) ;
const TextBuffer = require( '../TextBuffer.js' ) ;
const Rect = require( '../Rect.js' ) ;

const string = require( 'string-kit' ) ;



function TextBox( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	Element.call( this , options ) ;

	this.onClick = this.onClick.bind( this ) ;
	this.onDrag = this.onDrag.bind( this ) ;
	this.onWheel = this.onWheel.bind( this ) ;
	this.onParentResize = this.onParentResize.bind( this ) ;

	if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; }

	this.textAttr = options.textAttr || options.attr || { bgColor: 'default' } ;
	this.altTextAttr = options.altTextAttr || Object.assign( {} , this.textAttr , { color: 'gray' , italic: true } ) ;
	this.voidAttr = options.voidAttr || options.emptyAttr || options.attr || { bgColor: 'default' } ;

	this.scrollable = !! options.scrollable ;
	this.hasVScrollBar = this.scrollable && !! options.vScrollBar ;
	this.hasHScrollBar = this.scrollable && !! options.hScrollBar ;
	this.scrollX = options.scrollX || 0 ;
	this.scrollY = options.scrollY || 0 ;

	// false: scroll down to the bottom of the content, both content bottom and textBox bottom on the same cell
	// true: scroll down until the bottom of the content reaches the top of the textBox
	this.extraScrolling = !! options.extraScrolling ;

	this.autoScrollContextLines = options.autoScrollContextLines ?? 0 ;
	this.autoScrollContextColumns = options.autoScrollContextColumns ?? 1 ;

	// Right shift of the first-line, may be useful for prompt, or continuing another box in the flow
	this.firstLineRightShift = options.firstLineRightShift || 0 ;

	this.tabWidth = options.tabWidth || 4 ;		// How many cells (=spaces) for the tab character

	this.wordWrap = !! ( options.wordWrap || options.wordwrap ) ;
	this.lineWrap = !! ( options.lineWrap || this.wordWrap ) ;

	this.hiddenContent = options.hiddenContent ;

	this.stateMachine = options.stateMachine ;

	this.textAreaWidth = this.hasVScrollBar ? this.outputWidth - 1 : this.outputWidth ;
	this.textAreaHeight = this.hasHScrollBar ? this.outputHeight - 1 : this.outputHeight ;

	this.textBuffer = null ;
	this.altTextBuffer = null ;
	this.vScrollBarSlider = null ;
	this.hScrollBarSlider = null ;

	this.on( 'key' , this.onKey ) ;
	this.on( 'click' , this.onClick ) ;
	this.on( 'drag' , this.onDrag ) ;
	this.on( 'wheel' , this.onWheel ) ;
	this.on( 'parentResize' , this.onParentResize ) ;

	this.initChildren() ;

	if ( this.setContent === TextBox.prototype.setContent ) {
		this.setContent( options.content , options.contentHasMarkup , true ) ;
	}

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'TextBox' && ! options.noDraw ) { this.draw() ; }
}

module.exports = TextBox ;
Element.inherit( TextBox ) ;



// Support for strictInline mode
TextBox.prototype.strictInlineSupport = true ;



TextBox.prototype.keyBindings = {
	CTRL_K: 'meta' ,
	UP: 'tinyScrollUp' ,
	DOWN: 'tinyScrollDown' ,
	PAGE_UP: 'scrollUp' ,
	PAGE_DOWN: 'scrollDown' ,
	' ': 'scrollDown' ,
	HOME: 'scrollTop' ,
	END: 'scrollBottom' ,
	LEFT: 'scrollLeft' ,
	RIGHT: 'scrollRight' ,

	// T for Transfer
	ALT_T: 'copyToDocumentClipboard' ,
	META_T: 'copyToSystemClipboard'
} ;



TextBox.prototype.initChildren = function() {
	this.textBuffer = new TextBuffer( {
		dst: this.outputDst ,
		//palette: this.document.palette ,
		x: this.outputX ,
		y: this.outputY ,
		//width: this.textAreaWidth ,
		//height: this.textAreaHeight
		firstLineRightShift: this.firstLineRightShift ,
		tabWidth: this.tabWidth ,
		lineWrapWidth: this.lineWrap ? this.textAreaWidth : null ,
		wordWrap: this.wordWrap ,
		dstClipRect: {
			x: this.outputX ,
			y: this.outputY ,
			width: this.textAreaWidth ,
			height: this.textAreaHeight
		} ,
		hidden: this.hiddenContent ,
		forceInBound: true ,
		stateMachine: this.stateMachine
	} ) ;

	this.setAttr( undefined , undefined , true ) ;


	if ( this.useAltTextBuffer ) {
		this.altTextBuffer = new TextBuffer( {
			firstLineRightShift: this.firstLineRightShift ,
			tabWidth: this.tabWidth ,
			lineWrapWidth: this.lineWrap ? this.textAreaWidth : null ,
			wordWrap: this.wordWrap ,
			dstClipRect: {
				x: this.outputX ,
				y: this.outputY ,
				width: this.textAreaWidth ,
				height: this.textAreaHeight
			}
			//, stateMachine: this.stateMachine
		} ) ;

		this.setAltAttr() ;
		this.textBuffer.setVoidTextBuffer( this.altTextBuffer ) ;
	}


	if ( this.hasVScrollBar ) {
		this.vScrollBarSlider = new Slider( {
			internal: true ,
			parent: this ,
			x: this.outputX + this.outputWidth - 1 ,
			y: this.outputY ,
			height: this.outputHeight ,
			isVertical: true ,
			valueToRate: scrollY => -scrollY / Math.max( 1 , this.textBuffer.buffer.length - this.textAreaHeight ) ,
			rateToValue: rate => -rate * Math.max( 1 , this.textBuffer.buffer.length - this.textAreaHeight ) ,
			noDraw: true
		} ) ;

		this.vScrollBarSlider.on( 'slideStep' , d => this.scroll( 0 , -d * Math.ceil( this.textAreaHeight / 2 ) ) ) ;
		this.vScrollBarSlider.on( 'slide' , value => this.scrollTo( null , value ) ) ;
	}

	if ( this.hasHScrollBar ) {
		this.hScrollBarSlider = new Slider( {
			internal: true ,
			parent: this ,
			x: this.outputX ,
			y: this.outputY + this.outputHeight - 1 ,
			width: this.outputWidth - this.hasVScrollBar ,
			valueToRate: scrollX => {
				var lineWidth = this.textBuffer.getContentSize().width ;
				return -scrollX / Math.max( 1 , lineWidth - this.textAreaWidth ) ;
			} ,
			rateToValue: rate => {
				var lineWidth = this.textBuffer.getContentSize().width ;
				return -rate * Math.max( 1 , lineWidth - this.textAreaWidth ) ;
			} ,
			noDraw: true
		} ) ;

		this.hScrollBarSlider.on( 'slideStep' , d => this.scroll( -d * Math.ceil( this.textAreaWidth / 2 ) , 0 ) ) ;
		this.hScrollBarSlider.on( 'slide' , value => this.scrollTo( value , null ) ) ;
	}
} ;



TextBox.prototype.setSizeAndPosition = function( options ) {
	this.outputX =
		options.outputX !== undefined ? options.outputX :
		options.x !== undefined ? options.x :
		this.outputX || 0 ;
	this.outputY =
		options.outputY !== undefined ? options.outputY :
		options.y !== undefined ? options.y :
		this.outputY || 0 ;
	this.outputWidth =
		options.outputWidth !== undefined ? options.outputWidth :
		options.width !== undefined ? options.width :
		this.outputWidth || 1 ;
	this.outputHeight =
		options.outputHeight !== undefined ? options.outputHeight :
		options.height !== undefined ? options.height :
		this.outputHeight || 1 ;

	this.textAreaWidth = this.hasVScrollBar ? this.outputWidth - 1 : this.outputWidth ;
	this.textAreaHeight = this.hasHScrollBar ? this.outputHeight - 1 : this.outputHeight ;

	this.textBuffer.lineWrapWidth = this.lineWrap ? this.textAreaWidth : null ;
	if ( this.altTextBuffer ) { this.altTextBuffer.lineWrapWidth = this.lineWrap ? this.textAreaWidth : null ; }

	this.textBuffer.x = this.outputX ;
	this.textBuffer.y = this.outputY ;

	this.textBuffer.dstClipRect = new Rect( {
		x: this.outputX ,
		y: this.outputY ,
		width: this.textAreaWidth ,
		height: this.textAreaHeight
	} ) ;

	// Update word-wrap
	if ( this.lineWrap ) {
		this.textBuffer.wrapAllLines() ;
		if ( this.altTextBuffer ) { this.altTextBuffer.wrapAllLines() ; }
	}

	if ( this.vScrollBarSlider ) {
		this.vScrollBarSlider.setSizeAndPosition( {
			outputX: this.outputX + this.outputWidth - 1 ,
			outputY: this.outputY ,
			outputHeight: this.outputHeight
		} ) ;
	}

	if ( this.hScrollBarSlider ) {
		this.hScrollBarSlider.setSizeAndPosition( {
			outputX: this.outputX ,
			outputY: this.outputY + this.outputHeight - 1 ,
			outputWidth: this.hasVScrollBar ? this.outputWidth - 1 : this.outputWidth
		} ) ;
	}
} ;



TextBox.prototype.preDrawSelf = function() {
	// It's best to force the dst now, because it avoids to set textBuffer.dst everytime it changes,
	// and it could be changed by userland (so it's hard to keep it in sync without setters/getters)
	this.textBuffer.draw( { dst: this.outputDst } ) ;
} ;



TextBox.prototype.scrollTo = function( x , y , noDraw = false ) {
	if ( ! this.scrollable ) { return ; }

	if ( x !== undefined && x !== null ) {
		// Got a +1 after content size because of the word-wrap thing and eventual invisible \n
		this.scrollX = Math.min( 0 , Math.max( Math.round( x ) ,
			( this.extraScrolling ? 1 : this.textAreaWidth ) - this.textBuffer.getContentSize().width + 1
		) ) ;

		this.textBuffer.x = this.outputX + this.scrollX ;
	}

	if ( y !== undefined && y !== null ) {
		this.scrollY = Math.min( 0 , Math.max( Math.round( y ) ,
			( this.extraScrolling ? 1 : this.textAreaHeight ) - this.textBuffer.buffer.length
		) ) ;

		this.textBuffer.y = this.outputY + this.scrollY ;
	}

	if ( this.vScrollBarSlider ) {
		this.vScrollBarSlider.setValue( this.scrollY , true ) ;
	}

	if ( this.hScrollBarSlider ) {
		this.hScrollBarSlider.setValue( this.scrollX , true ) ;
	}

	if ( ! noDraw ) { this.draw() ; }
} ;

TextBox.prototype.scroll = function( dx , dy , dontDraw = false ) {
	return this.scrollTo(  dx ? this.scrollX + dx : null  ,  dy ? this.scrollY + dy : null  ,  dontDraw  ) ;
} ;

TextBox.prototype.scrollToTop = function( dontDraw = false ) {
	return this.scrollTo( null , 0 , dontDraw ) ;
} ;

TextBox.prototype.scrollToBottom = function( dontDraw = false ) {
	// Ignore extra scrolling here
	return this.scrollTo( null , this.textAreaHeight - this.textBuffer.buffer.length , dontDraw ) ;
} ;



TextBox.prototype.autoScrollAndDraw = function( onlyDrawCursorExceptIfScrolled = false , noDraw = false ) {
	var x , y ,
		contextColumns = Math.min( Math.floor( this.textAreaWidth / 2 ) , this.autoScrollContextColumns ) ,
		contextLines = Math.min( Math.floor( this.textAreaHeight / 2 ) , this.autoScrollContextLines ) ;

	// Do nothing if there is no scrolling yet (do not set x to 0 if it's unnecessary)
	if ( this.textBuffer.cx < -this.scrollX + contextColumns && this.scrollX !== 0 ) {
		// The cursor will be on left of the viewport
		x = Math.min( 0 , -this.textBuffer.cx + contextColumns ) ;
	}
	else if ( this.textBuffer.cx > this.textAreaWidth - this.scrollX - 1 - contextColumns ) {
		// The cursor will be on right of the viewport
		x = this.textAreaWidth - 1 - this.textBuffer.cx - contextColumns ;
	}

	if ( this.textBuffer.cy < -this.scrollY + contextLines ) {
		// The cursor will be on top of the viewport
		y = Math.min( 0 , -this.textBuffer.cy + contextLines ) ;
	}
	else if ( this.textBuffer.cy > this.textAreaHeight - this.scrollY - 1 - contextLines ) {
		// The cursor will be at the bottom of the viewport
		y = this.textAreaHeight - 1 - this.textBuffer.cy - contextLines ;
	}

	if ( x !== undefined || y !== undefined ) {
		// .scrollTo() call .draw(), so no need to do that here...
		this.scrollTo( x , y , noDraw ) ;
	}
	else if ( ! onlyDrawCursorExceptIfScrolled ) {
		this.draw() ;
	}
	else {
		this.drawCursor() ;
	}
} ;

TextBox.prototype.autoScrollAndSmartDraw = function() { return this.autoScrollAndDraw( true ) ; } ;



TextBox.prototype.setAttr = function( textAttr = this.textAttr , voidAttr = this.voidAttr , dontDraw = false , dontSetContent = false ) {
	this.textAttr = textAttr ;
	this.voidAttr = voidAttr ;
	this.textBuffer.setDefaultAttr( this.textAttr ) ;
	this.textBuffer.setVoidAttr( this.voidAttr ) ;

	if ( ! dontSetContent ) { this.setContent( this.content , this.contentHasMarkup , dontDraw ) ; }
} ;



TextBox.prototype.setAltAttr = function( altTextAttr = this.altTextAttr ) {
	this.altTextAttr = altTextAttr ;
	this.altTextBuffer.setDefaultAttr( this.altTextAttr ) ;
	this.altTextBuffer.setVoidAttr( this.voidAttr ) ;
} ;



TextBox.prototype.getContentSize = function() { return this.textBuffer.getContentSize() ; } ;
TextBox.prototype.getContent = function() { return this.textBuffer.getText() ; } ;



TextBox.prototype.setContent = function( content , hasMarkup , dontDraw ) {
	var contentSize ;

	if ( typeof content !== 'string' ) {
		if ( content === null || content === undefined ) { content = '' ; }
		else { content = '' + content ; }
	}

	this.content = content ;
	this.contentHasMarkup = hasMarkup ;

	this.textBuffer.setText( this.content , this.contentHasMarkup , this.textAttr ) ;

	if ( this.stateMachine ) {
		this.textBuffer.runStateMachine() ;
	}

	// Move the cursor at the end of the input
	this.textBuffer.moveToEndOfBuffer() ;

	if ( ! dontDraw ) {
		this.drawCursor() ;
		this.outerDraw() ;
	}
} ;



// Get content for alternate textBuffer
TextBox.prototype.getAltContent = function() {
	if ( ! this.altTextBuffer ) { return null ; }
	return this.altTextBuffer.getText() ;
} ;



// Set content for alternate textBuffer
TextBox.prototype.setAltContent = function( content , hasMarkup , dontDraw ) {
	if ( ! this.altTextBuffer ) { return ; }

	var contentSize ;

	if ( typeof content !== 'string' ) {
		if ( content === null || content === undefined ) { content = '' ; }
		else { content = '' + content ; }
	}

	//this.content = content ;
	//this.contentHasMarkup = hasMarkup ;

	this.altTextBuffer.setText( content , hasMarkup , this.altTextAttr ) ;

	//if ( this.stateMachine ) { this.altTextBuffer.runStateMachine() ; }

	if ( ! dontDraw ) {
		this.drawCursor() ;
		this.outerDraw() ;
	}
} ;



TextBox.prototype.prependContent = function( content , dontDraw ) { return this.addContent( content , 'prepend' , dontDraw ) ; } ;
TextBox.prototype.appendContent = function( content , dontDraw ) { return this.addContent( content , 'append' , dontDraw ) ; } ;
TextBox.prototype.appendLog = function( content , dontDraw ) { return this.addContent( content , 'appendLog' , dontDraw ) ; } ;



TextBox.prototype.addContent = function( content , mode , dontDraw ) {
	var contentSize , scroll = false ;

	if ( typeof content !== 'string' ) {
		if ( content === null || content === undefined ) { content = '' ; }
		else { content = '' + content ; }
	}

	switch ( mode ) {
		case 'prepend' :
			this.content = content + this.content ;
			this.textBuffer.prepend( content , this.contentHasMarkup , this.textAttr ) ;
			break ;
		case 'appendLog' :
			// Like 'append' but add a newLine if the last line is not empty, and also check if we need to scroll
			scroll = this.textBuffer.buffer.length <= this.textAreaHeight || this.scrollY <= this.textAreaHeight - this.textBuffer.buffer.length ;
			this.textBuffer.moveToEndOfBuffer() ;
			if ( this.textBuffer.cx ) { content = '\n' + content ; }
			this.content += content ;
			this.textBuffer.insert( content , this.contentHasMarkup , this.textAttr ) ;
			break ;
		case 'append' :
		default :
			this.content += content ;
			this.textBuffer.append( content , this.contentHasMarkup , this.textAttr ) ;
			break ;
	}

	if ( this.stateMachine ) {
		this.textBuffer.runStateMachine() ;
	}

	// Move the cursor at the end of the input
	this.textBuffer.moveToEndOfBuffer() ;

	if ( scroll ) {
		this.scrollToBottom( dontDraw ) ;
	}
	else if ( ! dontDraw ) {
		// Set it again to the scrollY value: it forces re-computing of the slider rate depending on new content
		if ( this.vScrollBarSlider ) {
			this.vScrollBarSlider.setValue( this.scrollY , true ) ;
		}

		this.drawCursor() ;
		this.draw() ;
		//this.outerDraw() ;
	}
} ;



// TODOC
TextBox.prototype.setTabWidth = function( tabWidth , internal = false ) {
	this.tabWidth = + tabWidth || 4 ;
	this.textBuffer.setTabWidth( this.tabWidth ) ;
	if ( this.altTextBuffer ) { this.altTextBuffer.setTabWidth( this.tabWidth ) ; }

	if ( ! internal ) {
		this.draw() ;
	}
} ;



// TODOC
TextBox.prototype.setStateMachine = function( stateMachine , internal = false ) {
	this.stateMachine = stateMachine ;
	this.textBuffer.stateMachine = this.stateMachine ;

	if ( this.stateMachine && ! internal ) {
		this.textBuffer.runStateMachine() ;
		this.draw() ;
	}
} ;



TextBox.prototype.onWheel = function( data ) {
	// It's a "tiny" scroll
	if ( ! this.hasFocus ) {
		this.document.giveFocusTo( this , 'select' ) ;
	}

	if ( this.scrollable ) {
		this.scroll( 0 , -data.yDirection * Math.ceil( this.textAreaHeight / 5 ) ) ;
	}
} ;



TextBox.prototype.onClick = function( data ) {
	if ( this.hasFocus ) {
		if ( this.textBuffer.selectionRegion ) {
			this.textBuffer.resetSelectionRegion() ;
			this.draw() ;
		}
	}
	else {
		if ( this.scrollable ) {
			// It is susceptible to click event only when it is scrollable
			this.document.giveFocusTo( this , 'select' ) ;
		}
	}
} ;



TextBox.prototype.onDrag = function( data ) {
	//console.error( "TB Drag:" , data ) ;
	var xmin , ymin , xmax , ymax ;

	if ( ! this.hasFocus ) {
		this.document.giveFocusTo( this , 'select' ) ;
	}

	if ( data.yFrom < data.y || ( data.yFrom === data.y && data.xFrom <= data.x ) ) {
		ymin = data.yFrom ;
		ymax = data.y ;
		xmin = data.xFrom ;
		xmax = data.x ;
	}
	else {
		ymin = data.y ;
		ymax = data.yFrom ;
		xmin = data.x ;
		xmax = data.xFrom ;
	}

	this.textBuffer.setSelectionRegion( {
		xmin: xmin - this.scrollX ,
		xmax: xmax - this.scrollX ,
		ymin: ymin - this.scrollY ,
		ymax: ymax - this.scrollY
	} ) ;

	if ( this.document ) {
		this.document.setSystemClipboard( this.textBuffer.getSelectionText() , 'primary' ).catch( () => undefined ) ;
	}

	this.draw() ;
} ;



TextBox.prototype.onParentResize = function() {
	if ( ! this.autoWidth && ! this.autoHeight ) { return ; }

	var options = {} ;

	if ( this.autoWidth ) {
		options.outputWidth = Math.round( this.outputDst.width * this.autoWidth ) ;
	}

	if ( this.autoHeight ) {
		options.outputHeight = Math.round( this.outputDst.height * this.autoHeight ) ;
	}

	this.setSizeAndPosition( options ) ;
	this.draw() ;
} ;



const userActions = TextBox.prototype.userActions ;

userActions.tinyScrollUp = function() {
	this.scroll( 0 , Math.ceil( this.textAreaHeight / 5 ) ) ;
	this.emit( 'scroll' ) ;
} ;

userActions.tinyScrollDown = function() {
	this.scroll( 0 , -Math.ceil( this.textAreaHeight / 5 ) ) ;
	this.emit( 'scroll' ) ;
} ;

userActions.scrollUp = function() {
	this.scroll( 0 , Math.ceil( this.textAreaHeight / 2 ) ) ;
	this.emit( 'scroll' ) ;
} ;

userActions.scrollDown = function() {
	this.scroll( 0 , -Math.ceil( this.textAreaHeight / 2 ) ) ;
	this.emit( 'scroll' ) ;
} ;

userActions.scrollLeft = function() {
	this.scroll( Math.ceil( this.textAreaWidth / 2 ) , 0 ) ;
	this.emit( 'scroll' ) ;
} ;

userActions.scrollRight = function() {
	this.scroll( -Math.ceil( this.textAreaWidth / 2 ) , 0 ) ;
	this.emit( 'scroll' ) ;
} ;

userActions.scrollTop = function() {
	this.scrollToTop() ;
	this.emit( 'scroll' ) ;
} ;

userActions.scrollBottom = function() {
	this.scrollToBottom() ;
	this.emit( 'scroll' ) ;
} ;

userActions.copyToDocumentClipboard = function() {
	if ( this.document ) {
		this.document.setDocumentClipboard( this.textBuffer.getSelectionText() ) ;
	}
} ;

userActions.copyToSystemClipboard = function() {
	if ( this.document ) {
		this.document.setSystemClipboard( this.textBuffer.getSelectionText() ).catch( () => undefined ) ;
	}
} ;


},{"../Rect.js":2,"../ScreenBuffer.js":3,"../TextBuffer.js":6,"./Element.js":25,"./Slider.js":36,"string-kit":133}],39:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const TextBox = require( './TextBox.js' ) ;
const boxesChars = require( '../spChars.js' ).box ;



function TextTable( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	Element.call( this , options ) ;

	this.cellContents = options.cellContents ;	// Should be an array of array of text

	// This replace .contentWidth/.contentHeight for cell-only size (without shrinking/expanding/fitting)
	this.rawContentWidth = 0 ;
	this.rawContentHeight = 0 ;

	this.contentHasMarkup = options.contentHasMarkup ;

	this.textBoxes = null ;				// Same format: array of array of textBoxes
	this.rowCount = 0 ;
	this.columnCount = 0 ;

	this.rowHeights = [] ;
	this.columnWidths = [] ;

	this.textAttr = options.textAttr || { bgColor: 'default' } ;
	this.voidAttr = options.voidAttr || options.emptyAttr || null ;

	this.firstRowTextAttr = options.firstRowTextAttr || null ;
	this.firstRowVoidAttr = options.firstRowVoidAttr || null ;
	this.evenRowTextAttr = options.evenRowTextAttr || null ;
	this.evenRowVoidAttr = options.evenRowVoidAttr || null ;

	this.firstColumnTextAttr = options.firstColumnTextAttr || null ;
	this.firstColumnVoidAttr = options.firstColumnVoidAttr || null ;
	this.evenColumnTextAttr = options.evenColumnTextAttr || null ;
	this.evenColumnVoidAttr = options.evenColumnVoidAttr || null ;

	this.firstCellTextAttr = options.firstCellTextAttr || null ;
	this.firstCellVoidAttr = options.firstCellVoidAttr || null ;

	// When rowNumber AND columnNumber are both even
	this.evenCellTextAttr = options.evenCellTextAttr || null ;
	this.evenCellVoidAttr = options.evenCellVoidAttr || null ;

	// Checker-like: when rowNumber + columnNumber is even
	this.checkerEvenCellTextAttr = options.checkerEvenCellTextAttr || null ;
	this.checkerEvenCellVoidAttr = options.checkerEvenCellVoidAttr || null ;

	/*
	// Select attr
	// /!\ NOT IMPLEMENTED YET /!\
	// Would allow one to navigate the table (it could be useful for making editable cells)
	this.selectedTextAttr = options.selectedTextAttr || null ;
	this.selectedVoidAttr = options.selectedVoidAttr || null ;
	this.selectable = options.selectable || null ;	// Can be 'row', 'column' or 'cell'
	this.selectedX = this.selectedY = 0 ;
	*/

	this.expandToWidth = options.expandToWidth !== undefined ? !! options.expandToWidth : !! options.fit ;
	this.shrinkToWidth = options.shrinkToWidth !== undefined ? !! options.shrinkToWidth : !! options.fit ;
	this.expandToHeight =
		options.expandToHeight !== undefined ? !! options.expandToHeight :
		! options.height ? false :
		!! options.fit ;
	this.shrinkToHeight =
		options.shrinkToHeight !== undefined ? !! options.shrinkToHeight :
		! options.height ? false :
		!! options.fit ;
	this.wordWrap = options.wordWrap !== undefined || options.wordwrap !== undefined ?
		!! ( options.wordWrap || options.wordwrap ) : !! options.fit ;
	this.lineWrap = this.wordWrap || ( options.lineWrap !== undefined ? !! options.lineWrap : !! options.fit ) ;

	this.hasBorder = options.hasBorder !== undefined ? !! options.hasBorder : true ;	// Default to true
	this.borderAttr = options.borderAttr || this.textAttr ;
	this.borderChars = boxesChars.light ;

	if ( typeof options.borderChars === 'object' ) {
		this.borderChars = boxesChars.__fix__( options.borderChars ) ;
	}
	else if ( typeof options.borderChars === 'string' && boxesChars[ options.borderChars ] ) {
		this.borderChars = boxesChars[ options.borderChars ] ;
	}

	if ( options.textBoxKeyBindings ) { this.textBoxKeyBindings = options.textBoxKeyBindings ; }

	this.initChildren() ;
	this.computeCells() ;

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'TextTable' && ! options.noDraw ) { this.draw() ; }
}

module.exports = TextTable ;
Element.inherit( TextTable ) ;



// Support for strictInline mode
TextTable.prototype.strictInlineSupport = true ;
TextTable.prototype.staticInline = true ;
TextTable.prototype.inlineResizeToContent = true ;



TextTable.prototype.textBoxKeyBindings = TextBox.prototype.keyBindings ;



TextTable.prototype.setCellContent = function( x , y , content , dontDraw = false , dontUpdateLayout = false ) {
	var textBox = this.textBoxes[ y ] && this.textBoxes[ y ][ x ] ;

	// We don't add cell, it should already exists
	if ( ! textBox ) { return ; }

	// Save cell content
	this.cellContents[ y ][ x ] = content ;
	textBox.setContent( content , this.contentHasMarkup , true ) ;

	if ( ! dontUpdateLayout ) {
		this.computeCells() ;
		if ( ! dontDraw ) { this.draw() ; }
	}
	else {
		if ( ! dontDraw ) { textBox.draw() ; }
	}
} ;



TextTable.prototype.setCellAttr = function( x , y , textAttr , voidAttr , dontDraw = false ) {
	var textBox = this.textBoxes[ y ] && this.textBoxes[ y ][ x ] ;
	if ( ! textBox ) { return ; }

	if ( voidAttr === undefined ) { voidAttr = textAttr ; }

	textBox.setAttr( textAttr , voidAttr , dontDraw ) ;
} ;



TextTable.prototype.resetCellAttr = function( x , y , dontDraw = false ) {
	var textBox = this.textBoxes[ y ] && this.textBoxes[ y ][ x ] ;
	if ( ! textBox ) { return ; }

	var textAttr = this.getTextAttrForCell( x , y ) ,
		voidAttr = this.getVoidAttrForCell( x , y , textAttr ) ;

	textBox.setAttr( textAttr , voidAttr , dontDraw ) ;
} ;



TextTable.prototype.setRowAttr = function( y , textAttr , voidAttr , dontDraw = false ) {
	for ( let x = 0 ; x < this.columnCount ; x ++ ) { this.setCellAttr( x , y , textAttr , voidAttr , true ) ; }
	if ( ! dontDraw ) { this.draw() ; }
} ;



TextTable.prototype.resetRowAttr = function( y , dontDraw = false ) {
	for ( let x = 0 ; x < this.columnCount ; x ++ ) { this.resetCellAttr( x , y , true ) ; }
	if ( ! dontDraw ) { this.draw() ; }
} ;



TextTable.prototype.setColumnAttr = function( x , textAttr , voidAttr , dontDraw = false ) {
	for ( let y = 0 ; y < this.rowCount ; y ++ ) { this.setCellAttr( x , y , textAttr , voidAttr , true ) ; }
	if ( ! dontDraw ) { this.draw() ; }
} ;



TextTable.prototype.resetColumnAttr = function( x , dontDraw = false ) {
	for ( let y = 0 ; y < this.rowCount ; y ++ ) { this.resetCellAttr( x , y , true ) ; }
	if ( ! dontDraw ) { this.draw() ; }
} ;



TextTable.prototype.setTableAttr = function( textAttr , voidAttr , dontDraw = false ) {
	for ( let y = 0 ; y < this.rowCount ; y ++ ) {
		for ( let x = 0 ; x < this.columnCount ; x ++ ) { this.setCellAttr( x , y , textAttr , voidAttr , true ) ; }
	}

	if ( ! dontDraw ) { this.draw() ; }
} ;



TextTable.prototype.resetTableAttr = function( dontDraw = false ) {
	for ( let y = 0 ; y < this.rowCount ; y ++ ) {
		for ( let x = 0 ; x < this.columnCount ; x ++ ) { this.resetCellAttr( x , y , true ) ; }
	}

	if ( ! dontDraw ) { this.draw() ; }
} ;



TextTable.prototype.getTextAttrForCell = function( x , y ) {
	return this.firstCellTextAttr && ! x && ! y  ?  this.firstCellTextAttr  :
		this.firstRowTextAttr && ! y  ?  this.firstRowTextAttr  :
		this.firstColumnTextAttr && ! x  ?  this.firstColumnTextAttr  :
		this.evenCellTextAttr && ! ( x % 2 ) && ! ( y % 2 )  ?  this.evenCellTextAttr  :
		this.checkerEvenCellTextAttr && ! ( ( x + y ) % 2 )  ?  this.checkerEvenCellTextAttr  :
		this.evenRowTextAttr && ! ( y % 2 )  ?  this.evenRowTextAttr  :
		this.evenColumnTextAttr && ! ( y % 2 )  ?  this.evenColumnTextAttr  :
		this.textAttr ;
} ;



TextTable.prototype.getVoidAttrForCell = function( x , y , textAttr ) {
	return this.firstCellVoidAttr && ! x && ! y  ?  this.firstCellVoidAttr  :
		this.firstRowVoidAttr && ! y  ?  this.firstRowVoidAttr  :
		this.firstColumnVoidAttr && ! x  ?  this.firstColumnVoidAttr  :
		this.evenCellVoidAttr && ! ( x % 2 ) && ! ( y % 2 )  ?  this.evenCellVoidAttr  :
		this.checkerEvenCellVoidAttr && ! ( ( x + y ) % 2 )  ?  this.checkerEvenCellVoidAttr  :
		this.evenRowVoidAttr && ! ( y % 2 )  ?  this.evenRowVoidAttr  :
		this.evenColumnVoidAttr && ! ( y % 2 )  ?  this.evenColumnVoidAttr  :
		this.voidAttr || textAttr ;
} ;



TextTable.prototype.initChildren = function() {
	var row , cellContent , textAttr , voidAttr ;

	this.rowCount = this.cellContents.length ;
	this.columnCount = 0 ;
	this.textBoxes = [] ;

	var x = 0 , y = 0 ;

	for ( row of this.cellContents ) {
		this.textBoxes[ y ] = [] ;
		x = 0 ;

		for ( cellContent of row ) {
			if ( x >= this.columnCount ) { this.columnCount = x + 1 ; }

			textAttr = this.getTextAttrForCell( x , y ) ;
			voidAttr = this.getVoidAttrForCell( x , y , textAttr ) ;

			this.textBoxes[ y ][ x ] = new TextBox( {
				internal: true ,
				parent: this ,
				content: cellContent ,
				contentHasMarkup: this.contentHasMarkup ,
				//value: cellContent ,
				x: this.outputX ,
				y: this.outputY ,
				width: this.outputWidth ,
				height: this.outputHeight ,
				lineWrap: this.lineWrap ,
				wordWrap: this.wordWrap ,
				//scrollable: !! this.scrollable ,
				//vScrollBar: !! this.vScrollBar ,
				//hScrollBar: !! this.hScrollBar ,
				//hiddenContent: this.hiddenContent ,
				textAttr ,
				voidAttr ,
				keyBindings: this.textBoxKeyBindings ,
				noDraw: true
			} ) ;

			x ++ ;
		}

		y ++ ;
	}
} ;



TextTable.prototype.computeCells = function() {
	var shrinked = this.computeColumnWidths() ;

	if ( shrinked ) {
		this.textBoxesWordWrap() ;
		//this.computeColumnWidths() ;
	}

	this.computeRowHeights() ;
	this.textBoxesSizeAndPosition() ;
} ;



TextTable.prototype.computeColumnWidths = function() {
	var x , y , textBox , max , width ;

	this.rawContentWidth = + this.hasBorder ;	// +true = 1

	for ( x = 0 ; x < this.columnCount ; x ++ ) {
		max = 0 ;
		for ( y = 0 ; y < this.rowCount ; y ++ ) {
			textBox = this.textBoxes[ y ][ x ] ;
			if ( ! textBox ) { continue ; }
			width = textBox.getContentSize().width || 1 ;
			if ( width > max ) { max = width ; }
		}
		this.columnWidths[ x ] = max ;
		this.rawContentWidth += max + this.hasBorder ;	// +true = 1
	}

	this.contentWidth = this.rawContentWidth ;

	if ( this.expandToWidth && this.rawContentWidth < this.outputWidth ) {
		this.expand( this.rawContentWidth , this.outputWidth , this.columnWidths ) ;
		this.contentWidth = this.outputWidth ;
	}
	else if ( this.shrinkToWidth && this.rawContentWidth > this.outputWidth ) {
		this.shrink( this.rawContentWidth , this.outputWidth , this.columnWidths ) ;
		this.contentWidth = this.outputWidth ;
		return true ;
	}

	return false ;
} ;



TextTable.prototype.computeRowHeights = function() {
	var x , y , textBox , max , height ;

	this.rawContentHeight = + this.hasBorder ;	// +true = 1

	for ( y = 0 ; y < this.rowCount ; y ++ ) {
		max = 0 ;
		for ( x = 0 ; x < this.columnCount ; x ++ ) {
			textBox = this.textBoxes[ y ][ x ] ;
			if ( ! textBox ) { continue ; }
			height = textBox.getContentSize().height || 1 ;
			if ( height > max ) { max = height ; }
		}
		this.rowHeights[ y ] = max ;
		this.rawContentHeight += max + this.hasBorder ;	// +true = 1
	}

	this.contentHeight = this.rawContentHeight ;

	if ( this.expandToHeight && this.rawContentHeight < this.outputHeight ) {
		this.expand( this.rawContentHeight , this.outputHeight , this.rowHeights ) ;
		this.contentHeight = this.outputHeight ;
	}
	else if ( this.shrinkToHeight && this.rawContentHeight > this.outputHeight ) {
		this.shrink( this.rawContentHeight , this.outputHeight , this.rowHeights ) ;
		this.contentHeight = this.outputHeight ;
		return true ;
	}

	return false ;
} ;



// Expand an array of size, using proportional expansion
TextTable.prototype.expand = function( contentSize , outputSize , sizeArray ) {
	var x ,
		floatSize = 0 ,
		remainder = 0 ,
		count = sizeArray.length ,
		noBorderWantedSize = outputSize - ( this.hasBorder ? count + 1 : 0 ) ;

	if ( noBorderWantedSize <= 0 ) { return ; }

	var noBorderSize = contentSize - ( this.hasBorder ? count + 1 : 0 ) ,
		rate = noBorderWantedSize / noBorderSize ;

	// Adjust from left to right
	for ( x = 0 ; x < count ; x ++ ) {
		floatSize = sizeArray[ x ] * rate + remainder ;
		sizeArray[ x ] = Math.max( 1 , Math.round( floatSize ) ) ;
		remainder = floatSize - sizeArray[ x ] ;
	}
} ;



// Shrink an array of size, larger are shrinked first
TextTable.prototype.shrink = function( contentSize , outputSize , sizeArray ) {
	var x , max ,
		secondMax = 0 ,
		maxIndexes = [] ,
		count = sizeArray.length ,
		floatColumnDelta , columnDelta , partialColumn ,
		delta = contentSize - outputSize ;

	while ( delta > 0 ) {
		max = 0 ;
		secondMax = 0 ;
		maxIndexes.length = 0 ;

		for ( x = 0 ; x < count ; x ++ ) {
			if ( sizeArray[ x ] > max ) {
				secondMax = max ;
				max = sizeArray[ x ] ;
				maxIndexes.length = 0 ;
				maxIndexes.push( x ) ;
			}
			else if ( sizeArray[ x ] === max ) {
				maxIndexes.push( x ) ;
			}
			else if ( sizeArray[ x ] > secondMax ) {
				secondMax = sizeArray[ x ] ;
			}
		}

		// We can't shrink anymore
		// /!\ we should probably test that before entering the loop!
		if ( ! max ) { return ; }

		floatColumnDelta = Math.min( max - secondMax , delta / maxIndexes.length ) ;
		columnDelta = Math.floor( floatColumnDelta ) ;

		if ( columnDelta >= 0 ) {
			for ( let index of maxIndexes ) {
				sizeArray[ index ] -= columnDelta ;
				delta -= columnDelta ;
			}
		}

		if ( columnDelta !== floatColumnDelta ) {
			partialColumn = delta % maxIndexes.length ;
			for ( let i = 0 ; i < maxIndexes.length && i < partialColumn ; i ++ ) {
				sizeArray[ maxIndexes[ i ] ] -- ;
			}
			delta -= partialColumn ;
		}
	}
} ;



TextTable.prototype.textBoxesWordWrap = function() {
	var x , y , textBox ;

	for ( y = 0 ; y < this.rowCount ; y ++ ) {
		for ( x = 0 ; x < this.columnCount ; x ++ ) {
			textBox = this.textBoxes[ y ][ x ] ;

			if ( textBox ) {
				textBox.setSizeAndPosition( {
					outputX: this.outputX ,
					outputY: this.outputY ,
					outputWidth: this.columnWidths[ x ] ,
					outputHeight: this.outputHeight
				} ) ;
			}
		}
	}
} ;



TextTable.prototype.textBoxesSizeAndPosition = function() {
	var x , y , outputX , outputY , textBox ;

	outputY = this.outputY + this.hasBorder ;

	for ( y = 0 ; y < this.rowCount ; y ++ ) {
		outputX = this.outputX + this.hasBorder ;

		for ( x = 0 ; x < this.columnCount ; x ++ ) {
			textBox = this.textBoxes[ y ][ x ] ;

			if ( textBox ) {
				textBox.setSizeAndPosition( {
					outputX ,
					outputY ,
					outputWidth: this.columnWidths[ x ] ,
					outputHeight: this.rowHeights[ y ]
				} ) ;
			}

			outputX += this.columnWidths[ x ] + this.hasBorder ;
		}

		outputY += this.rowHeights[ y ] + this.hasBorder ;
	}
} ;



TextTable.prototype.preDrawSelf = function() {
	// This only draw the frame/border
	if ( ! this.hasBorder ) { return ; }

	var i , j , x , y ;

	y = this.outputY ;

	for ( j = 0 ; j < this.rowHeights.length ; j ++ ) {
		x = this.outputX ;

		for ( i = 0 ; i < this.columnWidths.length ; i ++ ) {
			// For each cell...

			// ... draw the top-left corner
			this.outputDst.put( { x , y , attr: this.borderAttr } ,
				j ?
					( i ? this.borderChars.cross : this.borderChars.leftTee ) :
					( i ? this.borderChars.topTee : this.borderChars.topLeft )
			) ;

			// ... draw the left border
			this.outputDst.put( {
				x , y: y + 1 , direction: 'down' , attr: this.borderAttr
			} , this.borderChars.vertical.repeat( this.rowHeights[ j ] ) ) ;
			x ++ ;

			// ... draw the top border
			this.outputDst.put( { x , y , attr: this.borderAttr } , this.borderChars.horizontal.repeat( this.columnWidths[ i ] ) ) ;
			x += this.columnWidths[ i ] ;
		}

		// Draw the top-right corner only for the last cell
		this.outputDst.put( { x , y , attr: this.borderAttr } , j ? this.borderChars.rightTee : this.borderChars.topRight ) ;

		// Draw the right border only for the last cell
		this.outputDst.put( {
			x , y: y + 1 , direction: 'down' , attr: this.borderAttr
		} , this.borderChars.vertical.repeat( this.rowHeights[ j ] ) ) ;
		y += this.rowHeights[ j ] + 1 ;
	}


	// Only for the last row, we have to draw the bottom border and corners
	x = this.outputX ;

	for ( i = 0 ; i < this.columnWidths.length ; i ++ ) {
		// For each bottom cells...

		// ... draw the bottom-left corner
		this.outputDst.put( { x , y , attr: this.borderAttr } , i ? this.borderChars.bottomTee : this.borderChars.bottomLeft ) ;
		x ++ ;

		// ... draw the bottom border
		this.outputDst.put( { x , y , attr: this.borderAttr } , this.borderChars.horizontal.repeat( this.columnWidths[ i ] ) ) ;
		x += this.columnWidths[ i ] ;
	}

	// Draw the bottom-right corner only for the last cell of the last row
	this.outputDst.put( { x , y , attr: this.borderAttr } , this.borderChars.bottomRight ) ;
} ;


},{"../spChars.js":54,"./Element.js":25,"./TextBox.js":38}],40:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const Text = require( './Text.js' ) ;
const Button = require( './Button.js' ) ;



function ToggleButton( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	// Almost everything is constructed by the normal 'submit-button'
	Button.call( this , options ) ;

	this.value = !! options.value ;
	this.key = options.key || null ;

	if ( this.elementType === 'ToggleButton' && ! options.noDraw ) { this.draw() ; }
}

module.exports = ToggleButton ;
Element.inherit( ToggleButton , Button ) ;



ToggleButton.prototype.keyBindings = {
	ENTER: 'toggle' ,
	KP_ENTER: 'toggle' ,
	ALT_ENTER: 'submit'
} ;



// No blink effect
ToggleButton.prototype.blink = function() {} ;



ToggleButton.prototype.getValue = function() { return this.value ; } ;

ToggleButton.prototype.setValue = function( value , noDraw , noEmit ) {
	value = !! value ;
	if ( this.value === value ) { return ; }
	this.value = value ;
	this.updateStatus() ;

	if ( ! noEmit ) {
		this.emit( 'toggle' , this.value , undefined , this ) ;
		this.emit( this.value ? 'turnOn' : 'turnOff' , this.value , undefined , this ) ;
	}

	if ( ! noDraw ) { this.draw() ; }
} ;



ToggleButton.prototype.toggle = function( noDraw ) {
	this.setValue( ! this.value , noDraw ) ;
} ;



ToggleButton.prototype.updateStatus = function() {
	if ( this.disabled ) {
		this.attr = this.disabledAttr ;
		this.content = this.disabledContent ;
		this.leftPadding = this.disabledLeftPadding ;
		this.rightPadding = this.disabledRightPadding ;
	}
	else if ( this.hasFocus ) {
		if ( this.value ) {
			this.attr = this.turnedOnFocusAttr ;
			this.content = this.turnedOnFocusContent ;
			this.leftPadding = this.turnedOnFocusLeftPadding ;
			this.rightPadding = this.turnedOnFocusRightPadding ;
		}
		else {
			this.attr = this.turnedOffFocusAttr ;
			this.content = this.turnedOffFocusContent ;
			this.leftPadding = this.turnedOffFocusLeftPadding ;
			this.rightPadding = this.turnedOffFocusRightPadding ;
		}
	}
	else if ( this.value ) {
		this.attr = this.turnedOnBlurAttr ;
		this.content = this.turnedOnBlurContent ;
		this.leftPadding = this.turnedOnBlurLeftPadding ;
		this.rightPadding = this.turnedOnBlurRightPadding ;
	}
	else {
		this.attr = this.turnedOffBlurAttr ;
		this.content = this.turnedOffBlurContent ;
		this.leftPadding = this.turnedOffBlurLeftPadding ;
		this.rightPadding = this.turnedOffBlurRightPadding ;
	}
} ;



ToggleButton.prototype.onHover = function( data ) {
	if ( this.disabled ) { return ; }
	this.document.giveFocusTo( this , 'hover' ) ;
} ;



ToggleButton.prototype.onClick = function( data ) {
	if ( this.disabled ) { return ; }
	this.document.giveFocusTo( this , 'select' ) ;
	this.toggle() ;
} ;



ToggleButton.prototype.onShortcut = function() {
	if ( this.disabled ) { return ; }
	this.document.giveFocusTo( this , 'select' ) ;
	this.toggle() ;
} ;



const userActions = ToggleButton.prototype.userActions ;

userActions.toggle = function() {
	if ( this.disabled ) { return ; }
	this.toggle() ;
} ;

userActions.submit = function( key ) {
	if ( this.disabled ) { return ; }
	this.emit( 'submit' , this.key ? { [ this.key ]: this.value } : this.value , this.actionKeyBindings[ key ] , this ) ;
} ;


},{"./Button.js":17,"./Element.js":25,"./Text.js":37}],41:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Element = require( './Element.js' ) ;
const Container = require( './Container.js' ) ;
const framesChars = require( '../spChars.js' ).box ;



function Window( options ) {
	// Clone options if necessary
	options = ! options ? {} : options.internal ? options : Object.create( options ) ;
	options.internal = true ;

	if ( options.movable === undefined ) { options.movable = true ; }
	if ( options.title ) { options.content = options.title ; }
	if ( options.titleHasMarkup ) { options.contentHasMarkup = options.titleHasMarkup ; }

	Container.call( this , options ) ;

	this.frameChars = framesChars.double ;

	// Backward compatibility, boxChars is DEPRECATED
	if ( options.boxChars && ! options.frameChars ) { options.frameChars = options.boxChars ; }

	if ( options.frameChars ) {
		if ( typeof options.frameChars === 'object' ) {
			this.frameChars = options.frameChars ;
		}
		else if ( typeof options.frameChars === 'string' && framesChars[ options.frameChars ] ) {
			this.frameChars = framesChars[ options.frameChars ] ;
		}
	}

	// Only draw if we are not a superclass of the object
	if ( this.elementType === 'Window' && ! options.noDraw ) { this.draw() ; }
}

module.exports = Window ;
Element.inherit( Window , Container ) ;



Window.prototype.containerBorderSize = 1 ;
Window.prototype.outerDrag = true ;



Window.prototype.preDrawSelf = function() {
	var y , title , titleWidth , vFrame ,
		titleMaxWidth = this.outputWidth - 8 ;

	// Draw the top border
	if ( this.content && titleMaxWidth >= 1 ) {
		title = Element.truncateContent( this.content , titleMaxWidth , this.contentHasMarkup ) ;
		titleWidth = Element.getLastTruncateWidth() ;

		this.outputDst.put(
			{ x: this.outputX , y: this.outputY , markup: this.contentHasMarkup } ,
			this.frameChars.topLeft + this.frameChars.horizontal
			+ '[' + title + ']'
			+ this.frameChars.horizontal.repeat( this.outputWidth - 5 - titleWidth )
			+ this.frameChars.topRight
		) ;
	}
	else {
		this.outputDst.put(
			{ x: this.outputX , y: this.outputY } ,
			this.frameChars.topLeft + this.frameChars.horizontal.repeat( this.outputWidth - 2 ) + this.frameChars.topRight
		) ;
	}

	// Draw the bottom border
	this.outputDst.put(
		{ x: this.outputX , y: this.outputY + this.outputHeight - 1 } ,
		this.frameChars.bottomLeft + this.frameChars.horizontal.repeat( this.outputWidth - 2 ) + this.frameChars.bottomRight
	) ;

	// Draw the left and right border
	vFrame = this.frameChars.vertical.repeat( this.outputHeight - 2 ) ;
	this.outputDst.put( { x: this.outputX , y: this.outputY + 1 , direction: 'down' } , vFrame ) ;
	this.outputDst.put( { x: this.outputX + this.outputWidth - 1 , y: this.outputY + 1 , direction: 'down' } , vFrame ) ;

	Container.prototype.preDrawSelf.call( this ) ;
} ;



// Allow only the top-border/title-bar to be draggable?
//Window.prototype.onDrag = function( data ) { Container.prototype.onDrag.call( this , data ) ; } ;


},{"../spChars.js":54,"./Container.js":21,"./Element.js":25}],42:[function(require,module,exports){
(function (process){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Promise = require( 'seventh' ) ;
const string = require( 'string-kit' ) ;

require( './patches.js' ) ;
const execAsync = require( 'child_process' ).execAsync ;
const execFileAsync = require( 'child_process' ).execFileAsync ;
const spawn = require( 'child_process' ).spawn ;



const XCLIP_SELECTION_ARG = {
	c: 'clipboard' ,
	p: 'primary' ,
	s: 'secondary'
} ;



if ( process.platform === 'linux' ) {
	exports.getClipboard = async ( source ) => {
		var arg = XCLIP_SELECTION_ARG[ source[ 0 ] ] || 'clipboard' ;
		return await execFileAsync( 'xclip' , [ '-o' , '-selection' , arg ] ) ;
	} ;

	exports.setClipboard = async ( str , source ) => {
		var promise = new Promise() ;
		var arg = XCLIP_SELECTION_ARG[ source[ 0 ] ] || 'clipboard' ;
		var xclip = spawn( 'xclip' , [ '-i' , '-selection' , arg ] ) ;

		xclip.on( 'error' , error => {
			//console.error( 'xclip error:' , error ) ;
			promise.reject( error ) ;
		} ) ;

		xclip.on( 'exit' , code => {
			//console.log( 'xclip exited with code:' , code ) ;
			if ( code !== 0 ) { promise.reject( code ) ; }
			else { promise.resolve() ; }
		} ) ;

		// Send the string to push to the clipboard
		xclip.stdin.end( str ) ;

		return promise ;
	} ;
}
else {
	exports.getClipboard = () => Promise.reject( new Error( 'No clipboard manipulation program found' ) ) ;
	exports.setClipboard = () => Promise.reject( new Error( 'No clipboard manipulation program found' ) ) ;
}


}).call(this)}).call(this,require('_process'))
},{"./patches.js":49,"_process":191,"child_process":148,"seventh":114,"string-kit":133}],43:[function(require,module,exports){
(function (process){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const autoComplete = require( './autoComplete.js' ) ;

const fs = require( 'fs' ) ;
const path = require( 'path' ) ;

//const string = require( 'string-kit' ) ;
const Promise = require( 'seventh' ) ;



// Like fs.readdir(), but performs fs.stat() for each file in order to add a '/' to directories
exports.readdir = async ( dir , accept = null ) => {
	if ( dir[ dir.length - 1 ] !== '/' ) { dir += '/' ; }

	var files = await fs.promises.readdir( dir ) ;

	var fixedFiles = await Promise.map( files , async ( file ) => {
		var stats = await fs.promises.lstat( dir + file ) ;
		if ( accept && ! exports.statsFilter( stats , accept ) ) { return null ; }
		if ( stats.isDirectory() ) { file += '/' ; }
		return file ;
	} ) ;

	fixedFiles = fixedFiles.filter( file => file !== null ) ;

	return fixedFiles ;
} ;



exports.statsFilter = ( stats , accept ) => {
	if (
		( stats.isFile() && ! accept.file )
        || ( stats.isDirectory() && ! accept.directory )
	) {
		return false ;
	}

	return true ;
} ;



// Resolve base directory, returning a full path with a trailing slash
exports.resolveBaseDir = async ( baseDir ) => {
	if ( ! baseDir ) {
		baseDir = process.cwd() ;
	}
	else {
		baseDir = path.resolve( baseDir ) ;

		if ( ! path.isAbsolute( baseDir ) ) {
			baseDir = await fs.promises.realpath( baseDir ) ;
		}
	}

	if ( baseDir[ baseDir.length - 1 ] !== '/' ) {
		baseDir += '/' ;
	}

	return baseDir ;
} ;



/*
	params:
		* baseDir (mandatory) the base-directory, from where to start searching for files
		* accept `object` (optional)
			* file: accept files
			* directory: accept directory
*/
exports.autoCompleteFile = async ( inputString , params ) => {
	var inputDir , inputFile , currentDir , files , completion ,
		baseDir = params.baseDir ;

	// First, we have to manage input, splitting user inputDir/inputFile and setting the actual directory
	if ( inputString[ inputString.length - 1 ] === '/' ) {
		currentDir = inputDir = inputString ;
		inputFile = '' ;
	}
	else if ( inputString === '.' ) {
		inputDir = '' ;
		currentDir = '' ;
		inputFile = '.' ;
	}
	else {
		inputDir = path.dirname( inputString ) ;

		if ( inputDir === '.' ) {
			if ( inputString.startsWith( './' ) ) {
				inputDir = './' ;
				currentDir = '' ;
			}
			else {
				inputDir = currentDir = '' ;
			}
		}
		else {
			currentDir = inputDir = inputDir + '/' ;
		}

		inputFile = path.basename( inputString ) ;
	}

	// If the input doesn't start with a '/', prepend the baseDir
	if ( ! path.isAbsolute( currentDir ) ) { currentDir = baseDir + currentDir ; }

	try {
		files = await exports.readdir( currentDir , params.accept ) ;
	}
	catch ( error ) {
		return inputString ;
	}

	if ( ! Array.isArray( files ) || ! files.length ) { return inputString ; }

	completion = autoComplete( files , inputFile , true , inputDir ) ;
	//console.error( 'fileHelpers.js completion:' , completion , inputDir ) ;

	return completion ;
} ;


}).call(this)}).call(this,require('_process'))
},{"./autoComplete.js":7,"_process":191,"fs":148,"path":190,"seventh":114}],44:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const fileHelpers = require( './fileHelpers.js' ) ;
const path = require( 'path' ) ;



/*
	/!\ Document that!!! /!\
*/
module.exports = async function fileInput( options , callback ) {
	var baseDir , autoCompleteFileOptions , inputFieldOptions , input ;

	if ( typeof options === 'function' ) { callback = options ; options = {} ; }
	if ( ! options || typeof options !== 'object' ) { options = {} ; }

	try {
		baseDir = await fileHelpers.resolveBaseDir( options.baseDir ) ;

		autoCompleteFileOptions = { baseDir } ;

		// Transmit options to inputField()
		inputFieldOptions = Object.assign( {} , options , {
			autoComplete: inputString => fileHelpers.autoCompleteFile( inputString , autoCompleteFileOptions ) ,
			autoCompleteMenu: true ,
			minLength: 1
		} ) ;

		input = await this.inputField( inputFieldOptions ).promise ;
	}
	catch ( error ) {
		if ( callback ) { callback( error ) ; return ; }
		throw error ;
	}

	if ( ! input && typeof input !== 'string' ) {
		input = undefined ;
	}
	else {
		input = path.resolve( path.isAbsolute( input ) ? input : baseDir + input ) ;
	}

	if ( callback ) { callback( undefined , input ) ; }

	return input ;
} ;


},{"./fileHelpers.js":43,"path":190}],45:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



var Promise = require( 'seventh' ) ;



var defaultKeyBindings = {
	ENTER: 'submit' ,
	KP_ENTER: 'submit' ,
	//LEFT: 'previous' ,
	//RIGHT: 'next' ,
	//UP: 'previousRow' ,
	//DOWN: 'nextRow' ,
	UP: 'previous' ,
	DOWN: 'next' ,
	LEFT: 'previousColumn' ,
	RIGHT: 'nextColumn' ,
	TAB: 'cycleNext' ,
	SHIFT_TAB: 'cyclePrevious' ,
	HOME: 'first' ,
	END: 'last'
} ;



/*
	gridMenu( menuItems , [options] , callback )
		* menuItems `array` of menu item text
		* options `object` of options, where:
			* y `number` the line where the menu will be displayed, default to the next line
			* x `number` the column where the menu will be displayed (default: 1)
			* width `number` the maximum width of the grid menu (default: terminal's width)
			* style `function` the style of unselected items, default to `term`
			* selectedStyle `function` the style of the selected item, default to `term.inverse`
			* leftPadding `string` the text to put before a menu item, default to ' '
			* selectedLeftPadding `string` the text to put before a selected menu item, default to ' '
			* rightPadding `string` the text to put after a menu item, default to ' '
			* selectedRightPadding `string` the text to put after a selected menu item, default to ' '
			* itemMaxWidth `number` the max width for an item, default to the 1/3 of the terminal width
			  or of the specified width option
			* keyBindings `Object` overide default key bindings
			* exitOnUnexpectedKey `boolean` if an unexpected key is pressed, it exits, calling the callback with undefined values
		* callback( error , response ), where:
			* error
			* response `Object` where:
				* selectedIndex `number` the user-selected menu item index
				* selectedText `string` the user-selected menu item text
				* x `number` the x coordinate of the selected menu item (the first character)
				* y `number` the y coordinate of the selected menu item
				* unexpectedKey `string` when 'exitOnUnexpectedKey' option is set, this contains the key that produced the exit
*/
module.exports = function gridMenu( menuItems_ , options , callback ) {
	if ( arguments.length < 1 ) { throw new Error( '[terminal] gridMenu() needs at least an array of menuItems argument' ) ; }

	if ( ! Array.isArray( menuItems_ ) || ! menuItems_.length ) { throw new TypeError( '[terminal] gridMenu(): argument #0 should be a non-empty array' ) ; }

	if ( typeof options === 'function' ) { callback = options ; options = {} ; }
	else if ( ! options || typeof options !== 'object' ) { options = {} ; }

	if ( ! options.style ) { options.style = this ; }
	if ( ! options.selectedStyle ) { options.selectedStyle = this.inverse ; }

	if ( options.leftPadding === undefined ) { options.leftPadding = ' ' ; }
	if ( options.selectedLeftPadding === undefined ) { options.selectedLeftPadding = ' ' ; }
	if ( options.rightPadding === undefined ) { options.rightPadding = ' ' ; }
	if ( options.selectedRightPadding === undefined ) { options.selectedRightPadding = ' ' ; }

	if ( ! options.x ) { options.x = 1 ; }

	if ( ! options.y ) { this( '\n' ) ; }
	else { this.moveTo( options.x , options.y ) ; }

	if ( ! options.width ) { options.width = this.width - options.x + 1 ; }

	// itemMaxWidth default to 1/3 of the terminal width
	if ( ! options.itemMaxWidth ) { options.itemMaxWidth = Math.floor( ( options.width - 1 ) / 3 ) ; }

	var keyBindings = options.keyBindings || defaultKeyBindings ;

	if ( ! this.grabbing ) { this.grabInput() ; }


	var start = {} , selectedIndex = 0 , finished = false , alreadyCleanedUp = false ,
		itemInnerWidth = 0 , itemOuterWidth = 0 ,
		menuItems , columns , rows , padLength ;

	padLength = Math.max( options.leftPadding.length , options.selectedLeftPadding.length ) +
		Math.max( options.rightPadding.length , options.selectedRightPadding.length ) ;

	menuItems_ = menuItems_.map( element => {
		if ( typeof element !== 'string' ) { element = '' + element ; }
		itemInnerWidth = Math.max( itemInnerWidth , element.length ) ;
		return element ;
	} ) ;

	itemInnerWidth = Math.min( itemInnerWidth , options.itemMaxWidth - padLength ) ;
	itemOuterWidth = itemInnerWidth + padLength ;
	columns = Math.floor( options.width / itemOuterWidth ) ;
	rows = Math.ceil( menuItems_.length / columns ) ;

	menuItems = menuItems_.map( ( element , index ) => ( {
		// row first
		//offsetX: ( index % columns ) * itemOuterWidth ,
		//offsetY: Math.floor( index / columns ) ,

		// column first
		offsetY: index % rows ,
		offsetX: options.x - 1 + Math.floor( index / rows ) * itemOuterWidth ,

		index: index ,
		text: element ,
		displayText: element.length > itemInnerWidth ?
			element.slice( 0 , itemInnerWidth - 1 ) + '…' :
			element + ' '.repeat( itemInnerWidth - element.length )
	} ) ) ;


	//console.log( menuItems ) ; process.exit() ;

	var cleanup = ( error , data ) => {
		if ( alreadyCleanedUp ) { return ; }
		alreadyCleanedUp = true ;

		finished = true ;
		this.removeListener( 'key' , onKey ) ;
		this.removeListener( 'mouse' , onMouse ) ;
		this.moveTo( 1 , start.y + rows ) ;

		if ( error ) {
			if ( callback ) { callback( error ) ; }
			else { controller.promise.reject( error ) ; }
			return ;
		}

		var value = data !== undefined ? data : {
			selectedIndex: selectedIndex ,
			selectedText: menuItems[ selectedIndex ].text ,
			x: 1 + menuItems[ selectedIndex ].offsetX ,
			y: start.y + menuItems[ selectedIndex ].offsetY
		} ;

		if ( callback ) { callback( undefined , value ) ; }
		else { controller.promise.resolve( value ) ; }
	} ;

	// Compute the coordinate of the end of a string, given a start coordinate
	var redraw = () => {
		for ( var i = 0 ; i < menuItems.length ; i ++ ) { redrawItem( i ) ; }
		redrawCursor() ;
	} ;

	var redrawItem = ( index ) => {
		var item = menuItems[ index ] ;

		this.moveTo( 1 + item.offsetX , start.y + item.offsetY ) ;

		if ( index === selectedIndex ) {
			options.selectedStyle.noFormat( options.selectedLeftPadding ) ;
			options.selectedStyle.noFormat( item.displayText ) ;
			options.selectedStyle.noFormat( options.selectedRightPadding ) ;
		}
		else {
			options.style.noFormat( options.leftPadding ) ;
			options.style.noFormat( item.displayText ) ;
			options.style.noFormat( options.rightPadding ) ;
		}
	} ;

	var redrawCursor = () => {
		this.moveTo( 1 + menuItems[ selectedIndex ].offsetX , start.y + menuItems[ selectedIndex ].offsetY ) ;
	} ;


	var onKey = ( key , trash , data ) => {
		if ( finished ) { return ; }

		var oldSelectedIndex = selectedIndex ;

		switch( keyBindings[ key ] ) {
			case 'submit' :
				cleanup() ;
				break ;

			case 'previous' :
				if ( selectedIndex > 0 ) {
					selectedIndex -- ;
					redrawItem( selectedIndex ) ;
					redrawItem( selectedIndex + 1 ) ;
					redrawCursor() ;
					//redraw() ;
				}
				break ;

			case 'next' :
				if ( selectedIndex < menuItems.length - 1 ) {
					selectedIndex ++ ;
					redrawItem( selectedIndex - 1 ) ;
					redrawItem( selectedIndex ) ;
					redrawCursor() ;
					//redraw() ;
				}
				break ;
			/*
			case 'previousRow' :
				if ( selectedIndex >= columns )
				{
					selectedIndex -= columns ;
					redrawItem( oldSelectedIndex ) ;
					redrawItem( selectedIndex ) ;
					redrawCursor() ;
					//redraw() ;
				}
				break ;

			case 'nextRow' :
				if ( selectedIndex < menuItems.length - columns )
				{
					selectedIndex += columns ;
					redrawItem( oldSelectedIndex ) ;
					redrawItem( selectedIndex ) ;
					redrawCursor() ;
					//redraw() ;
				}
				break ;
			*/
			case 'previousColumn' :
				if ( selectedIndex >= rows ) {
					selectedIndex -= rows ;
					redrawItem( oldSelectedIndex ) ;
					redrawItem( selectedIndex ) ;
					redrawCursor() ;
					//redraw() ;
				}
				break ;

			case 'nextColumn' :
				if ( selectedIndex < menuItems.length - rows ) {
					selectedIndex += rows ;
					redrawItem( oldSelectedIndex ) ;
					redrawItem( selectedIndex ) ;
					redrawCursor() ;
					//redraw() ;
				}
				break ;

			case 'cyclePrevious' :
				selectedIndex -- ;

				if ( selectedIndex < 0 ) { selectedIndex = menuItems.length - 1 ; }

				redrawItem( oldSelectedIndex ) ;
				redrawItem( selectedIndex ) ;
				redrawCursor() ;
				//redraw() ;
				break ;

			case 'cycleNext' :
				selectedIndex ++ ;

				if ( selectedIndex >= menuItems.length ) { selectedIndex = 0 ; }

				redrawItem( oldSelectedIndex ) ;
				redrawItem( selectedIndex ) ;
				redrawCursor() ;
				//redraw() ;
				break ;

			case 'first' :
				if ( selectedIndex !== 0 ) {
					selectedIndex = 0 ;
					redrawItem( oldSelectedIndex ) ;
					redrawItem( selectedIndex ) ;
					redrawCursor() ;
					//redraw() ;
				}
				break ;

			case 'last' :
				if ( selectedIndex !== menuItems.length - 1 ) {
					selectedIndex = menuItems.length - 1 ;
					redrawItem( oldSelectedIndex ) ;
					redrawItem( selectedIndex ) ;
					redrawCursor() ;
					//redraw() ;
				}
				break ;

			default :
				if ( options.exitOnUnexpectedKey ) {
					cleanup( undefined , { unexpectedKey: key , unexpectedKeyData: data } ) ;
				}
				break ;
		}
	} ;


	var onMouse = ( name , data ) => {

		if ( finished ) { return ; }

		// If out of bounds, exit now!
		if ( data.y < start.y || data.y >= start.y + rows ) { return ; }

		var i , inBounds = false ,
			oldSelectedIndex = selectedIndex ;

		for ( i = 0 ; i < menuItems.length ; i ++ ) {
			if (
				data.y === start.y + menuItems[ i ].offsetY &&
				data.x >= 1 + menuItems[ i ].offsetX &&
				data.x < 1 + menuItems[ i ].offsetX + itemOuterWidth
			) {
				inBounds = true ;

				if ( selectedIndex !== i ) {
					selectedIndex = i ;
					redrawItem( oldSelectedIndex ) ;
					redrawItem( selectedIndex ) ;
					redrawCursor() ;
				}

				break ;
			}
		}

		if ( inBounds && name === 'MOUSE_LEFT_BUTTON_PRESSED' ) {
			cleanup() ;
		}
	} ;


	this.getCursorLocation( ( error , x , y ) => {
		if ( error ) {
			// Some bad terminals (windows...) doesn't support cursor location request, we should fallback to a decent behavior.
			// So we just move to the last line and create a new line.
			//cleanup( error ) ; return ;
			this.row.eraseLineAfter( this.height )( '\n' ) ;
			x = 1 ;
			y = this.height ;
		}

		start.x = x ;
		start.y = y ;

		var extra = start.y + rows - this.height ;

		if ( extra > 0 ) {
			// create extra lines
			this( '\n'.repeat( extra ) ) ;
			start.y -= extra ;
		}

		redraw() ;

		this.on( 'key' , onKey ) ;
		if ( this.mouseGrabbing ) { this.on( 'mouse' , onMouse ) ; }
	} ) ;

	// For compatibility
	var controller = {} ;

	controller.promise = new Promise() ;

	return controller ;
} ;


},{"seventh":114}],46:[function(require,module,exports){
(function (global){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const ndarray = require( 'ndarray' ) ;
const Promise = require( 'seventh' ) ;



const image = {} ;
module.exports = image ;



var getPixels ;
if ( global.IS_BROWSER ) { getPixels = require( '@cronvel/get-pixels' ) ; }



image.load = function load( fn , filepath , options , callback ) {
	if ( ! getPixels ) {
		// It loads some rarely useful files, do it only at last minute
		try {
			// Try the original get-pixels first, if available
			getPixels = require( 'get-pixels' ) ;
		}
		catch ( error ) {
			getPixels = require( '@cronvel/get-pixels' ) ;
		}
	}

	if ( typeof options === 'function' ) { callback = options ; options = {} ; }
	else if ( ! options || typeof options !== 'object' ) { options = {} ; }

	var promise = new Promise() ;

	getPixels( filepath , ( error_ , pixels ) => {

		if ( error_ ) {
			var error = new Error( 'Bad image URL: ' + error_ ) ;
			error.code = error_.code ;
			error.parent = error_ ;

			if ( callback ) { callback( error ) ; }
			else { promise.reject( error ) ; }

			return ;
		}

		if ( pixels.shape.length === 4 ) {
			// Probably a GIF or a format having animation,
			// we just extract the first image: a 3D array
			pixels = pixels.pick( 0 , null , null , null ) ;
		}

		if ( options.shrink ) { pixels = image.shrinkNdarrayImage( pixels , options.shrink ) ; }

		var result = fn( pixels , options ) ;

		if ( callback ) { callback( undefined , result ) ; }
		else { promise.resolve( result ) ; }
	} ) ;

	return promise ;
} ;



// Naive interpolation
image.shrinkNdarrayImage = function shrinkNdarrayImage( pixels , options ) {
	var rate = Math.min( options.width / pixels.shape[ 0 ] , options.height / pixels.shape[ 1 ] ) ;
	if ( rate >= 1 ) { return pixels ; }

	var dstWidth = Math.ceil( pixels.shape[ 0 ] * rate ) ;
	var dstHeight = Math.ceil( pixels.shape[ 1 ] * rate ) ;

	var xDst , yDst , xSrc , xSrcMin , xSrcMax , ySrc , ySrcMin , ySrcMax ,
		r , g , b , a , count ,
		hasAlpha = pixels.shape[ 2 ] === 4 ;

	var shrinkedPixels = ndarray(
		new Uint8Array( dstWidth * dstHeight * pixels.shape[ 2 ] ) ,
		[ dstWidth , dstHeight , pixels.shape[ 2 ] ]
	) ;

	for ( xDst = 0 ; xDst < dstWidth ; xDst ++ ) {
		for ( yDst = 0 ; yDst < dstHeight ; yDst ++ ) {
			xSrcMin = Math.ceil( xDst / rate ) ;
			xSrcMax = Math.min( Math.ceil( ( xDst + 1 ) / rate - 1 ) , pixels.shape[ 0 ] - 1 ) ;
			ySrcMin = Math.ceil( yDst / rate ) ;
			ySrcMax = Math.min( Math.ceil( ( yDst + 1 ) / rate - 1 ) , pixels.shape[ 1 ] - 1 ) ;
			count = r = g = b = a = 0 ;

			for ( xSrc = xSrcMin ; xSrc <= xSrcMax ; xSrc ++ ) {
				for ( ySrc = ySrcMin ; ySrc <= ySrcMax ; ySrc ++ ) {
					r += pixels.get( xSrc , ySrc , 0 ) ;
					g += pixels.get( xSrc , ySrc , 1 ) ;
					b += pixels.get( xSrc , ySrc , 2 ) ;
					if ( hasAlpha ) { a += pixels.get( xSrc , ySrc , 3 ) ; }
					count ++ ;
				}
			}

			shrinkedPixels.set( xDst , yDst , 0 , Math.round( r / count ) ) ;
			shrinkedPixels.set( xDst , yDst , 1 , Math.round( g / count ) ) ;
			shrinkedPixels.set( xDst , yDst , 2 , Math.round( b / count ) ) ;
			if ( hasAlpha ) { shrinkedPixels.set( xDst , yDst , 3 , Math.round( a / count ) ) ; }
		}
	}

	return shrinkedPixels ;
} ;


}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"@cronvel/get-pixels":64,"get-pixels":148,"ndarray":77,"seventh":114}],47:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const NextGenEvents = require( 'nextgen-events' ) ;
const Promise = require( 'seventh' ) ;
const string = require( 'string-kit' ) ;
const autoComplete = require( './autoComplete.js' ) ;



const defaultKeyBindings = {
	ENTER: 'submit' ,
	KP_ENTER: 'submit' ,
	ESCAPE: 'cancel' ,
	BACKSPACE: 'backDelete' ,
	DELETE: 'delete' ,
	LEFT: 'backward' ,
	RIGHT: 'forward' ,
	UP: 'historyPrevious' ,
	DOWN: 'historyNext' ,
	HOME: 'startOfInput' ,
	END: 'endOfInput' ,
	TAB: 'autoComplete' ,
	CTRL_R: 'autoCompleteUsingHistory' ,
	CTRL_LEFT: 'previousWord' ,
	CTRL_RIGHT: 'nextWord' ,
	ALT_D: 'deleteNextWord' ,
	CTRL_W: 'deletePreviousWord' ,
	CTRL_U: 'deleteAllBefore' ,
	CTRL_K: 'deleteAllAfter'
} ;



const defaultTokenRegExp = /\S+/g ;



/*
	inputField( [options] , callback )
		* options `Object` where:
			* y `number` the line where the input field will start (default to the current cursor location)
			* x `number` the column where the input field will start (default to the current cursor location,
			  or 1 if the *y* option is defined)
			* echo `boolean` if true (the default), input are displayed on the terminal
			* echoChar `string` or `true` if set, all characters are replaced by this one (useful for password fields),
			  if true, it is replaced by a dot: •
			* default `string` default input/placeholder
			* cursorPosition `integer` (default: -1, end of input) set the cursor position/offset in the input,
			  negative value starts from the end
			* cancelable `boolean` if true (default: false), it is cancelable by user using the cancel key (default: ESC),
			  thus will return undefined
			* style `Function` style used, default to the terminal instance (no style)
			* hintStyle `Function` style used for hint (auto-completion preview), default to `terminal.brightBlack` (gray)
			* maxLength `number` maximum length of the input
			* minLength `number` minimum length of the input
			* history `Array` (optional) an history array, so UP and DOWN keys move up and down in the history
			* autoComplete `Array` or `Function( inputString , [callback] )` (optional) an array of possible completion,
			  so the TAB key will auto-complete the input field. If it is a function, it should accept an input `string`
			  and return the completed `string` (if no completion can be done, it should return the input string,
			  if multiple candidate are possible, it should return an array of string), if **the function accepts 2 arguments**
			  (checked using *function*.length), then **the auto-completer will be asynchronous**!
			  If it does not accept a callback but returns a *thenable* (Promise-like), it will be asynchronous too.
				/!\ Also, if autoCompleteMenu is set and the array contains a special property 'prefix', it will be prepended
				after autoCompleteMenu()!
			* autoCompleteMenu `boolean` or `Object` of options, used in conjunction with the 'autoComplete' options, if *truthy*
			  any auto-complete attempt having many completion candidates will display a menu to let the user choose between each
			  possibilities. If an object is given, it should contain options for the [.singleLineMenu()](#ref.singleLineMenu)
			  that is used for the completion (notice: some options are overwritten: 'y' and 'exitOnUnexpectedKey')
			* autoCompleteHint `boolean` if true (default: false) use the hintStyle to write the auto-completion preview
			  at the right of the input
			* keyBindings `Object` overide default key bindings
			* tokenHook `Function( token , isEndOfInput , previousTokens , term , config )` this is a hook called for each
			  token of the input, where:
				* token `String` is the current token
				* isEndOfInput `boolean` true if this is the **last token and if it ends the input string**
				  (e.g. it is possible for the last token to be followed by some blank char, in that case *isEndOfInput*
				  would be false)
				* previousTokens `Array` of `String` is a array containing all tokens before the current one
				* term is a Terminal instance
				* config `Object` is an object containing dynamic settings that can be altered by the hook, where:
					* style `Function` style in use (see the *style* option)
					* hintStyle `Function` style in use for hint (see the *hintStyle* option)
					* tokenRegExp `RegExp` the regexp in use for tokenization (see the *tokenRegExp* option)
					* autoComplete `Array` or `Function( inputString , [callback] )` (see the *autoComplete* option)
					* autoCompleteMenu `boolean` or `Object` (see the *autoCompleteMenu* option)
					* autoCompleteHint `boolean` enable/disable the auto-completion preview (see the *autoCompleteHint* option)
			  The config settings are always reset on new input, on new tokenization pass.
			  The hook can return a *style* (`Function`, like the *style* option) that will be used to print that token.
			  Used together, this can achieve syntax hilighting, as well as dynamic behavior suitable for a shell.
			  Finally, it can return a string, styled or not, that will be displayed in place of that token,
			  useful if the token should have multiple styles (but that string **MUST** contains the same number of
			  printable character, or it would lead hazardous behavior).
			* tokenResetHook `Function( term , config )` this is a hook called before the first token
			* tokenRegExp `RegExp` this is the regex used to tokenize the input, by default a token is space-delimited,
			  so "one two three" would be tokenized as [ "one" , "two" , "three" ].
		* callback( error , input )
			* error `mixed` truthy if an underlying error occurs
			* input `string` the user input
*/
module.exports = function inputField( options , callback ) {
	if ( typeof options === 'function' ) { callback = options ; options = {} ; }
	else if ( ! options || typeof options !== 'object' ) { options = {} ; }

	if ( options.echo === undefined ) { options.echo = true ; }

	if ( typeof options.maxLength !== 'number' ) { options.maxLength = Infinity ; }
	if ( typeof options.minLength !== 'number' ) { options.minLength = 0 ; }

	if ( options.echoChar && typeof options.echoChar !== 'string' ) { options.echoChar = '•' ; }

	if ( options.autoCompleteMenu ) {
		if ( typeof options.autoCompleteMenu !== 'object' ) { options.autoCompleteMenu = {} ; }
		options.autoCompleteMenu.exitOnUnexpectedKey = true ;
		delete options.autoCompleteMenu.y ;
	}

	var keyBindings = options.keyBindings || defaultKeyBindings ;

	if ( options.tokenRegExp && ( ! ( options.tokenRegExp instanceof RegExp ) || ! options.tokenRegExp.flags.includes( 'g' ) ) ) {
		throw new Error( ".inputField(): if set, the 'tokenRegExp' option should be a RegExp with the 'g' flag" ) ;
	}

	if ( ! this.grabbing ) { this.grabInput() ; }



	var controller , finished = false , paused = false , alreadyCleanedUp = false ,
		offset = options.cursorPosition !== undefined ? options.cursorPosition : -1 ,
		echo = !! options.echo ,
		start = {} , end = {} , cursor = {} , endHint = {} ,
		inputs = [] , inputIndex ,
		alwaysRedraw = options.tokenHook || options.autoCompleteHint ,
		hint = [] , meta = false ;

	var dynamic = {
		style: options.style || this ,
		hintStyle: options.hintStyle || this.brightBlack ,
		tokenRegExp: options.tokenRegExp || defaultTokenRegExp ,
		autoComplete: options.autoComplete ,
		autoCompleteMenu: options.autoCompleteMenu ,
		autoCompleteHint: !! options.autoCompleteHint
	} ;



	// Now inputs is an array of input, input being an array of char (thanks to JS using UCS-2 instead of UTF-8)

	if ( Array.isArray( options.history ) ) {
		inputs = options.history.map( str => string.unicode.toArray( str ).slice( 0 , options.maxLength ) ) ;
	}


	if ( options.default && typeof options.default === 'string' ) {
		inputs.push( string.unicode.toArray( options.default ).slice( 0 , options.maxLength ) ) ;
	}
	else {
		inputs.push( [] ) ;
	}



	var init = () => {
		inputIndex = inputs.length - 1 ;
		offset = boundOffset( offset ) ;

		if ( options.y !== undefined ) {
			options.x = options.x || 1 ;
			this.moveTo.eraseLineAfter( options.x , options.y ) ;
			finishInit( options.x , options.y ) ;
		}
		else {
			// Get the cursor location before getting started
			this.getCursorLocation( ( error , x , y ) => {
				if ( error ) {
					// Some bad terminals (windows...) doesn't support cursor location request, we should fallback to a decent behavior.
					// So we just move to the last line, create a new line, and add a little prompt (it would be misleading otherwise).
					//cleanup( error ) ; return ;
					this.row.eraseLineAfter( this.height )( '\n> ' ) ;
					x = 3 ;
					y = this.height ;
				}

				finishInit( x , y ) ;
			} ) ;
		}
	} ;



	var finishInit = ( x , y ) => {
		start.x = end.x = cursor.x = x ;
		start.y = end.y = cursor.y = y ;

		if ( inputs[ inputIndex ].length ) {
			// There is already something (placeholder, ...), so redraw now!
			computeAllCoordinate() ;
			redraw() ;
		}

		this.on( 'key' , onKey ) ;
		//controller.ready = true ;
		controller.emit( 'ready' ) ;
	} ;



	var cleanup = ( error , input ) => {
		if ( alreadyCleanedUp ) { return ; }
		alreadyCleanedUp = true ;

		finished = true ;
		this.removeListener( 'key' , onKey ) ;

		if ( error === 'abort' ) { return ; }

		this.styleReset() ;

		if ( error ) {
			if ( callback ) { callback( error ) ; }
			else { controller.promise.reject( error ) ; }
			return ;
		}

		var value ;

		if ( typeof input === 'string' ) { value = input ; }
		else if ( input ) { value = input.join( '' ) ; }

		if ( callback ) { callback( undefined , value ) ; }
		else { controller.promise.resolve( value ) ; }
	} ;



	// Compute the coordinate of the cursor and end of a string, given a start coordinate
	var computeAllCoordinate = () => {
		var scroll ,
			inputWidth = string.unicode.arrayWidth( inputs[ inputIndex ] ) ,
			hintWidth = string.unicode.arrayWidth( hint ) ;

		end = offsetCoordinate( inputWidth ) ;
		endHint = offsetCoordinate( inputWidth + hintWidth ) ;

		if ( endHint.y > this.height ) {
			// We have gone out of the screen, scroll!
			scroll = endHint.y - this.height ;

			dynamic.style.noFormat( '\n'.repeat( scroll ) ) ;

			start.y -= scroll ;
			end.y -= scroll ;
			endHint.y -= scroll ;
		}

		cursorCoordinate() ;
	} ;



	// Cursor coordinate from the current offset
	var cursorCoordinate = () => {
		cursor = offsetCoordinate( string.unicode.arrayWidth( inputs[ inputIndex ] , offset ) ) ;
	} ;



	// Compute the coordinate of an offset, given a start coordinate
	var offsetCoordinate = offset_ => {
		return {
			x: 1 + ( start.x + offset_ - 1 ) % this.width ,
			y: start.y + Math.floor( ( start.x + offset_ - 1 ) / this.width )
		} ;
	} ;



	var boundOffset = offset_ => {
		if ( typeof offset_ !== 'number' || isNaN( offset_ ) ) { return inputs[ inputIndex ].length ; }

		if ( offset_ < 0 ) { offset_ = inputs[ inputIndex ].length + 1 + offset_ ; }

		if ( offset_ < 0 ) { offset_ = 0 ; }
		else if ( offset_ >= inputs[ inputIndex ].length ) { offset_ = inputs[ inputIndex ].length ; }

		return offset_ ;
	} ;



	// Compute the coordinate of the end of a string, given a start coordinate
	var redraw = ( extraLines , forceClear ) => {
		var i , hintCleared ;

		extraLines = extraLines || 0 ;

		if ( ! dynamic.autoCompleteHint && forceClear ) {
			// Used by history, when autoCompleteHint is off, the current line is not erased
			this.moveTo( end.x , end.y ) ;
			dynamic.style.noFormat.eraseLineAfter( '' ) ;
		}

		this.moveTo( start.x , start.y ) ;

		if ( options.tokenHook ) { writeTokens( inputs[ inputIndex ].join( '' ) ) ; }
		else if ( options.echoChar ) { dynamic.style.noFormat( options.echoChar.repeat( inputs[ inputIndex ].length ) ) ; }
		else { dynamic.style.noFormat( inputs[ inputIndex ].join( '' ) ) ; }

		hintCleared = clearHint() ;

		if ( extraLines > 0 ) {
			// If the previous input was using more lines, erase them now
			for ( i = 1 ; i <= extraLines ; i ++ ) {
				this.moveTo( 1 , end.y + i ) ;
				dynamic.style.noFormat.eraseLineAfter( '' ) ;
			}
		}

		if ( ! hintCleared && ( cursor.y < end.y || end.x === this.width ) ) {
			this.moveTo( end.x , end.y ) ;
			dynamic.style.noFormat.eraseLineAfter( '' ) ;
		}

		this.moveTo( cursor.x , cursor.y ) ;
	} ;



	// Not used internally for instance, only for controller.redrawCursor()
	var redrawCursor = () => {
		if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , redrawCursor ) ; return ; }
		this.moveTo( cursor.x , cursor.y ) ;
	} ;



	var pause = () => {
		if ( paused ) { return ; }
		paused = true ;

		// Don't redraw now if not ready, it will be drawn once ready (avoid double-draw)
		//if ( controller.hasState( 'ready' ) ) { redraw() ; }
	} ;



	var resume = () => {
		if ( ! paused ) { return ; }
		paused = false ;

		// Don't redraw now if not ready, it will be drawn once ready (avoid double-draw)
		if ( controller.hasState( 'ready' ) ) { redraw() ; }
	} ;



	var clearHint = () => {
		// First, check if there are some hints to be cleared
		if ( ! dynamic.autoCompleteHint ) { return false ; }

		var y = end.y ;

		this.moveTo( end.x , end.y ) ;
		dynamic.style.noFormat.eraseLineAfter( '' ) ;

		// If the previous input was using more lines, erase them now
		while ( y < endHint.y ) {
			y ++ ;
			this.moveTo( 1 , y ) ;
			dynamic.style.noFormat.eraseLineAfter( '' ) ;
		}

		this.moveTo( cursor.x , cursor.y ) ;

		return true ;
	} ;



	// Compute the coordinate of the end of a string, given a start coordinate
	var autoCompleteMenu = ( menu ) => {
		paused = true ;

		this.singleLineMenu( menu , dynamic.autoCompleteMenu , ( error , response ) => {
			// Unpause unconditionnally
			paused = false ;
			if ( error ) { return ; }

			if ( response.selectedText ) {
				// Prepend something before the text
				if ( menu.prefix ) { response.selectedText = menu.prefix + response.selectedText ; }

				// Append something after the text
				if ( menu.postfix ) { response.selectedText += menu.postfix ; }

				response.selectedText = string.unicode.toArray( response.selectedText ).slice( 0 , options.maxLength ) ;

				inputs[ inputIndex ] = response.selectedText.concat(
					inputs[ inputIndex ].slice( offset , options.maxLength + offset - response.selectedText.length )
				) ;

				offset = response.selectedText.length ;
			}

			if ( echo ) {
				// Erase the menu
				this.column.eraseLineAfter( 1 ) ;

				// If the input field was ending on the last line, we need to move it one line up
				if ( end.y >= this.height && start.y > 1 ) { start.y -- ; }

				computeAllCoordinate() ;
				redraw() ;
				this.moveTo( cursor.x , cursor.y ) ;
			}

			if ( response.unexpectedKey && response.unexpectedKey !== 'TAB' ) {
				// Forward the key to the event handler
				onKey( response.unexpectedKey , undefined , response.unexpectedKeyData ) ;
			}
		} )
			.on( 'highlight' , eventData => controller.emit( 'highlight' ,  eventData ) ) ;
	} ;



	var writeTokens = ( text ) => {
		var match , lastIndex , lastEndIndex = 0 , tokens = [] , tokenStyle , isEndOfInput ;

		// Reset dynamic stuffs
		dynamic.style = options.style || this ;
		dynamic.hintStyle = options.hintStyle || this.brightBlack ;
		dynamic.tokenRegExp = options.tokenRegExp || defaultTokenRegExp ;
		dynamic.autoComplete = options.autoComplete ;
		dynamic.autoCompleteMenu = options.autoCompleteMenu ;
		dynamic.autoCompleteHint = !! options.autoCompleteHint ;

		dynamic.tokenRegExp.lastIndex = 0 ;

		if ( options.tokenResetHook ) { options.tokenResetHook( this , dynamic ) ; }

		while ( ( match = dynamic.tokenRegExp.exec( text ) ) !== null ) {
			// Back-up that now, since it can be modified by the hook
			lastIndex = dynamic.tokenRegExp.lastIndex ;

			if ( match.index > lastEndIndex ) { dynamic.style.noFormat( text.slice( lastEndIndex , match.index ) ) ; }

			isEndOfInput = match.index + match[ 0 ].length === text.length ;

			tokenStyle = options.tokenHook( match[ 0 ] , isEndOfInput , tokens , this , dynamic ) ;

			if ( typeof tokenStyle === 'function' ) { tokenStyle.noFormat( match[ 0 ] ) ; }
			else if ( typeof tokenStyle === 'string' ) { this.noFormat( tokenStyle ) ; }
			else { dynamic.style.noFormat( match[ 0 ] ) ; }

			tokens.push( match[ 0 ] ) ;

			lastEndIndex = match.index + match[ 0 ].length ;

			// Restore it, if it was modified
			dynamic.tokenRegExp.lastIndex = lastIndex ;
		}

		if ( lastEndIndex < text.length ) { dynamic.style.noFormat( text.slice( lastEndIndex ) ) ; }
	} ;



	var autoCompleteHint = () => {
		// The cursor should be at the end ATM
		if ( ! dynamic.autoComplete || ! dynamic.autoCompleteHint || offset < inputs[ inputIndex ].length ) {
			return ;
		}

		var autoCompleted , inputText = inputs[ inputIndex ].join( '' ) ;

		var finishCompletion = () => {
			if ( Array.isArray( autoCompleted ) ) { return ; }

			hint = string.unicode.toArray( autoCompleted.slice( inputText.length ) )
				.slice( 0 , options.maxLength - inputs[ inputIndex ].length ) ;

			computeAllCoordinate() ;
			this.moveTo( end.x , end.y ) ;	// computeAllCoordinate() can add some newline
			dynamic.hintStyle.noFormat( hint.join( '' ) ) ;
			this.moveTo( cursor.x , cursor.y ) ;
		} ;

		if ( Array.isArray( dynamic.autoComplete ) ) {
			autoCompleted = autoComplete( dynamic.autoComplete , inputText , dynamic.autoCompleteMenu ) ;
		}
		else if ( typeof dynamic.autoComplete === 'function' ) {
			if ( dynamic.autoComplete.length === 2 ) {
				dynamic.autoComplete( inputText , ( error , autoCompleted_ ) => {
					if ( error ) { cleanup( error ) ; return ; }

					autoCompleted = autoCompleted_ ;
					finishCompletion() ;
				} ) ;
				return ;
			}

			autoCompleted = dynamic.autoComplete( inputText ) ;

			if ( Promise.isThenable( autoCompleted ) ) {
				autoCompleted.then(
					autoCompleted_ => {
						autoCompleted = autoCompleted_ ;
						finishCompletion() ;
					} ,
					error => { cleanup( error ) ; }
				) ;
				return ;
			}
		}

		finishCompletion() ;
	} ;



	// The main method: the key event handler
	var onKey = ( key , trash , data ) => {

		if ( finished || paused ) { return ; }

		var leftPart , autoCompleteUsed , autoCompleted , extraLines , charToDelete , cutOffset , altKey ,
			lastOffset = offset ;

		// if previous keystroke triggered the 'meta' keybinding, prepend ALT_ to this key
		if ( meta ) {
			meta = false ;
			altKey = 'ALT_' + key.toUpperCase() ;

			if ( data ) { data.isCharacter = false ; }
			if ( keyBindings[ altKey ] ) { key = altKey ; }
		}

		if ( data && data.isCharacter ) {
			// if data.isCharacter, this is a regular UTF-8 character, not a special key

			if ( inputs[ inputIndex ].length >= options.maxLength ) { return ; }

			// Insert version
			//inputs[ inputIndex ] = inputs[ inputIndex ].slice( 0 , offset ) + key + inputs[ inputIndex ].slice( offset ) ;
			inputs[ inputIndex ].splice( offset , 0 , key ) ;
			offset ++ ;

			if ( echo ) {
				if ( offset === inputs[ inputIndex ].length && ! alwaysRedraw ) {
					dynamic.style.noFormat( options.echoChar || key ) ;
					// Now it's done by computeAllCoordinate()
					//if ( cursor.x >= this.width ) { dynamic.style.noFormat( '\n' ) ; }
					computeAllCoordinate() ;
				}
				else {
					// redraw() is mandatory in insert mode
					computeAllCoordinate() ;
					redraw() ;
					if ( dynamic.autoCompleteHint ) { autoCompleteHint() ; }
				}
			}
		}
		else {
			// Here we have a special key

			switch( keyBindings[ key ] ) {
				case 'submit' :
					if ( inputs[ inputIndex ].length < options.minLength ) { break ; }
					clearHint() ;
					cleanup( undefined , inputs[ inputIndex ] ) ;
					break ;

				case 'cancel' :
					if ( options.cancelable ) { cleanup() ; }
					break ;

				case 'meta' :
					meta = true ;
					break ;

				case 'backDelete' :
					if ( inputs[ inputIndex ].length && offset > 0 ) {
						charToDelete = inputs[ inputIndex ][ offset - 1 ] ;
						inputs[ inputIndex ].splice( offset - 1 , 1 ) ;
						offset -- ;

						if ( echo ) {
							// The cursor position check should happen BEFORE we modify it with computeAllCoordinate()
							if ( cursor.y < end.y || cursor.x === 1 || alwaysRedraw ) {
								computeAllCoordinate() ;
								// Every time something is deleted, we need a redraw with the forceClear option on
								redraw( undefined , true ) ;
								if ( dynamic.autoCompleteHint ) { autoCompleteHint() ; }
							}
							else {
								computeAllCoordinate() ;

								// .backDelete() does not work with full-width, Terminal Kit should stop using it until a reliable escape sequence combo is found
								//this.backDelete() ;
								if ( string.unicode.isFullWidth( charToDelete ) ) {
									this.left( 2 ) ;
									this.delete( 2 ) ;
								}
								else {
									this.left( 1 ) ;
									this.delete( 1 ) ;
								}
							}
						}
					}
					break ;

				case 'delete' :
					if ( inputs[ inputIndex ].length && offset < inputs[ inputIndex ].length ) {
						charToDelete = inputs[ inputIndex ][ offset ] ;
						inputs[ inputIndex ].splice( offset , 1 ) ;

						if ( echo ) {
							// The cursor position check should happen BEFORE we modify it with computeAllCoordinate()
							if ( cursor.y < end.y || alwaysRedraw ) {
								computeAllCoordinate() ;
								// Every time something is deleted, we need a redraw with the forceClear option on
								redraw( undefined , true ) ;
								if ( dynamic.autoCompleteHint ) { autoCompleteHint() ; }
							}
							else {
								computeAllCoordinate() ;
								this.delete( string.unicode.isFullWidth( charToDelete ) ? 2 : 1 ) ;
							}
						}
					}
					break ;

				case 'deleteAllBefore' :
					if ( inputs[ inputIndex ].length && offset > 0 ) {
						//inputs[ inputIndex ] = inputs[ inputIndex ].slice( 0 , offset - 1 ) + inputs[ inputIndex ].slice( offset ) ;
						inputs[ inputIndex ].splice( 0 , offset ) ;
						offset = 0 ;

						if ( echo ) {
							computeAllCoordinate() ;
							// Need forceClear
							redraw( undefined , true ) ;
						}
					}
					break ;

				case 'deleteAllAfter' :
					if ( inputs[ inputIndex ].length && offset < inputs[ inputIndex ].length ) {
						inputs[ inputIndex ].splice( offset , inputs[ inputIndex ].length - offset ) ;

						if ( echo ) {
							computeAllCoordinate() ;
							// Need forceClear
							redraw( undefined , true ) ;
							if ( dynamic.autoCompleteHint ) { autoCompleteHint() ; }
						}
					}
					break ;

				case 'backward' :
					if ( inputs[ inputIndex ].length && offset > 0 ) {
						if ( dynamic.autoCompleteHint && offset === inputs[ inputIndex ].length ) {
							clearHint() ;
						}

						offset -- ;

						if ( echo ) {
							computeAllCoordinate() ;
							this.moveTo( cursor.x , cursor.y ) ;
						}
					}
					break ;

				case 'forward' :
					if ( inputs[ inputIndex ].length && offset < inputs[ inputIndex ].length ) {
						offset ++ ;

						if ( echo ) {
							computeAllCoordinate() ;
							this.moveTo( cursor.x , cursor.y ) ;
						}

						if ( dynamic.autoCompleteHint && offset === inputs[ inputIndex ].length ) {
							autoCompleteHint() ;
						}
					}

					break ;

				case 'deletePreviousWord' :
					if ( inputs[ inputIndex ].length && offset > 0 ) {
						if ( dynamic.autoCompleteHint && offset === inputs[ inputIndex ].length ) {
							clearHint() ;
						}

						cutOffset = offset -- ;

						while ( offset > 0 && inputs[ inputIndex ][ offset ] === ' ' ) { offset -- ; }
						while ( offset > 0 && inputs[ inputIndex ][ offset - 1 ] !== ' ' ) { offset -- ; }

						inputs[ inputIndex ].splice( offset , cutOffset - offset ) ;

						if ( echo ) {
							computeAllCoordinate() ;
							this.moveTo( cursor.x , cursor.y ) ;
							redraw( undefined , true ) ;
						}
					}
					break ;

				case 'deleteNextWord' :
					if ( inputs[ inputIndex ].length && offset < inputs[ inputIndex ].length ) {
						cutOffset = offset ;

						while ( offset < inputs[ inputIndex ].length && inputs[ inputIndex ][ offset ] === ' ' ) { offset ++ ; }
						while ( offset < inputs[ inputIndex ].length && inputs[ inputIndex ][ offset ] !== ' ' ) { offset ++ ; }
						while ( offset < inputs[ inputIndex ].length && inputs[ inputIndex ][ offset ] === ' ' ) { offset ++ ; }

						inputs[ inputIndex ].splice( cutOffset , offset - cutOffset ) ;
						offset = Math.min( inputs[ inputIndex ].length , cutOffset ) ;

						if ( echo ) {
							computeAllCoordinate() ;
							this.moveTo( cursor.x , cursor.y ) ;
							redraw( undefined , true ) ;
						}

						if ( dynamic.autoCompleteHint && offset === inputs[ inputIndex ].length ) {
							autoCompleteHint() ;
						}
					}
					break ;

				case 'previousWord' :
					if ( inputs[ inputIndex ].length && offset > 0 ) {
						if ( dynamic.autoCompleteHint && offset === inputs[ inputIndex ].length ) {
							clearHint() ;
						}

						offset -- ;

						while ( offset > 0 && inputs[ inputIndex ][ offset ] === ' ' ) { offset -- ; }
						while ( offset > 0 && inputs[ inputIndex ][ offset - 1 ] !== ' ' ) { offset -- ; }

						if ( echo ) {
							computeAllCoordinate() ;
							this.moveTo( cursor.x , cursor.y ) ;
						}
					}
					break ;

				case 'nextWord' :
					if ( inputs[ inputIndex ].length && offset < inputs[ inputIndex ].length ) {
						while ( offset < inputs[ inputIndex ].length && inputs[ inputIndex ][ offset ] === ' ' ) { offset ++ ; }
						while ( offset < inputs[ inputIndex ].length && inputs[ inputIndex ][ offset ] !== ' ' ) { offset ++ ; }

						if ( echo ) {
							computeAllCoordinate() ;
							this.moveTo( cursor.x , cursor.y ) ;
						}

						if ( dynamic.autoCompleteHint && offset === inputs[ inputIndex ].length ) {
							autoCompleteHint() ;
						}
					}

					break ;

				case 'startOfInput' :
					if ( dynamic.autoCompleteHint && offset === inputs[ inputIndex ].length ) {
						clearHint() ;
					}

					offset = 0 ;

					if ( echo ) {
						computeAllCoordinate() ;
						this.moveTo( cursor.x , cursor.y ) ;
					}
					break ;

				case 'endOfInput' :
					offset = inputs[ inputIndex ].length ;

					if ( echo ) {
						computeAllCoordinate() ;
						this.moveTo( cursor.x , cursor.y ) ;
					}

					if ( dynamic.autoCompleteHint && lastOffset !== inputs[ inputIndex ].length ) {
						autoCompleteHint() ;
					}

					break ;

				case 'historyNext' :
					if ( inputIndex < inputs.length - 1 ) {
						inputIndex ++ ;
						offset = inputs[ inputIndex ].length ;

						if ( echo ) {
							extraLines = end.y - start.y ;
							computeAllCoordinate() ;
							extraLines -= end.y - start.y ;
							redraw( extraLines , true ) ;
							this.moveTo( cursor.x , cursor.y ) ;
						}

						// Not sure if this is desirable
						//if ( dynamic.autoCompleteHint ) { autoCompleteHint() ; }
					}
					break ;

				case 'historyPrevious' :
					if ( inputIndex > 0 ) {
						inputIndex -- ;
						offset = inputs[ inputIndex ].length ;

						if ( echo ) {
							extraLines = end.y - start.y ;
							computeAllCoordinate() ;
							extraLines -= end.y - start.y ;
							redraw( extraLines , true ) ;
							this.moveTo( cursor.x , cursor.y ) ;
						}

						// Not sure if this is desirable
						//if ( dynamic.autoCompleteHint ) { autoCompleteHint() ; }
					}
					break ;

				case 'autoCompleteUsingHistory' :
				case 'autoComplete' :
					autoCompleteUsed = keyBindings[ key ] === 'autoCompleteUsingHistory' ? options.history : dynamic.autoComplete ;

					if ( ! autoCompleteUsed ) { break ; }

					leftPart = inputs[ inputIndex ].slice( 0 , offset ) ;

					var finishCompletion = () => {
						if ( Array.isArray( autoCompleted ) ) {
							if ( dynamic.autoCompleteMenu ) { autoCompleteMenu( autoCompleted ) ; }
							return ;
						}

						leftPart = string.unicode.toArray( autoCompleted ).slice( 0 , options.maxLength ) ;

						inputs[ inputIndex ] = leftPart.concat(
							inputs[ inputIndex ].slice( offset , options.maxLength + offset - leftPart.length )
						) ;

						offset = leftPart.length ;

						if ( echo ) {
							computeAllCoordinate() ;
							redraw() ;
						}
					} ;

					if ( Array.isArray( autoCompleteUsed ) ) {
						autoCompleted = autoComplete( autoCompleteUsed , leftPart.join( '' ) , dynamic.autoCompleteMenu ) ;
					}
					else if ( typeof autoCompleteUsed === 'function' ) {
						if ( autoCompleteUsed.length === 2 ) {
							autoCompleteUsed( leftPart.join( '' ) , ( error , autoCompleted_ ) => {
								if ( error ) { cleanup( error ) ; return ; }

								autoCompleted = autoCompleted_ ;
								finishCompletion() ;
							} ) ;
							return ;
						}

						autoCompleted = autoCompleteUsed( leftPart.join( '' ) ) ;

						if ( Promise.isThenable( autoCompleted ) ) {
							autoCompleted.then(
								autoCompleted_ => {
									autoCompleted = autoCompleted_ ;
									finishCompletion() ;
								} ,
								error => { cleanup( error ) ; }
							) ;
							return ;
						}
					}

					finishCompletion() ;

					break ;
			}
		}
	} ;


	// Return a controller for the input field

	controller = Object.create( NextGenEvents.prototype ) ;

	controller.defineStates( 'ready' ) ;

	// /!\ .ready is deprecated, it is now a getter to .hasState('ready')
	Object.defineProperty( controller , 'ready' , {
		get: function() { return this.hasState( 'ready' ) ; }
	} ) ;

	// Tmp, for compatibility
	controller.widgetType = 'inputField' ;

	// Stop everything and do not even call the callback
	controller.abort = () => {
		if ( finished ) { return ; }
		cleanup( 'abort' ) ;
	} ;

	// Stop and call the completion callback with the current input
	controller.stop = () => {
		if ( finished ) { return ; }
		cleanup( undefined , inputs[ inputIndex ] ) ;
	} ;

	// Pause and resume: the input field will not respond to event when paused
	controller.pause = pause ;
	controller.resume = resume ;
	controller.focus = ( value ) => {
		if ( value ) { resume() ; }
		else { pause() ; }
	} ;

	// Get the current input
	controller.getInput = () => inputs[ inputIndex ].join( '' ) ;

	controller.value = controller.getInput ;

	// Get the current position
	controller.getPosition = () => ( { x: start.x , y: start.y } ) ;

	// Hide the input field
	controller.hide = () => {
		if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , controller.hide ) ; return ; }

		var i , j ;

		for ( i = start.x , j = start.y ; j <= end.y ; i = 1 , j ++ ) {
			this.moveTo.eraseLineAfter( i , j ) ;
		}

		echo = false ;
	} ;

	// Show the input field
	controller.show = () => {
		if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , controller.show ) ; return ; }
		echo = true ;
		redraw() ;
	} ;

	// Redraw the input field
	controller.redraw = () => {
		if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , controller.redraw ) ; return ; }
		redraw( undefined , true ) ;
	} ;

	// Redraw the cursor
	controller.redrawCursor = () => {
		if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , controller.redrawCursor ) ; return ; }
		redrawCursor() ;
	} ;

	controller.getCursorPosition = () => offset ;

	controller.setCursorPosition = newOffset => {
		newOffset = boundOffset( newOffset ) ;

		if ( newOffset !== offset ) {
			if ( dynamic.autoCompleteHint && offset === inputs[ inputIndex ].length ) {
				clearHint() ;
			}

			offset = newOffset ;

			if ( echo ) {
				computeAllCoordinate() ;
				this.moveTo( cursor.x , cursor.y ) ;
			}

			if ( dynamic.autoCompleteHint && offset === inputs[ inputIndex ].length ) {
				autoCompleteHint() ;
			}
		}
	} ;

	// Rebase the input field where the cursor is
	controller.rebase = ( x , y ) => {
		if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , controller.rebase ) ; return ; }

		var rebaseTo = ( x_ , y_ ) => {

			start.x = x_ ;
			start.y = y_ ;

			if ( options.echo ) {
				echo = true ;
				computeAllCoordinate() ;
				redraw() ;
			}

			controller.emit( 'rebased' ) ;
		} ;

		if ( x !== undefined && y !== undefined ) {
			rebaseTo( x , y ) ;
			return ;
		}

		// First, disable echoing: getCursorLocation is async!
		echo = false ;

		this.getCursorLocation( ( error , x_ , y_ ) => {
			if ( error ) {
				// Some bad terminals (windows...) doesn't support cursor location request, we should fallback to a decent behavior.
				// Here we just ignore the rebase.
				//cleanup( error ) ;
				return ;
			}

			rebaseTo( x_ , y_ ) ;
		} ) ;
	} ;

	controller.promise = new Promise() ;


	// Init the input field
	init() ;

	return controller ;
} ;


},{"./autoComplete.js":7,"nextgen-events":78,"seventh":114,"string-kit":133}],48:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const string = require( 'string-kit' ) ;



const colorNameToIndexDict = {
	// ANSI
	black: 0 ,
	red: 1 ,
	green: 2 ,
	yellow: 3 ,
	blue: 4 ,
	magenta: 5 ,
	violet: 5 ,
	cyan: 6 ,
	white: 7 ,
	grey: 8 ,
	gray: 8 ,
	brightblack: 8 ,
	brightred: 9 ,
	brightgreen: 10 ,
	brightyellow: 11 ,
	brightblue: 12 ,
	brightmagenta: 13 ,
	brightviolet: 13 ,
	brightcyan: 14 ,
	brightwhite: 15
} ;



// Color name to index
exports.colorNameToIndex = color => colorNameToIndexDict[ color.toLowerCase() ] ;



const indexToColorNameArray = [
	"black" , "red" , "green" , "yellow" , "blue" , "magenta" , "cyan" , "white" ,
	"gray" , "brightRed" , "brightGreen" , "brightYellow" , "brightBlue" , "brightMagenta" , "brightCyan" , "brightWhite"
] ;



// Color name to index
exports.indexToColorName = index => indexToColorNameArray[ index ] ;



exports.hexToRgba = hex => {
	// Strip the # if necessary
	if ( hex[ 0 ] === '#' ) { hex = hex.slice( 1 ) ; }

	if ( hex.length === 3 ) {
		hex = hex[ 0 ] + hex[ 0 ] + hex[ 1 ] + hex[ 1 ] + hex[ 2 ] + hex[ 2 ] ;
	}

	return {
		r: parseInt( hex.slice( 0 , 2 ) , 16 ) || 0 ,
		g: parseInt( hex.slice( 2 , 4 ) , 16 ) || 0 ,
		b: parseInt( hex.slice( 4 , 6 ) , 16 ) || 0 ,
		a: hex.length > 6 ? parseInt( hex.slice( 6 , 8 ) , 16 ) || 0 : 255
	} ;
} ;



// DEPRECATED function names
exports.color2index = exports.colorNameToIndex ;
exports.index2color = exports.indexToColorName ;
exports.hexToColor = exports.hexToRgba ;



// Strip all control chars, if newline is true, only newline control chars are preserved
exports.stripControlChars = ( str , newline ) => {
	if ( newline ) { return str.replace( /[\x00-\x09\x0b-\x1f\x7f]/g , '' ) ; }
	return str.replace( /[\x00-\x1f\x7f]/g , '' ) ;
} ;



// From https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings
const escapeSequenceRegex       = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g ;
const escapeSequenceParserRegex = /([\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><])|([^\u001b\u009b]+)/g ;



exports.stripEscapeSequences = str => str.replace( escapeSequenceRegex , '' ) ;



// Return the real width of the string (i.e. as displayed in the terminal)
exports.ansiWidth =
exports.stringWidth = str => {
	var matches , width = 0 ;

	// Reset
	escapeSequenceParserRegex.lastIndex = 0 ;

	while ( ( matches = escapeSequenceParserRegex.exec( str ) ) ) {
		if ( matches[ 2 ] ) {
			width += string.unicode.width( matches[ 2 ] ) ;
		}
	}

	return width ;
} ;



// Userland may use this, it is more efficient than .truncateString() + .stringWidth(),
// and BTW even more than testing .stringWidth() then .truncateString() + .stringWidth()
var lastTruncateWidth = 0 ;
exports.getLastTruncateWidth = () => lastTruncateWidth ;



// Truncate a string to a given real width
exports.truncateAnsiString =
exports.truncateString = ( str , maxWidth ) => {
	var matches , width = 0 ;

	lastTruncateWidth = 0 ;

	// Reset
	escapeSequenceParserRegex.lastIndex = 0 ;

	while ( ( matches = escapeSequenceParserRegex.exec( str ) ) ) {
		if ( matches[ 2 ] ) {
			width += string.unicode.width( matches[ 2 ] ) ;

			if ( width >= maxWidth ) {
				if ( width === maxWidth ) {
					return str.slice( 0 , matches.index + matches[ 2 ].length ) ;
				}

				return str.slice( 0 , matches.index ) + string.unicode.truncateWidth( matches[ 2 ] , maxWidth - lastTruncateWidth ) ;
			}

			lastTruncateWidth = width ;
		}
	}

	return str ;
} ;



// Width of a string with a markup, without control chars
exports.markupWidth = str => {
	// Fix a possible ReDoS, the regex:   /\^\[[^\]]*]|\^(.)/g   was replaced by:   /\^\[[^\]]*]?|\^(.)/g
	// The exploit was possible with a string like: '^['.repeat(bigNumber)
	return string.unicode.width( str.replace( /\^\[[^\]]*]?|\^(.)/g , ( match , second ) => {
		if ( second === ' ' || second === '^' ) {
			return second ;
		}

		return '' ;
	} ) ) ;
} ;



// Truncate a string to a given real width, the string may contains markup, but no control chars
exports.truncateMarkupString = ( str , maxWidth ) => {
	var index = 0 , charWidth ,
		strArray = string.unicode.toArray( str ) ;

	lastTruncateWidth = 0 ;

	while ( index < strArray.length ) {
		if ( strArray[ index ] === '^' ) {
			index ++ ;

			if ( strArray[ index ] === '[' ) {
				while ( index < strArray.length && strArray[ index ] !== ']' ) { index ++ ; }
				index ++ ;
				continue ;
			}

			if ( strArray[ index ] !== ' ' && strArray[ index ] !== '^' ) {
				index ++ ;
				continue ;
			}
		}

		charWidth = string.unicode.isFullWidth( strArray[ index ] ) ? 2 : 1 ;

		if ( lastTruncateWidth + charWidth > maxWidth ) {
			strArray.length = index ;
			return strArray.join( '' ) ;
		}

		lastTruncateWidth += charWidth ;
		index ++ ;
	}

	return str ;
} ;



// Function used for sequenceSkip option of string-kit's .wordwrap()
// TODO: many issues remaining
exports.escapeSequenceSkipFn = ( strArray , index ) => {
	//console.error( '>>> Entering' ) ;
	var code ;

	if ( strArray[ index ] !== '\x1b' ) { return index ; }
	index ++ ;
	if ( strArray[ index ] !== '[' ) { return index ; }
	index ++ ;

	for ( ; index < strArray.length ; index ++ ) {
		code = strArray[ index ].charCodeAt( 0 ) ;
		//console.error( 'code:' , strArray[ index ] , code.toString( 16 ) ) ;

		if ( ( code >= 0x41 && code <= 0x5a ) || ( code >= 0x61 && code <= 0x7a ) ) {
			//console.error( "<<< break!" ) ;
			index ++ ;
			break ;
		}
	}

	return index ;
} ;



exports.wordWrapAnsi = ( str , width ) => string.wordwrap( str , {
	width: width ,
	noJoin: true ,
	fill: true ,
	regroupFn: strArray => {
		var sequence = '' ,
			csi = false ,
			newStrArray = [] ;

		strArray.forEach( char => {
			var charCode ;

			if ( csi ) {
				sequence += char ;
				charCode = char.charCodeAt( 0 ) ;

				if ( ( charCode >= 0x41 && charCode <= 0x5a ) || ( charCode >= 0x61 && charCode <= 0x7a ) ) {
					newStrArray.push( sequence ) ;
					sequence = '' ;
					csi = false ;
				}
			}
			else if ( sequence ) {
				sequence += char ;

				if ( char === '[' ) {
					csi = true ;
				}
				else {
					newStrArray.push( sequence ) ;
					sequence = '' ;
				}
			}
			else if ( char === '\x1b' ) {
				sequence = char ;
			}
			else {
				newStrArray.push( char ) ;
			}
		} ) ;

		return newStrArray ;
	} ,
	charWidthFn: char => {
		if ( char[ 0 ] === '\x1b' ) { return 0 ; }
		return string.unicode.charWidth( char ) ;
	}
} ) ;



exports.wordwrapMarkup =	// <-- DEPRECATED name (bad camel case)
exports.wordWrapMarkup = ( str , width ) => string.wordwrap( str , {
	width: width ,
	noJoin: true ,
	fill: true ,
	regroupFn: strArray => {
		var markup = '' ,
			complexMarkup = false ,
			newStrArray = [] ;

		strArray.forEach( char => {
			if ( complexMarkup ) {
				markup += char ;

				if ( char === ']' ) {
					newStrArray.push( markup ) ;
					markup = '' ;
					complexMarkup = false ;
				}
			}
			else if ( markup ) {
				markup += char ;

				if ( char === '[' ) {
					complexMarkup = true ;
				}
				else {
					newStrArray.push( markup ) ;
					markup = '' ;
				}
			}
			else if ( char === '^' ) {
				markup = char ;
			}
			else {
				newStrArray.push( char ) ;
			}
		} ) ;

		return newStrArray ;
	} ,
	charWidthFn: char => {
		if ( char[ 0 ] === '^' && char[ 1 ] ) {
			if ( char[ 1 ] === '^' || char[ 1 ] === ' ' ) { return 1 ; }
			return 0 ;
		}

		return string.unicode.charWidth( char ) ;
	}
} ) ;



exports.preserveMarkupFormat = string.createFormatter( {
	argumentSanitizer: str => str.replace( /[\x00-\x1f\x7f^]/g , char => char === '^' ? '^^' : '' ) ,
	noMarkup: true
} ) ;



// Catch-all keywords to key:value
const CATCH_ALL_KEYWORDS = {
	// Foreground colors
	defaultColor: [ 'color' , 'default' ] ,
	black: [ 'color' , 'black' ] ,
	red: [ 'color' , 'red' ] ,
	green: [ 'color' , 'green' ] ,
	yellow: [ 'color' , 'yellow' ] ,
	blue: [ 'color' , 'blue' ] ,
	magenta: [ 'color' , 'magenta' ] ,
	cyan: [ 'color' , 'cyan' ] ,
	white: [ 'color' , 'white' ] ,
	grey: [ 'color' , 'grey' ] ,
	gray: [ 'color' , 'gray' ] ,
	brightBlack: [ 'color' , 'brightBlack' ] ,
	brightRed: [ 'color' , 'brightRed' ] ,
	brightGreen: [ 'color' , 'brightGreen' ] ,
	brightYellow: [ 'color' , 'brightYellow' ] ,
	brightBlue: [ 'color' , 'brightBlue' ] ,
	brightMagenta: [ 'color' , 'brightMagenta' ] ,
	brightCyan: [ 'color' , 'brightCyan' ] ,
	brightWhite: [ 'color' , 'brightWhite' ] ,

	// Background colors
	defaultBgColor: [ 'bgColor' , 'default' ] ,
	bgBlack: [ 'bgColor' , 'black' ] ,
	bgRed: [ 'bgColor' , 'red' ] ,
	bgGreen: [ 'bgColor' , 'green' ] ,
	bgYellow: [ 'bgColor' , 'yellow' ] ,
	bgBlue: [ 'bgColor' , 'blue' ] ,
	bgMagenta: [ 'bgColor' , 'magenta' ] ,
	bgCyan: [ 'bgColor' , 'cyan' ] ,
	bgWhite: [ 'bgColor' , 'white' ] ,
	bgGrey: [ 'bgColor' , 'grey' ] ,
	bgGray: [ 'bgColor' , 'gray' ] ,
	bgBrightBlack: [ 'bgColor' , 'brightBlack' ] ,
	bgBrightRed: [ 'bgColor' , 'brightRed' ] ,
	bgBrightGreen: [ 'bgColor' , 'brightGreen' ] ,
	bgBrightYellow: [ 'bgColor' , 'brightYellow' ] ,
	bgBrightBlue: [ 'bgColor' , 'brightBlue' ] ,
	bgBrightMagenta: [ 'bgColor' , 'brightMagenta' ] ,
	bgBrightCyan: [ 'bgColor' , 'brightCyan' ] ,
	bgBrightWhite: [ 'bgColor' , 'brightWhite' ] ,

	// Other styles
	dim: [ 'dim' , true ] ,
	bold: [ 'bold' , true ] ,
	underline: [ 'underline' , true ] ,
	italic: [ 'italic' , true ] ,
	inverse: [ 'inverse' , true ]
} ;

exports.markupCatchAllKeywords = CATCH_ALL_KEYWORDS ;



exports.markupOptions = {
	parse: true ,
	shiftMarkup: {
		'#': 'background'
	} ,
	markup: {
		':': null ,
		' ': markupStack => {
			markupStack.length = 0 ;
			return [ null , ' ' ] ;
		} ,
		';': markupStack => {
			markupStack.length = 0 ;
			return [ null , { specialReset: true } ] ;
		} ,

		'-': { dim: true } ,
		'+': { bold: true } ,
		'_': { underline: true } ,
		'/': { italic: true } ,
		'!': { inverse: true } ,

		'k': { color: 0 } ,
		'r': { color: 1 } ,
		'g': { color: 2 } ,
		'y': { color: 3 } ,
		'b': { color: 4 } ,
		'm': { color: 5 } ,
		'c': { color: 6 } ,
		'w': { color: 7 } ,
		'K': { color: 8 } ,
		'R': { color: 9 } ,
		'G': { color: 10 } ,
		'Y': { color: 11 } ,
		'B': { color: 12 } ,
		'M': { color: 13 } ,
		'C': { color: 14 } ,
		'W': { color: 15 }
	} ,
	shiftedMarkup: {
		background: {
			':': [ null , { defaultColor: true , bgDefaultColor: true } ] ,
			' ': markupStack => {
				markupStack.length = 0 ;
				return [ null , { defaultColor: true , bgDefaultColor: true } , ' ' ] ;
			} ,
			';': markupStack => {
				markupStack.length = 0 ;
				return [ null , { specialReset: true , defaultColor: true , bgDefaultColor: true } ] ;
			} ,

			'k': { bgColor: 0 } ,
			'r': { bgColor: 1 } ,
			'g': { bgColor: 2 } ,
			'y': { bgColor: 3 } ,
			'b': { bgColor: 4 } ,
			'm': { bgColor: 5 } ,
			'c': { bgColor: 6 } ,
			'w': { bgColor: 7 } ,
			'K': { bgColor: 8 } ,
			'R': { bgColor: 9 } ,
			'G': { bgColor: 10 } ,
			'Y': { bgColor: 11 } ,
			'B': { bgColor: 12 } ,
			'M': { bgColor: 13 } ,
			'C': { bgColor: 14 } ,
			'W': { bgColor: 15 }
		}
	} ,
	dataMarkup: {
		color: 'color' ,
		fgColor: 'color' ,
		fg: 'color' ,
		c: 'color' ,
		bgColor: 'bgColor' ,
		bg: 'bgColor'
	} ,
	markupCatchAll: ( markupStack , key , value ) => {
		var attr = {} ;

		if ( value === undefined ) {
			if ( key[ 0 ] === '#' ) {
				attr.color = key ;
			}
			else if ( CATCH_ALL_KEYWORDS[ key ] ) {
				attr[ CATCH_ALL_KEYWORDS[ key ][ 0 ] ] = CATCH_ALL_KEYWORDS[ key ][ 1 ] ;
			}
			else {
				// Fallback: it's a foreground color
				attr.color = key ;
			}
		}

		markupStack.push( attr ) ;
		return attr || {} ;
	}
} ;



const asciiSymbolName = {
	' ': 'SPACE' ,
	'!': 'EXCLAMATION' ,
	'"': 'DOUBLE_QUOTE' ,
	'#': 'HASH' ,
	'$': 'DOLLAR' ,
	'%': 'PERCENT' ,
	'&': 'AMPERSAND' ,
	"'": 'SINGLE_QUOTE' ,
	'(': 'OPEN_PARENTHESIS' ,
	')': 'CLOSE_PARENTHESIS' ,
	'*': 'ASTERISK' ,
	'+': 'PLUS' ,
	',': 'COMMA' ,
	'-': 'HYPHEN' ,
	'.': 'DOT' ,
	'/': 'SLASH' ,
	':': 'COLON' ,
	';': 'SEMICOLON' ,
	'<': 'LESS_THAN' ,
	'=': 'EQUAL' ,
	'>': 'GREATER_THAN' ,
	'?': 'QUESTION' ,
	'@': 'AT' ,
	'[': 'OPEN_BRACKET' ,
	'\\': 'BACKSLASH' ,
	']': 'CLOSE_BRACKET' ,
	'^': 'CARET' ,
	'_': 'UNDERSCORE' ,
	'`': 'BACK_QUOTE' ,
	'{': 'OPEN_BRACE' ,
	'|': 'PIPE' ,
	'}': 'CLOSE_BRACE' ,
	'~': 'TILDE'
} ;

// Non-control character name
exports.characterName = char => {
	if ( asciiSymbolName[ char ] ) { return asciiSymbolName[ char ] ; }
	return char.toUpperCase() ;
} ;

// Transform a Terminal-Kit Key code (like CTRL_C) to user-friendly/interface name (like Ctrl-C)
exports.keyToUserInterfaceName = key => {
	if ( asciiSymbolName[ key ] ) { return asciiSymbolName[ key ] ; }

	return key.replace(
		/([A-Za-z0-9])([A-Za-z0-9]*)|([_-]+)/g ,
		( match , firstLetter , endOfWord , separator ) => {
			if ( separator ) { return '-' ; }
			return firstLetter.toUpperCase() + endOfWord.toLowerCase() ;
		}
	) ;
} ;

// Transform a user-friendly/interface name (like Ctrl-C) to a Terminal-Kit Key code (like CTRL_C)
exports.userInterfaceNameToKey = key => {
	return key.replace(
		/([A-Za-z0-9]+)|([_-]+)/g ,
		( match , word , separator ) => {
			if ( separator ) { return '_' ; }
			return word.toUpperCase() ;
		}
	) ;
} ;


},{"string-kit":133}],49:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Promise = require( 'seventh' ) ;

// Patch the child process module to support asyncness
Promise.promisifyNodeApi( require( 'child_process' ) ) ;


},{"child_process":148,"seventh":114}],50:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



//var string = require( 'string-kit' ) ;



/*
	progressBar( options )
		* options `object` of options, all of them are OPTIONAL, where:
			* width: `number` the total width of the progress bar, default to the max available width
			* percent: `boolean` if true, it shows the progress in percent alongside with the progress bar
			* eta: `boolean` if true, it shows the Estimated Time of Arrival alongside with the progress bar
			* items `number` the number of items, turns the 'item mode' on
			* title `string` the title of the current progress bar, turns the 'title mode' on
			* barStyle `function` the style of the progress bar items, default to `term.cyan`
			* barBracketStyle `function` the style of the progress bar bracket character, default to options.barStyle if given
			  or `term.blue`
			* percentStyle `function` the style of percent value string, default to `term.yellow`
			* etaStyle `function` the style of the ETA display, default to `term.bold`
			* itemStyle `function` the style of the item display, default to `term.dim`
			* titleStyle `function` the style of the title display, default to `term.bold`
			* itemSize `number` the size of the item status, default to 33% of width
			* titleSize `number` the size of the title, default to 33% of width or title.length depending on context
			* barChar `string` the char used for the bar, default to '='
			* barHeadChar `string` the char used for the bar, default to '>'
			* maxRefreshTime `number` the maximum time between two refresh in ms, default to 500ms
			* minRefreshTime `number` the minimum time between two refresh in ms, default to 100ms
			* inline `boolean` (default: false) if true it is not locked in place, i.e. it redraws itself on the current line
			* syncMode `boolean` true if it should work in sync mode
			* y `integer` if set, display the progressBar on that line y-coord
			* x `integer` if set and the 'y' option is set, display the progressBar starting on that x-coord
*/
module.exports = function progressBar_( options ) {
	if ( ! options || typeof options !== 'object' ) { options = {} ; }

	var controller = {} , progress , ready = false , pause = false ,
		maxItems , itemsDone = 0 , itemsStarted = [] , itemFiller ,
		title , titleFiller ,
		width , y , startX , endX , oldWidth ,
		wheel , wheelCounter = 0 , itemRollCounter = 0 ,
		updateCount = 0 , progressUpdateCount = 0 ,
		lastUpdateTime , lastRedrawTime ,
		startingTime , redrawTimer ,
		etaStartingTime , lastEta , etaFiller ;

	etaStartingTime = startingTime = ( new Date() ).getTime() ;

	wheel = [ '|' , '/' , '-' , '\\' ] ;

	options.syncMode = !! options.syncMode ;

	width = options.width || this.width - 1 ;

	if ( ! options.barBracketStyle ) {
		if ( options.barStyle ) { options.barBracketStyle = options.barStyle ; }
		else { options.barBracketStyle = this.blue ; }
	}

	if ( ! options.barStyle ) { options.barStyle = this.cyan ; }
	if ( ! options.percentStyle ) { options.percentStyle = this.yellow ; }
	if ( ! options.etaStyle ) { options.etaStyle = this.bold ; }
	if ( ! options.itemStyle ) { options.itemStyle = this.dim ; }
	if ( ! options.titleStyle ) { options.titleStyle = this.bold ; }

	if ( ! options.barChar ) { options.barChar = '=' ; }
	else { options.barChar = options.barChar[ 0 ] ; }

	if ( ! options.barHeadChar ) { options.barHeadChar = '>' ; }
	else { options.barHeadChar = options.barHeadChar[ 0 ] ; }

	if ( typeof options.maxRefreshTime !== 'number' ) { options.maxRefreshTime = 500 ; }
	if ( typeof options.minRefreshTime !== 'number' ) { options.minRefreshTime = 100 ; }

	if ( typeof options.items === 'number' ) { maxItems = options.items ; }
	if ( maxItems && typeof options.itemSize !== 'number' ) { options.itemSize = Math.round( width / 3 ) ; }

	itemFiller = ' '.repeat( options.itemSize ) ;


	if ( options.title && typeof options.title === 'string' ) {
		title = options.title ;

		if ( typeof options.titleSize !== 'number' ) {
			options.titleSize = Math.round( Math.min( options.title.length + 1 , width / 3 ) ) ;
		}
	}

	titleFiller = ' '.repeat( options.titleSize ) ;


	etaFiller = '           ' ;	// 11 chars

	// This is a naive ETA for instance...
	var etaString = updated => {
		var eta = '' , elapsedTime , elapsedEtaTime , remainingTime ,
			averageUpdateDelay , averageUpdateProgress , lastUpdateElapsedTime , fakeProgress ;

		if ( progress >= 1 ) {
			eta = ' done' ;
		}
		else if ( progress > 0 ) {
			elapsedTime = ( new Date() ).getTime() - startingTime ;
			elapsedEtaTime = ( new Date() ).getTime() - etaStartingTime ;

			if ( ! updated && progressUpdateCount > 1 ) {
				lastUpdateElapsedTime = ( new Date() ).getTime() - lastUpdateTime ;
				averageUpdateDelay = elapsedEtaTime / progressUpdateCount ;
				averageUpdateProgress = progress / progressUpdateCount ;

				//console.log( '\n' , elapsedEtaTime , lastUpdateElapsedTime , averageUpdateDelay , averageUpdateProgress , '\n' ) ;

				// Do not update ETA if it's not an update, except if update time is too long
				if ( lastUpdateElapsedTime < averageUpdateDelay ) {
					fakeProgress = progress + averageUpdateProgress * lastUpdateElapsedTime / averageUpdateDelay ;
				}
				else {
					fakeProgress = progress + averageUpdateProgress ;
				}

				if ( fakeProgress > 0.99 ) { fakeProgress = 0.99 ; }
			}
			else {
				fakeProgress = progress ;
			}

			remainingTime = elapsedEtaTime * ( ( 1 - fakeProgress ) / fakeProgress ) / 1000 ;

			eta = ' in ' ;

			if ( remainingTime < 10 ) { eta += Math.round( remainingTime * 10 ) / 10 + 's' ; }
			else if ( remainingTime < 120 ) { eta += Math.round( remainingTime ) + 's' ; }
			else if ( remainingTime < 7200 ) { eta += Math.round( remainingTime / 60 ) + 'min' ; }
			else if ( remainingTime < 172800 ) { eta += Math.round( remainingTime / 3600 ) + 'hours' ; }
			else if ( remainingTime < 31536000 ) { eta += Math.round( remainingTime / 86400 ) + 'days' ; }
			else { eta = 'few years' ; }
		}
		else {
			etaStartingTime = ( new Date() ).getTime() ;
		}

		eta = ( eta + etaFiller ).slice( 0 , etaFiller.length ) ;
		lastEta = eta ;

		return eta ;
	} ;



	var redraw = updated => {
		var time , itemIndex , itemName = itemFiller , titleName = titleFiller ,
			innerBarSize , progressSize , voidSize ,
			progressBar = '' , voidBar = '' , percent = '' , eta = '' ;

		if ( ! ready || pause ) { return ; }

		time = ( new Date() ).getTime() ;

		// If progress is >= 1, then it's finished, so we should redraw NOW (before the program eventually quit)
		if ( ( ! progress || progress < 1 ) && lastRedrawTime && time < lastRedrawTime + options.minRefreshTime ) {
			if ( ! options.syncMode ) {
				if ( redrawTimer ) { clearTimeout( redrawTimer ) ; }
				redrawTimer = setTimeout( redraw.bind( this , updated ) , lastRedrawTime + options.minRefreshTime - time ) ;
			}
			return ;
		}


		this.saveCursor() ;

		// If 'y' is null, we are in the blind mode, we haven't get the cursor location
		if ( y === null ) { this.column( startX ) ; }
		else { this.moveTo( startX , y ) ; }

		//this.noFormat( Math.floor( progress * 100 ) + '%' ) ;

		innerBarSize = width - 2 ;

		if ( options.percent ) {
			innerBarSize -= 4 ;
			percent = ( '   ' + Math.round( ( progress || 0 ) * 100 ) + '%' ).slice( -4 ) ;
		}

		if ( options.eta ) {
			eta = etaString( updated ) ;
			innerBarSize -= eta.length ;
		}

		innerBarSize -= options.itemSize || 0 ;
		if ( maxItems ) {
			if ( ! itemsStarted.length ) {
				itemName = '' ;
			}
			else if ( itemsStarted.length === 1 ) {
				itemName = ' ' + itemsStarted[ 0 ] ;
			}
			else {
				itemIndex = ( itemRollCounter ++ ) % itemsStarted.length ;
				itemName = ' [' + ( itemIndex + 1 ) + '/' + itemsStarted.length + '] ' + itemsStarted[ itemIndex ] ;
			}

			if ( itemName.length > itemFiller.length ) { itemName = itemName.slice( 0 , itemFiller.length - 1 ) + '…' ; }
			else if ( itemName.length < itemFiller.length ) { itemName = ( itemName + itemFiller ).slice( 0 , itemFiller.length ) ; }
		}

		innerBarSize -= options.titleSize || 0 ;
		if ( title ) {
			titleName = title ;

			if ( titleName.length >= titleFiller.length ) { titleName = titleName.slice( 0 , titleFiller.length - 2 ) + '… ' ; }
			else { titleName = ( titleName + titleFiller ).slice( 0 , titleFiller.length ) ; }
		}

		progressSize = progress === undefined ? 1 : Math.round( innerBarSize * Math.max( Math.min( progress , 1 ) , 0 ) ) ;
		voidSize = innerBarSize - progressSize ;

		/*
		console.log( "Size:" , width ,
			voidSize , innerBarSize , progressSize , eta.length , title.length , itemName.length ,
			voidSize + progressSize + eta.length + title.length + itemName.length
		) ;
		//*/

		if ( progressSize ) {
			if ( progress === undefined ) {
				progressBar = wheel[ ++ wheelCounter % wheel.length ] ;
			}
			else {
				progressBar += options.barChar.repeat( progressSize - 1 ) ;
				progressBar += options.barHeadChar ;
			}
		}

		voidBar += ' '.repeat( voidSize ) ;

		options.titleStyle( titleName ) ;

		if ( percent ) { options.percentStyle( percent ) ; }

		if ( progress === undefined ) { this( ' ' ) ; }
		else { options.barBracketStyle( '[' ) ; }

		options.barStyle( progressBar ) ;
		this( voidBar ) ;

		if ( progress === undefined ) { this( ' ' ) ; /*this( '+' ) ;*/ }
		else { options.barBracketStyle( ']' ) ; }

		options.etaStyle( eta ) ;
		//this( '*' ) ;
		options.itemStyle( itemName ) ;
		//this( '&' ) ;

		this.restoreCursor() ;

		if ( ! options.syncMode ) {
			if ( redrawTimer ) { clearTimeout( redrawTimer ) ; }
			if ( ! progress || progress < 1 ) { redrawTimer = setTimeout( redraw , options.maxRefreshTime ) ; }
		}

		lastRedrawTime = time ;
	} ;


	if ( options.syncMode || options.inline || options.y ) {
		oldWidth = width ;

		if ( options.y ) {
			startX = + options.x || 1 ;
			y = + options.y || 1 ;
		}
		else {
			startX = 1 ;
			y = null ;
		}

		endX = Math.min( startX + width , this.width ) ;
		width = endX - startX ;

		if ( width !== oldWidth ) {
			// Should resize all part here
			if ( options.titleSize ) { options.titleSize = Math.floor( options.titleSize * width / oldWidth ) ; }
			if ( options.itemSize ) { options.itemSize = Math.floor( options.itemSize * width / oldWidth ) ; }
		}

		ready = true ;
		redraw() ;
	}
	else {
		// Get the cursor location before getting started
		this.getCursorLocation( ( error , x_ , y_ ) => {
			if ( error ) {
				// Some bad terminals (windows...) doesn't support cursor location request, we should fallback to a decent behavior.
				// So we just move to the last line and create a new line.
				//cleanup( error ) ; return ;
				this.row.eraseLineAfter( this.height )( '\n' ) ;
				x_ = 1 ;
				y_ = this.height ;
			}

			var oldWidth_ = width ;

			startX = x_ ;
			endX = Math.min( x_ + width , this.width ) ;
			y = y_ ;
			width = endX - startX ;

			if ( width !== oldWidth_ ) {
				// Should resize all part here
				if ( options.titleSize ) { options.titleSize = Math.floor( options.titleSize * width / oldWidth_ ) ; }
				if ( options.itemSize ) { options.itemSize = Math.floor( options.itemSize * width / oldWidth_ ) ; }
			}

			ready = true ;
			redraw() ;
		} ) ;
	}

	controller.startItem = name => {
		itemsStarted.push( name ) ;

		// No need to redraw NOW if there are other items running.
		// Let the timer do the job.
		if ( itemsStarted.length === 1 ) {
			// If progress is >= 1, then it's finished, so we should redraw NOW (before the program eventually quit)
			if ( progress >= 1 ) { redraw() ; return ; }

			if ( options.syncMode ) {
				redraw() ;
			}
			else {
				// Using a setTimeout with a 0ms time and redrawTimer clearing has a nice effect:
				// if multiple synchronous update are performed, redraw will be called once
				if ( redrawTimer ) { clearTimeout( redrawTimer ) ; }
				redrawTimer = setTimeout( redraw , 0 ) ;
			}
		}
	} ;

	controller.itemDone = name => {
		var index ;

		itemsDone ++ ;

		if ( maxItems ) { progress = itemsDone / maxItems ; }
		else { progress = undefined ; }

		lastUpdateTime = ( new Date() ).getTime() ;
		updateCount ++ ;
		progressUpdateCount ++ ;

		index = itemsStarted.indexOf( name ) ;
		if ( index >= 0 ) { itemsStarted.splice( index , 1 ) ; }

		// If progress is >= 1, then it's finished, so we should redraw NOW (before the program eventually quit)
		if ( progress >= 1 ) { redraw( true ) ; return ; }

		if ( options.syncMode ) {
			redraw() ;
		}
		else {
			// Using a setTimeout with a 0ms time and redrawTimer clearing has a nice effect:
			// if multiple synchronous update are performed, redraw will be called once
			if ( redrawTimer ) { clearTimeout( redrawTimer ) ; }
			redrawTimer = setTimeout( redraw.bind( this , true ) , 0 ) ;
		}
	} ;

	controller.update = toUpdate => {
		if ( ! toUpdate ) { toUpdate = {} ; }
		else if ( typeof toUpdate === 'number' ) { toUpdate = { progress: toUpdate } ; }

		if ( 'progress' in toUpdate ) {
			if ( typeof toUpdate.progress !== 'number' ) {
				progress = undefined ;
			}
			else {
				// Not sure if it is a good thing to let the user set progress to a value that is lesser than the current one
				progress = toUpdate.progress ;

				if ( progress > 1 ) { progress = 1 ; }
				else if ( progress < 0 ) { progress = 0 ; }

				if ( progress > 0 ) { progressUpdateCount ++ ; }

				lastUpdateTime = ( new Date() ).getTime() ;
				updateCount ++ ;
			}
		}

		if ( typeof toUpdate.items === 'number' ) {
			maxItems = toUpdate.items ;
			if ( maxItems ) { progress = itemsDone / maxItems ; }

			if ( typeof options.itemSize !== 'number' ) {
				options.itemSize = Math.round( width / 3 ) ;
				itemFiller = ' '.repeat( options.itemSize ) ;
			}
		}

		if ( typeof toUpdate.title === 'string' ) {
			title = toUpdate.title ;

			if ( typeof options.titleSize !== 'number' ) {
				options.titleSize = Math.round( width / 3 ) ;
				titleFiller = ' '.repeat( options.titleSize ) ;
			}
		}

		// If progress is >= 1, then it's finished, so we should redraw NOW (before the program eventually quit)
		if ( progress >= 1 ) { redraw( true ) ; return ; }

		if ( options.syncMode ) {
			redraw() ;
		}
		else {
			// Using a setTimeout with a 0ms time and redrawTimer clearing has a nice effect:
			// if multiple synchronous update are performed, redraw will be called once
			if ( redrawTimer ) { clearTimeout( redrawTimer ) ; }
			redrawTimer = setTimeout( redraw.bind( this , true ) , 0 ) ;
		}
	} ;

	controller.pause = controller.stop = () => {
		pause = true ;
	} ;

	controller.resume = () => {
		if ( pause ) {
			pause = false ;
			redraw() ;
		}
	} ;

	controller.reset = () => {
		etaStartingTime = startingTime = ( new Date() ).getTime() ;
		itemsDone = 0 ;
		progress = undefined ;
		itemsStarted.length = 0 ;
		wheelCounter = itemRollCounter = updateCount = progressUpdateCount = 0 ;
		redraw() ;
	} ;

	return controller ;
} ;


},{}],51:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const termkit = require( './termkit' ) ;
const stringWidth = termkit.stringWidth ;
const string = require( 'string-kit' ) ;
const NextGenEvents = require( 'nextgen-events' ) ;
const Promise = require( 'seventh' ) ;



const defaultKeyBindings = {
	ENTER: 'submit' ,
	KP_ENTER: 'submit' ,
	UP: 'previous' ,
	DOWN: 'next' ,
	TAB: 'cycleNext' ,
	SHIFT_TAB: 'cyclePrevious' ,
	HOME: 'first' ,
	END: 'last' ,
	BACKSPACE: 'cancel' ,
	DELETE: 'cancel' ,
	ESCAPE: 'escape'
} ;



/*
	singleColumnMenu( menuItems , [options] , callback )
		* menuItems `array` of menu item text
		* options `object` of options, where:
			* y `number` the line where the menu will be displayed, default to the next line
			* style `function` the style of unselected items, default to `term`
			* selectedStyle `function` the style of the selected item, default to `term.inverse`
			* submittedStyle `function` the style of the submitted item, default to `term.bgGray.bold`
			* disabledStyle `function` the style of unselected items **when the menu is paused/disabled**,
			  default to `term.dim`
			* disabledSelectedStyle `function` the style of the selected item **when the menu is paused/disabled**,
			  default to `term.bgGray.dim`
			* disabledSubmittedStyle `function` the style of the submitted item **when the menu is paused/disabled**,
			  default to `term.bgGray`
			* leftPadding `string` the text to put before a menu item, default to ' '
			* selectedLeftPadding `string` the text to put before a selected menu item, default to ' '
			* submittedLeftPadding `string` the text to put before a submitted menu item, default to ' '
			* extraLines `number` ensure that many lines after the bottom of the menu
			* oneLineItem `boolean` if true (default: false), big items do not span multiple lines, instead they are truncated
			  and ended with an ellipsis char
			* itemMaxWidth `number` the max width for an item, default to the terminal width
			* continueOnSubmit `boolean` if true, the submit action does not end the menu, the callback argument is ignored.
			  The 'submit' event should be listened instead.
			* selectedIndex `number` selected index at initialization (default: 0)
			* unsubmittableIndexes `Array` of `boolean` indexes that are not submittable (default: [])
			* submitted `boolean` if true, selected index is already submitted at initialization (default: false)
			* paused `boolean` (default: false) true if the menu start in paused/disabled mode
			* scrollRegionBottom `number` if set, it indicates the bottom line of the current scroll region
			* keyBindings `Object` overide default key bindings
			* cancelable `boolean` if ESCAPE is pressed, it exits, calling the callback with undefined values
			* exitOnUnexpectedKey `boolean` if an unexpected key is pressed, it exits, calling the callback with undefined values
		* callback( error , response ), where:
			* error
			* response `Object` where:
				* selectedIndex `number` the user-selected menu item index
				* selectedText `string` the user-selected menu item text
				* x `number` the x coordinate of the selected menu item (the first character)
				* y `number` the y coordinate of the selected menu item
				* unexpectedKey `string` when 'exitOnUnexpectedKey' option is set, this contains the key that produced the exit
				* canceled `bool` when 'cancelable' option is set, this is set to true
*/
module.exports = function singleColumnMenu( menuItemsArg , options , callback ) {
	if ( arguments.length < 1 ) { throw new Error( '[terminal] singleColumnMenu() needs at least an array of menuItems' ) ; }
	if ( ! Array.isArray( menuItemsArg ) || ! menuItemsArg.length ) { throw new TypeError( '[terminal] singleColumnMenu(): argument #0 should be a non-empty array' ) ; }

	if ( typeof options === 'function' ) { callback = options ; options = {} ; }
	else if ( ! options || typeof options !== 'object' ) { options = {} ; }

	if ( ! options.style ) { options.style = this ; }
	if ( ! options.selectedStyle ) { options.selectedStyle = this.inverse ; }
	if ( ! options.submittedStyle ) { options.submittedStyle = this.bgGray.bold ; }
	if ( ! options.disabledStyle ) { options.disabledStyle = this.dim ; }
	if ( ! options.disabledSelectedStyle ) { options.disabledSelectedStyle = this.bgGray.dim ; }
	if ( ! options.disabledSubmittedStyle ) { options.disabledSubmittedStyle = this.bgGray ; }

	if ( options.leftPadding === undefined ) { options.leftPadding = ' ' ; }
	if ( options.selectedLeftPadding === undefined ) { options.selectedLeftPadding = options.leftPadding ; }
	if ( options.submittedLeftPadding === undefined ) { options.submittedLeftPadding = options.leftPadding ; }

	if ( typeof options.extraLines !== 'number' || options.extraLines < 0 ) { options.extraLines = 1 ; }

	if ( ! options.itemMaxWidth ) { options.itemMaxWidth = this.width - 1 ; }

	if ( ! options.unsubmittableIndexes ) { options.unsubmittableIndexes = [] ; }

	var selectedIndex = options.selectedIndex || 0 ;
	var submittedIndex = options.submitted ? options.selectedIndex : null ;
	var paused = !! options.paused ;

	var keyBindings = options.keyBindings || defaultKeyBindings ;

	if ( ! this.grabbing ) { this.grabInput() ; }


	var start = {} , end = {} , textWidth , outerWidth , paddingLength ,
		menuItems , offsetY = 0 , lineCount = 0 , scrollLines = 0 ,
		controller , finished = false , alreadyCleanedUp = false ;



	// Functions...



	var init = () => {
		computeItems( menuItemsArg ) ;

		if ( options.y !== undefined ) {
			this.moveTo( 1 , options.y ) ;
			finishInit( 1 , options.y ) ;
		}
		else {
			this( '\n' ) ;
			this.getCursorLocation( ( error , x , y ) => {
				if ( error ) {
					// Some bad terminals (windows...) doesn't support cursor location request, we should fallback to a decent behavior.
					// So we just move to the last line and create a new line.
					//cleanup( error ) ; return ;
					this.row.eraseLineAfter( this.height )( '\n' ) ;
					x = 1 ;
					y = this.height ;
				}

				finishInit( x , y ) ;
			} ) ;
		}
	} ;



	var computeItems = ( menuItems_ ) => {
		textWidth = 0 ;

		paddingLength = Math.max( stringWidth( options.leftPadding ) , stringWidth( options.selectedLeftPadding ) ) ;

		menuItems_ = menuItems_.map( element => {
			if ( typeof element !== 'string' ) { element = '' + element ; }
			textWidth = Math.max( textWidth , stringWidth( element ) ) ;
			return element ;
		} ) ;

		if ( ! options.oneLineItem && textWidth > options.itemMaxWidth - paddingLength ) {
			outerWidth = Math.min( textWidth + paddingLength , this.width ) ;

			menuItems = menuItems_.map( ( element , index ) => {

				var item , lines ,
					lineLength = options.itemMaxWidth - paddingLength ;

				lines = string.wordwrap( element , {
					width: lineLength ,
					noJoin: true ,
					fill: true ,
					skipFn: termkit.escapeSequenceSkipFn
				} ) ;

				item = {
					offsetY: offsetY ,
					index: index ,
					text: element ,
					displayText: lines
				} ;

				offsetY += lines.length ;

				return item ;
			} ) ;

			lineCount = offsetY ;
		}
		else {
			textWidth = Math.min( textWidth , options.itemMaxWidth - paddingLength ) ;
			outerWidth = Math.min( textWidth + paddingLength , this.width ) ;

			menuItems = menuItems_.map( ( element , index ) => {
				var elementWidth = stringWidth( element ) ;

				return {
					offsetY: index ,
					index: index ,
					text: element ,
					displayText: [ elementWidth > textWidth ?
						element.slice( 0 , textWidth - 1 ) + '…' :
						element + ' '.repeat( textWidth - elementWidth ) ]
				} ;
			} ) ;

			lineCount = menuItems.length ;
		}
	} ;



	var finishInit = ( x , y ) => {
		// It is possible for userland to end the menu immediately
		if ( finished ) { return ; }

		prepareArea( x , y ) ;
		redraw() ;

		this.on( 'key' , onKey ) ;
		if ( this.mouseGrabbing ) { this.on( 'mouse' , onMouse ) ; }

		controller.emit( 'ready' ) ;
		emitHighlight() ;
	} ;



	var emitHighlight = () => {
		var item = menuItems[ selectedIndex ] ;

		controller.emit( 'highlight' , {
			highlightedIndex: item.index ,
			highlightedText: item.text ,
			submitted: submittedIndex !== null ,
			x: 1 ,
			y: start.y + item.offsetY
		} ) ;

	} ;



	var prepareArea = ( x , y ) => {
		start.x = x ;
		start.y = y ;

		end.x = 1 ;
		end.y = y + lineCount ;

		scrollLines = start.y + lineCount - ( options.scrollRegionBottom || this.height ) - 1 + options.extraLines ;

		if ( scrollLines > 0 ) {
			// create extra lines
			this( '\n'.repeat( scrollLines ) ) ;
			start.y -= scrollLines ;
			end.y -= scrollLines ;
		}
	} ;



	var cleanup = ( error , data , eraseMenu ) => {
		if ( alreadyCleanedUp ) { return ; }
		alreadyCleanedUp = true ;

		finished = true ;
		this.removeListener( 'key' , onKey ) ;
		this.removeListener( 'mouse' , onMouse ) ;

		if ( error === 'abort' ) { return ; }

		if ( controller.hasState( 'ready' ) ) {
			if ( eraseMenu ) { erase() ; }
			else { this.moveTo( 1 , end.y ) ; }
		}

		if ( error ) {
			if ( callback ) { callback( error ) ; }
			else { controller.promise.reject( error ) ; }
			return ;
		}

		var value = data !== undefined ? data : {
			selectedIndex: selectedIndex ,
			selectedText: menuItems[ selectedIndex ].text ,
			submitted: submittedIndex !== null ,
			x: 1 ,
			y: start.y + menuItems[ selectedIndex ].offsetY
		} ;

		if ( callback ) { callback( undefined , value ) ; }
		else { controller.promise.resolve( value ) ; }
	} ;



	var erase = () => {
		if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , erase ) ; return ; }

		var i , j ;

		for ( i = start.x , j = start.y ; j <= end.y ; i = 1 , j ++ ) {
			this.moveTo.eraseLineAfter( i , j ) ;
		}

		this.moveTo( 1 , start.y ) ;
	} ;



	// Compute the coordinate of the end of a string, given a start coordinate
	var redraw = () => {
		for ( var i = 0 ; i < menuItems.length ; i ++ ) { redrawItem( i ) ; }
		redrawCursor() ;
	} ;



	var redrawItem = ( index ) => {

		// Called by finishInit before emitting 'ready'
		//if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , redrawItem.bind( undefined , index ) ) ; return ; }

		var item = menuItems[ index ] ;

		item.displayText.forEach( ( text , line ) => {

			this.moveTo( 1 , start.y + item.offsetY + line ) ;

			if ( paused || options.unsubmittableIndexes[ index ] ) {
				if ( index === submittedIndex ) {
					if ( line ) { options.disabledSubmittedStyle.forceStyleOnReset.noFormat( options.leftPadding ) ; }
					else { options.disabledSubmittedStyle.forceStyleOnReset.noFormat( options.submittedLeftPadding ) ; }

					options.disabledSubmittedStyle.forceStyleOnReset.noFormat( text ) ;
				}
				else if ( index === selectedIndex ) {
					if ( line ) { options.disabledSelectedStyle.forceStyleOnReset.noFormat( options.leftPadding ) ; }
					else { options.disabledSelectedStyle.forceStyleOnReset.noFormat( options.selectedLeftPadding ) ; }

					options.disabledSelectedStyle.forceStyleOnReset.noFormat( text ) ;
				}
				else {
					options.disabledStyle.forceStyleOnReset.noFormat( options.leftPadding ) ;
					options.disabledStyle.forceStyleOnReset.noFormat( text ) ;
				}
			}
			else if ( index === submittedIndex ) {
				if ( line ) { options.submittedStyle.forceStyleOnReset.noFormat( options.leftPadding ) ; }
				else { options.submittedStyle.forceStyleOnReset.noFormat( options.submittedLeftPadding ) ; }

				options.submittedStyle.forceStyleOnReset.noFormat( text ) ;
			}
			else if ( index === selectedIndex ) {
				if ( line ) { options.selectedStyle.forceStyleOnReset.noFormat( options.leftPadding ) ; }
				else { options.selectedStyle.forceStyleOnReset.noFormat( options.selectedLeftPadding ) ; }

				options.selectedStyle.forceStyleOnReset.noFormat( text ) ;
			}
			else {
				options.style.forceStyleOnReset.noFormat( options.leftPadding ) ;
				options.style.forceStyleOnReset.noFormat( text ) ;
			}
		} ) ;
	} ;



	var redrawCursor = () => {
		// Called by finishInit before emitting 'ready'
		//if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , redrawCursor ) ; return ; }
		this.moveTo( 1 , start.y + menuItems[ selectedIndex ].offsetY ) ;
	} ;



	var select = ( index ) => {
		var oldSelectedIndex = selectedIndex ;

		if ( selectedIndex !== index && index >= 0 && index < menuItems.length ) {
			selectedIndex = index ;

			// Don't redraw now if not ready, it will be drawn once ready (avoid double-draw)
			if ( controller.hasState( 'ready' ) ) {
				redrawItem( oldSelectedIndex ) ;
				redrawItem( selectedIndex ) ;
				redrawCursor() ;
				emitHighlight() ;
			}
		}
	} ;



	var submit = () => {
		if ( submittedIndex !== null || options.unsubmittableIndexes[ selectedIndex ] ) { return ; }
		submittedIndex = selectedIndex ;

		// Don't redraw now if not ready, it will be drawn once ready (avoid double-draw)
		if ( controller.hasState( 'ready' ) ) {
			redrawItem( submittedIndex ) ;
			redrawCursor() ;
		}

		controller.emit( 'submit' , {
			selectedIndex: submittedIndex ,
			selectedText: menuItems[ submittedIndex ].text ,
			submitted: true ,
			x: 1 ,
			y: start.y + menuItems[ submittedIndex ].offsetY
		} ) ;

		if ( ! options.continueOnSubmit ) { cleanup() ; }
	} ;



	var cancel = () => {
		var oldSelectedIndex = submittedIndex ;

		if ( submittedIndex === null ) { return ; }
		submittedIndex = null ;
		redrawItem( oldSelectedIndex ) ;
		redrawCursor() ;
		controller.emit( 'cancel' ) ;
	} ;



	var pause = () => {
		if ( paused ) { return ; }
		paused = true ;

		// Don't redraw now if not ready, it will be drawn once ready (avoid double-draw)
		if ( controller.hasState( 'ready' ) ) { redraw() ; }
	} ;



	var resume = () => {
		if ( ! paused ) { return ; }
		paused = false ;

		// Don't redraw now if not ready, it will be drawn once ready (avoid double-draw)
		if ( controller.hasState( 'ready' ) ) { redraw() ; }
	} ;



	var onKey = ( key , trash , data ) => {

		if ( finished || paused ) { return ; }

		var oldSelectedIndex = selectedIndex ;

		switch ( keyBindings[ key ] ) {
			case 'submit' :
				submit() ;
				break ;

			case 'previous' :
				if ( submittedIndex !== null ) { return ; }
				if ( selectedIndex > 0 ) {
					selectedIndex -- ;
					redrawItem( selectedIndex ) ;
					redrawItem( selectedIndex + 1 ) ;
					redrawCursor() ;
					//redraw() ;
					emitHighlight() ;
				}
				break ;

			case 'next' :
				if ( submittedIndex !== null ) { return ; }
				if ( selectedIndex < menuItems.length - 1 ) {
					selectedIndex ++ ;
					redrawItem( selectedIndex - 1 ) ;
					redrawItem( selectedIndex ) ;
					redrawCursor() ;
					//redraw() ;
					emitHighlight() ;
				}
				break ;

			case 'cyclePrevious' :
				if ( submittedIndex !== null ) { return ; }
				selectedIndex -- ;

				if ( selectedIndex < 0 ) { selectedIndex = menuItems.length - 1 ; }

				redrawItem( oldSelectedIndex ) ;
				redrawItem( selectedIndex ) ;
				redrawCursor() ;
				//redraw() ;
				emitHighlight() ;
				break ;

			case 'cycleNext' :
				if ( submittedIndex !== null ) { return ; }
				selectedIndex ++ ;

				if ( selectedIndex >= menuItems.length ) { selectedIndex = 0 ; }

				redrawItem( oldSelectedIndex ) ;
				redrawItem( selectedIndex ) ;
				redrawCursor() ;
				//redraw() ;
				emitHighlight() ;
				break ;

			case 'first' :
				if ( submittedIndex !== null ) { return ; }
				if ( selectedIndex !== 0 ) {
					selectedIndex = 0 ;
					redrawItem( oldSelectedIndex ) ;
					redrawItem( selectedIndex ) ;
					redrawCursor() ;
					//redraw() ;
					emitHighlight() ;
				}
				break ;

			case 'last' :
				if ( submittedIndex !== null ) { return ; }
				if ( selectedIndex !== menuItems.length - 1 ) {
					selectedIndex = menuItems.length - 1 ;
					redrawItem( oldSelectedIndex ) ;
					redrawItem( selectedIndex ) ;
					redrawCursor() ;
					//redraw() ;
					emitHighlight() ;
				}
				break ;

			case 'cancel' :
				cancel() ;
				break ;

			case 'escape' :
				if ( options.cancelable ) {
					cleanup( undefined , { canceled: true } ) ;
				}
				if ( options.exitOnUnexpectedKey ) {
					cleanup( undefined , { unexpectedKey: key , unexpectedKeyData: data } ) ;
				}
				break ;

			default :
				if ( options.exitOnUnexpectedKey ) {
					cleanup( undefined , { unexpectedKey: key , unexpectedKeyData: data } ) ;
				}
				break ;
		}
	} ;



	var onMouse = ( name , data ) => {

		if ( finished || paused || submittedIndex !== null ) { return ; }

		// If out of bounds, exit now!
		if ( data.y < start.y || data.y >= end.y ) { return ; }

		var i , yMin , yMax ,
			inBounds = false ;

		for ( i = 0 ; i < menuItems.length ; i ++ ) {
			yMin = start.y + menuItems[ i ].offsetY ;
			yMax = start.y + menuItems[ i ].offsetY + menuItems[ i ].displayText.length - 1 ;

			if ( data.y >= yMin && data.y <= yMax && data.x < 1 + outerWidth ) {
				inBounds = true ;
				select( i ) ;
				break ;
			}
		}

		if ( inBounds && name === 'MOUSE_LEFT_BUTTON_PRESSED' ) {
			submit() ;
		}
	} ;



	// Return a controller for the menu

	controller = Object.create( NextGenEvents.prototype ) ;

	controller.defineStates( 'ready' ) ;

	// Stop everything and do not even call the callback
	controller.abort = () => {
		if ( finished ) { return ; }
		cleanup( 'abort' ) ;
	} ;

	// Stop and call the completion callback with no item
	controller.stop = ( eraseMenu ) => {
		if ( finished ) { return ; }
		cleanup( undefined , undefined , eraseMenu ) ;
	} ;

	controller.select = select ;
	controller.submit = submit ;
	controller.cancel = cancel ;
	controller.erase = erase ;

	// Pause and resume: the menu will not respond to event when paused
	controller.pause = pause ;
	controller.resume = resume ;
	controller.focus = ( value ) => {
		if ( value ) { resume() ; }
		else { pause() ; }
	} ;

	// Get the current state
	controller.getState = () => ( {
		selectedIndex: selectedIndex ,
		selectedText: menuItems[ selectedIndex ].text ,
		submitted: submittedIndex !== null ,
		start: start ,
		end: end ,
		x: 1 ,
		y: start.y + menuItems[ selectedIndex ].offsetY
		//scrollLines: scrollLines
	} ) ;

	// Get the current position
	controller.getPosition = () => ( { x: start.x , y: start.y } ) ;

	// Hide the menu
	controller.hide = () => {
		if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , controller.hide ) ; return ; }
		erase() ;
	} ;

	// Show the menu
	controller.show = () => {
		if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , controller.show ) ; return ; }
		redraw() ;
	} ;

	// Redraw the menu
	controller.redraw = () => {
		if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , controller.redraw ) ; return ; }
		redraw() ;
	} ;

	// Redraw the cursor
	controller.redrawCursor = () => {
		if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , controller.redrawCursor ) ; return ; }
		redrawCursor() ;
	} ;

	// Rebase the menu where the cursor is
	controller.rebase = () => {
		if ( ! controller.hasState( 'ready' ) ) { controller.once( 'ready' , controller.rebase ) ; return ; }

		// First, disable the menu: getCursorLocation is async!
		var wasPaused = paused ;
		paused = true ;

		this.getCursorLocation( ( error , x , y ) => {
			if ( error ) {
				// Some bad terminals (windows...) doesn't support cursor location request, we should fallback to a decent behavior.
				// Here we just ignore the rebase.
				//cleanup( error ) ;
				return ;
			}

			paused = wasPaused ;
			prepareArea( x , y ) ;
			redraw() ;
			controller.emit( 'rebased' ) ;
		} ) ;
	} ;

	controller.promise = new Promise() ;

	// Init the menu
	init() ;

	return controller ;
} ;


},{"./termkit":56,"nextgen-events":78,"seventh":114,"string-kit":133}],52:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const termkit = require( './termkit' ) ;
const stringWidth = termkit.stringWidth ;
const NextGenEvents = require( 'nextgen-events' ) ;
const Promise = require( 'seventh' ) ;



const defaultKeyBindings = {
	ENTER: 'submit' ,
	KP_ENTER: 'submit' ,
	LEFT: 'previous' ,
	RIGHT: 'next' ,
	UP: 'previousPage' ,
	DOWN: 'nextPage' ,
	TAB: 'cycleNext' ,
	SHIFT_TAB: 'cyclePrevious' ,
	HOME: 'first' ,
	END: 'last' ,
	ESCAPE: 'escape'
} ;



/*
	singleLineMenu( menuItems , [options] , callback )
		* menuItems `array` of menu item text
		* options `object` of options, where:
			* y `number` the line where the menu will be displayed, default to the next line
			* separator `string` (default: '  ') the string separating each menu item
			* nextPageHint `string` (default: ' » ') string indicator for a next page
			* previousPageHint `string` (default: ' « ') string indicator for a previous page
			* style `function` the style of unselected items, default to `term`
			* selectedStyle `function` the style of the selected item, default to `term.dim.blue.bgGreen`
			* selectedIndex `number` selected index at initialization (default: 0)
			* align `string` one of 'left' (default), 'right' or 'center', align the menu accordingly
			* fillIn `boolean` if true (default: false), the menu will fill in the whole line with white chars
			* keyBindings `Object` overide default key bindings
			* cancelable `boolean` if ESCAPE is pressed, it exits, calling the callback with undefined values
			* exitOnUnexpectedKey `boolean` if an unexpected key is pressed, it exits, calling the callback with undefined values
		* callback( error , response ), where:
			* error
			* response `Object` where:
				* selectedIndex `number` the user-selected menu item index
				* selectedText `string` the user-selected menu item text
				* x `number` the x coordinate of the selected menu item (the first character)
				* y `number` the y coordinate of the selected menu item (same coordinate for all items since it's a single line menu)
				* unexpectedKey `string` when 'exitOnUnexpectedKey' option is set, this contains the key that produced the exit
				* canceled `bool` when 'cancelable' option is set, this is set to true
*/
module.exports = function singleLineMenu( menuItems_ , options , callback ) {
	if ( arguments.length < 1 ) { throw new Error( '[terminal] singleLineMenu() needs at least an array of menuItems' ) ; }
	if ( ! Array.isArray( menuItems_ ) || ! menuItems_.length ) { throw new TypeError( '[terminal] singleLineMenu(): argument #0 should be a non-empty array' ) ; }

	if ( typeof options === 'function' ) { callback = options ; options = {} ; }
	else if ( ! options || typeof options !== 'object' ) { options = {} ; }

	if ( options.separator === undefined ) { options.separator = '  ' ; }
	if ( options.nextPageHint === undefined ) { options.nextPageHint = ' » ' ; }
	if ( options.previousPageHint === undefined ) { options.previousPageHint = ' « ' ; }
	if ( ! options.style ) { options.style = this ; }
	if ( ! options.selectedStyle ) { options.selectedStyle = this.dim.blue.bgGreen ; }

	if ( ! options.y ) { this( '\n' ) ; }
	else { this.moveTo( 1 , options.y ) ; }

	var keyBindings = options.keyBindings || defaultKeyBindings ;

	if ( ! this.grabbing ) { this.grabInput() ; }

	var menuItems = menuItems_.map( e => typeof e === 'string' ? e : '' + e ) ;

	var selectedIndexInPage = options.selectedIndex = options.selectedIndex || 0 ;
	var start = {} , selectedPage = 0 , finished = false , menuPages = [] , alreadyCleanedUp = false ;

	// Width
	var nextPageHintWidth = stringWidth( options.nextPageHint ) ,
		previousPageHintWidth = stringWidth( options.previousPageHint ) ,
		separatorWidth = stringWidth( options.separator ) ;

	var computePages = () => {
		var i , itemWidth , displayText , p = 0 , endX = 1 , nextEndX , firstItem = true ,
			lastItem , lineWidth , offset ,
			xMax = this.width - nextPageHintWidth ;

		menuPages = [ [] ] ;

		for ( i = 0 ; i < menuItems.length ; i ++ ) {
			if ( p >= menuPages.length ) { menuPages.push( [] ) ; }

			itemWidth = stringWidth( menuItems[ i ] ) ;
			nextEndX = endX + itemWidth + separatorWidth ;

			if ( nextEndX > xMax ) {
				if ( firstItem ) {
					itemWidth = xMax - endX ;
					displayText = termkit.truncateString( menuItems[ i ] , itemWidth - 1 ) + '…' ;

					if ( i === options.selectedIndex ) {
						selectedPage = p ;
						selectedIndexInPage = menuPages[ p ].length ;
					}

					menuPages[ p ].push( {
						index: i ,
						text: menuItems[ i ] ,
						displayText: displayText ,
						displayTextWidth: itemWidth ,
						x: endX
					} ) ;
				}
				else {
					i -- ;
				}

				p ++ ;
				endX = 1 + previousPageHintWidth ;
				firstItem = true ;

				continue ;
			}

			if ( i === options.selectedIndex ) {
				selectedPage = p ;
				selectedIndexInPage = menuPages[ p ].length ;
			}

			menuPages[ p ].push( {
				index: i ,
				text: menuItems[ i ] ,
				displayText: menuItems[ i ] ,
				displayTextWidth: itemWidth ,
				x: endX
			} ) ;

			endX = nextEndX ;
			firstItem = false ;
		}

		for ( p = 0 ; p < menuPages.length ; p ++ ) {
			lastItem = menuPages[ p ][ menuPages[ p ].length - 1 ] ;
			lineWidth = lastItem.x + lastItem.displayTextWidth - 1 ;
			if ( p < menuPages.length - 1 ) { lineWidth += nextPageHintWidth ; }

			menuPages[ p ].x = 1 ;

			if ( lineWidth < this.width ) {
				if ( options.align === 'right' ) { offset = this.width - lineWidth ; }
				else if ( options.align === 'center' ) { offset = Math.floor( ( this.width - lineWidth ) / 2 ) ; }
				else { offset = 0 ; }

				menuPages[ p ].x += offset ;

				if ( offset ) {
					menuPages[ p ].forEach( item => item.x += offset ) ;
				}
			}
		}
	} ;

	var cleanup = ( error , data ) => {
		if ( alreadyCleanedUp ) { return ; }
		alreadyCleanedUp = true ;

		finished = true ;
		this.removeListener( 'key' , onKey ) ;
		this.removeListener( 'mouse' , onMouse ) ;

		if ( error ) {
			if ( callback ) { callback( error ) ; }
			else { controller.promise.reject( error ) ; }
			return ;
		}

		var page = menuPages[ selectedPage ] ;

		var value = data !== undefined ? data : {
			selectedIndex: page[ selectedIndexInPage ].index ,
			selectedText: page[ selectedIndexInPage ].text ,
			x: page[ selectedIndexInPage ].x ,
			y: start.y
		} ;

		if ( callback ) { callback( undefined , value ) ; }
		else { controller.promise.resolve( value ) ; }
	} ;

	// Compute the coordinate of the end of a string, given a start coordinate
	var redraw = () => {
		var i , cursorX ,
			page = menuPages[ selectedPage ] ,
			endX = page.x ;

		this.moveTo.eraseLineAfter( 1 , start.y ) ;

		if ( options.fillIn && endX > 1 ) { options.style.noFormat( ' '.repeat( endX - 1 ) ) ; }
		else { this.column( endX ) ; }

		if ( selectedPage ) {
			options.style.forceStyleOnReset.noFormat( options.previousPageHint ) ;
			endX += previousPageHintWidth ;
		}

		for ( i = 0 ; i < page.length ; i ++ ) {
			if ( i ) {
				options.style.forceStyleOnReset.noFormat( options.separator ) ;
				endX += separatorWidth ;
			}

			if ( i === selectedIndexInPage ) {
				options.selectedStyle.forceStyleOnReset.noFormat( page[ i ].displayText ) ;
				cursorX = endX ;
			}
			else {
				options.style.forceStyleOnReset.noFormat( page[ i ].displayText ) ;
			}

			endX += page[ i ].displayTextWidth ;
		}

		if ( selectedPage < menuPages.length - 1 ) {
			options.style.forceStyleOnReset.noFormat( options.nextPageHint ) ;
			endX += nextPageHintWidth ;
		}

		if ( options.fillIn && endX < this.width ) { options.style.noFormat( ' '.repeat( this.width - endX ) ) ; }

		this.column( cursorX ) ;
	} ;

	var emitHighlight = () => {
		var item = menuPages[ selectedPage ][ selectedIndexInPage ] ;

		controller.emit( 'highlight' , {
			highlightedIndex: item.index ,
			highlightedText: item.text ,
			x: item.x ,
			y: start.y
		} ) ;
	} ;


	var onKey = ( key , trash , data ) => {
		if ( finished ) { return ; }

		var changed = false ,
			page = menuPages[ selectedPage ] ;

		switch( keyBindings[ key ] ) {
			case 'submit' :
				cleanup() ;
				break ;

			case 'previous' :
				if ( selectedIndexInPage > 0 ) {
					selectedIndexInPage -- ;
					changed = true ;
				}
				else if ( selectedPage > 0 ) {
					selectedPage -- ;
					selectedIndexInPage = menuPages[ selectedPage ].length - 1 ;
					changed = true ;
				}
				break ;

			case 'next' :
				if ( selectedIndexInPage < page.length - 1 ) {
					selectedIndexInPage ++ ;
					changed = true ;
				}
				else if ( selectedPage < menuPages.length - 1 ) {
					selectedPage ++ ;
					selectedIndexInPage = 0 ;
					changed = true ;
				}
				break ;

			case 'cycleNext' :
				if ( selectedPage === menuPages.length - 1 && selectedIndexInPage === page.length - 1 ) {
					selectedPage = 0 ;
					selectedIndexInPage = 0 ;
					changed = true ;
				}
				else if ( selectedIndexInPage < page.length - 1 ) {
					selectedIndexInPage ++ ;
					changed = true ;
				}
				else if ( selectedPage < menuPages.length - 1 ) {
					selectedPage ++ ;
					selectedIndexInPage = 0 ;
					changed = true ;
				}
				break ;

			case 'cyclePrevious' :
				if ( selectedPage === 0 && selectedIndexInPage === 0 ) {
					selectedPage = menuPages.length - 1 ;
					selectedIndexInPage = menuPages[ selectedPage ].length - 1 ;
					changed = true ;
				}
				else if ( selectedIndexInPage > 0 ) {
					selectedIndexInPage -- ;
					changed = true ;
				}
				else if ( selectedPage > 0 ) {
					selectedPage -- ;
					selectedIndexInPage = menuPages[ selectedPage ].length - 1 ;
					changed = true ;
				}
				break ;

			case 'first' :
				if ( selectedPage !== 0 || selectedIndexInPage !== 0 ) {
					selectedPage = 0 ;
					selectedIndexInPage = 0 ;
					changed = true ;
				}
				break ;

			case 'last' :
				if ( selectedPage !== menuPages.length - 1 || selectedIndexInPage !== menuPages[ selectedPage ].length - 1 ) {
					selectedPage = menuPages.length - 1 ;
					selectedIndexInPage = menuPages[ selectedPage ].length - 1 ;
					changed = true ;
				}
				break ;

			case 'previousPage' :
				if ( selectedPage > 0 ) {
					selectedPage -- ;
					selectedIndexInPage = 0 ;
					changed = true ;
				}
				break ;

			case 'nextPage' :
				if ( selectedPage < menuPages.length - 1 ) {
					selectedPage ++ ;
					selectedIndexInPage = 0 ;
					changed = true ;
				}
				break ;

			case 'escape' :
				if ( options.cancelable ) {
					cleanup( undefined , { canceled: true } ) ;
				}
				if ( options.exitOnUnexpectedKey ) {
					cleanup( undefined , { unexpectedKey: key , unexpectedKeyData: data } ) ;
				}
				break ;

			default :
				if ( options.exitOnUnexpectedKey ) {
					cleanup( undefined , { unexpectedKey: key , unexpectedKeyData: data } ) ;
				}
				break ;

		}

		if ( changed ) {
			redraw() ;
			emitHighlight() ;
		}
	} ;


	var onMouse = ( name , data ) => {
		if ( finished ) { return ; }

		// If out of bounds, exit now!
		if ( data.y !== start.y ) { return ; }

		var i , item , nextButtonX ,
			inBounds = false ,
			page = menuPages[ selectedPage ] ;

		// First check previous/next page button click
		if ( name === 'MOUSE_LEFT_BUTTON_PRESSED' ) {
			if ( selectedPage > 0 && data.x >= 1 && data.x < 1 + previousPageHintWidth ) {
				selectedPage -- ;
				selectedIndexInPage = 0 ;
				redraw() ;
				emitHighlight() ;
				return ;
			}

			nextButtonX = page[ page.length - 1 ].x + page[ page.length - 1 ].displayTextWidth ;

			if ( selectedPage < menuPages.length - 1 && data.x >= nextButtonX && data.x < nextButtonX + nextPageHintWidth ) {
				selectedPage ++ ;
				selectedIndexInPage = 0 ;
				redraw() ;
				emitHighlight() ;
				return ;
			}
		}

		for ( i = 0 ; i < page.length ; i ++ ) {
			item = page[ i ] ;

			if ( data.x >= item.x && data.x < item.x + item.displayTextWidth ) {
				inBounds = true ;

				if ( selectedIndexInPage !== i ) {
					selectedIndexInPage = i ;
					redraw() ;
					emitHighlight() ;
				}

				break ;
			}
		}

		if ( inBounds && name === 'MOUSE_LEFT_BUTTON_PRESSED' ) {
			cleanup() ;
		}
	} ;

	var controller = Object.create( NextGenEvents.prototype ) ;

	controller.promise = new Promise() ;

	this.getCursorLocation( ( error , x , y ) => {
		if ( error ) {
			// Some bad terminals (windows...) doesn't support cursor location request, we should fallback to a decent behavior.
			// So we just move to the last line and create a new line.
			//cleanup( error ) ; return ;
			this.row.eraseLineAfter( this.height )( '\n' ) ;
			x = 1 ;
			y = this.height ;
		}

		start.x = x ;
		start.y = y ;
		computePages() ;
		redraw() ;

		// Emit the first auto-selected item
		emitHighlight() ;

		this.on( 'key' , onKey ) ;
		if ( this.mouseGrabbing ) { this.on( 'mouse' , onMouse ) ; }
	} ) ;

	return controller ;
} ;


},{"./termkit":56,"nextgen-events":78,"seventh":114}],53:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



var Promise = require( 'seventh' ) ;



/*
	fakeTyping( str , [options] , callback )
		* str
		* options
			* style
			* delay
			* flashStyle
			* flashDelay
		* callback
*/
module.exports = function slowTyping( str , options , callback ) {
	if ( typeof str !== 'string' ) { throw new TypeError( '[terminal] slowTyping(): argument #0 should be a string' ) ; }
	if ( typeof options === 'function' ) { callback = options ; options = {} ; }
	if ( ! options || typeof options !== 'object' ) { options = {} ; }

	if ( ! options.style ) { options.style = this.green ; }
	if ( ! options.delay ) { options.delay = 150 ; }
	if ( ! options.flashStyle ) { options.flashStyle = this.bold.brightGreen ; }
	if ( ! options.flashDelay ) { options.flashDelay = 100 ; }

	var index , unflashTimer , promise = new Promise() ;

	var printChar = () => {
		if ( unflashTimer ) {
			clearTimeout( unflashTimer ) ;
			unflashTimer = null ;
			unflash() ;
		}

		if ( index === undefined ) {
			index = 0 ;
		}
		else if ( index >= str.length ) {
			if ( callback ) { callback() ; }
			else { promise.resolve() ; }
			return ;
		}
		else {
			if ( options.flashStyle && str[ index ].match( /\S/ ) ) {
				options.flashStyle( str[ index ] ) ;
				unflashTimer = setTimeout( unflash , options.flashDelay ) ;
			}
			else {
				options.style( str[ index ] ) ;
			}

			index ++ ;
		}

		setTimeout( printChar , ( 0.2 + Math.random() * 1.8 ) * options.delay ) ;
	} ;

	var unflash = () => {
		this.left( 1 ) ;
		options.style( str[ index - 1 ] ) ;
		unflashTimer = null ;
	} ;

	printChar() ;

	return promise ;
} ;


},{"seventh":114}],54:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



// Characters that are hard to type
// Comments explain how to type it on a linux platform, using a fr layout keyboard

const BIT_DOTS = "⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙⡚⡛⡜⡝⡞⡟⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷⠸⠹⠺⠻⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿".split( '' ) ;
const GROWING_BLOCK =   [ ' ' , '▁' , '▂' , '▃' , '▄' , '▅' , '▆' , '▇' , '█' ] ;
const ENLARGING_BLOCK = [ ' ' , '▏' , '▎' , '▍' , '▌' , '▋' , '▊' , '▉' , '█' ] ;



module.exports = {
	password: '●' ,		// Currently: the same as blackCircle
	ellispsis: '…' ,

	forwardSingleQuote: '´' ,	// Altgr + ,
	overscore: '¯' ,	// Altgr + Shift + $
	multiply: '×' ,		// Altgr + Shift + ;
	divide: '÷' ,		// Altgr + Shift + :

	// Arrows
	up: '↑' ,			// Altgr + Shift + u
	down: '↓' ,			// Altgr + u
	left: '←' ,			// Altgr + y
	right: '→' ,		// Altgr + i
	leftAndRight: '↔' ,
	upAndDown: '↕' ,
	upLeft: '↖' ,
	upRight: '↗' ,
	downRight: '↘' ,
	downLeft: '↙' ,
	upLeftAndDownRight: '⤡' ,
	upRightAndDownLeft: '⤢' ,

	// Those names are most common in the UTF-8 parlance
	northWest: '↖' ,
	northEast: '↗' ,
	southEast: '↘' ,
	southWest: '↙' ,
	northWestAndSouthEast: '⤡' ,
	northEastAndSouthWest: '⤢' ,

	fullBlock: '█' ,
	upperHalfBlock: '▀' ,
	lowerHalfBlock: '▄' ,

	// Array of 0-8 growing/enlarging blocks
	growingBlock: GROWING_BLOCK ,
	enlargingBlock: ENLARGING_BLOCK ,

	bitDots: BIT_DOTS ,

	// When editing this, update spChars.md doc
	bar: {
		classic: {
			border: [ '[' , ']' ] ,
			body: [ '=' , ' ' ]
		} ,
		classicWithArrow: {
			border: [ '[' , ']' ] ,
			body: [ '=' , '>' , ' ' ]
		} ,
		classicWithHalf: {
			border: [ '[' , ']' ] ,
			body: [ '=' , ' ' , '-' , '=' , ' ' ]
		} ,
		solid: {
			border: [ '^!▉' , '▏' ] ,
			body: [ '█' , ... ENLARGING_BLOCK , ' ' ]
		}
	} ,

	// When editing this, update spChars.md doc
	box: {
		__fix__: object => ( {
			vertical: object.vertical || ' ' ,
			horizontal: object.horizontal || ' ' ,
			topLeft: object.topLeft || ' ' ,
			topRight: object.topRight || ' ' ,
			bottomLeft: object.bottomLeft || ' ' ,
			bottomRight: object.bottomRight || ' ' ,
			topTee: object.topTee || ' ' ,
			bottomTee: object.bottomTee || ' ' ,
			leftTee: object.leftTee || ' ' ,
			rightTee: object.rightTee || ' ' ,
			cross: object.cross || ' '
		} ) ,
		plain: {
			vertical: '█' ,
			horizontal: '█' ,
			topLeft: '█' ,
			topRight: '█' ,
			bottomLeft: '█' ,
			bottomRight: '█' ,
			topTee: '█' ,
			bottomTee: '█' ,
			leftTee: '█' ,
			rightTee: '█' ,
			cross: '█'
		} ,
		empty: {
			vertical: ' ' ,
			horizontal: ' ' ,
			topLeft: ' ' ,
			topRight: ' ' ,
			bottomLeft: ' ' ,
			bottomRight: ' ' ,
			topTee: ' ' ,
			bottomTee: ' ' ,
			leftTee: ' ' ,
			rightTee: ' ' ,
			cross: ' '
		} ,
		ascii: {
			vertical: '|' ,
			horizontal: '-' ,
			topLeft: '|' ,
			topRight: '|' ,
			bottomLeft: '|' ,
			bottomRight: '|' ,
			topTee: '-' ,
			bottomTee: '-' ,
			leftTee: '|' ,
			rightTee: '|' ,
			cross: '+'
		} ,
		light: {
			vertical: '│' ,
			horizontal: '─' ,
			topLeft: '┌' ,
			topRight: '┐' ,
			bottomLeft: '└' ,
			bottomRight: '┘' ,
			topTee: '┬' ,
			bottomTee: '┴' ,
			leftTee: '├' ,
			rightTee: '┤' ,
			cross: '┼'
		} ,
		lightRounded: {
			vertical: '│' ,
			horizontal: '─' ,
			topLeft: '╭' ,
			topRight: '╮' ,
			bottomLeft: '╰' ,
			bottomRight: '╯' ,
			topTee: '┬' ,
			bottomTee: '┴' ,
			leftTee: '├' ,
			rightTee: '┤' ,
			cross: '┼'
		} ,
		heavy: {
			vertical: '┃' ,
			horizontal: '━' ,
			topLeft: '┏' ,
			topRight: '┓' ,
			bottomLeft: '┗' ,
			bottomRight: '┛' ,
			topTee: '┳' ,
			bottomTee: '┻' ,
			leftTee: '┣' ,
			rightTee: '┫' ,
			cross: '╋'
		} ,
		double: {
			vertical: '║' ,
			horizontal: '═' ,
			topLeft: '╔' ,
			topRight: '╗' ,
			bottomLeft: '╚' ,
			bottomRight: '╝' ,
			topTee: '╦' ,
			bottomTee: '╩' ,
			leftTee: '╠' ,
			rightTee: '╣' ,
			cross: '╬'
		} ,
		dotted: {
			vertical: '┊' ,
			horizontal: '┄' ,
			topLeft: '┌' ,
			topRight: '┐' ,
			bottomLeft: '└' ,
			bottomRight: '┘' ,
			topTee: '┬' ,
			bottomTee: '┴' ,
			leftTee: '├' ,
			rightTee: '┤' ,
			cross: '┼'
		}
	} ,

	// When editing this, update spChars.md doc
	animation: {
		asciiSpinner: [ '│' , '/' , '-' , '\\' ] ,
		lineSpinner: [ '│' , '/' , '─' , '\\' ] ,
		dotSpinner: [
			BIT_DOTS[ 7 ] ,
			BIT_DOTS[ 19 ] ,
			BIT_DOTS[ 49 ] ,
			BIT_DOTS[ 112 ] ,
			BIT_DOTS[ 224 ] ,
			BIT_DOTS[ 200 ] ,
			BIT_DOTS[ 140 ] ,
			BIT_DOTS[ 14 ]
		] ,
		bitDots: BIT_DOTS ,
		impulse: [
			"∙∙∙" ,
			"●∙∙" ,
			"∙●∙" ,
			"∙∙●" ,
			"∙●∙" ,
			"●∙∙" ,
			"∙∙∙" ,
			"∙∙∙"
		] ,
		unboxing: [ ' ' , '▁' , '▂' , '▃' , '▄' , '▅' , '▆' , '▇' , '█' , '▉' , '▊' , '▋' , '▌' , '▍' , '▎' , '▏' ] ,
		'unboxing-color': [
			'^r^#^b ' , '^r^#^b▁' , '^r^#^b▂' , '^r^#^b▃' , '^r^#^b▄' , '^r^#^b▅' , '^r^#^b▆' , '^r^#^b▇' , '^r^#^m█' , '^r^#^m▉' , '^r^#^m▊' , '^r^#^m▋' , '^r^#^m▌' , '^r^#^m▍' , '^r^#^m▎' , '^r^#^m▏' ,
			'^m^#^y█' , '^m^#^y▇' , '^m^#^y▆' , '^m^#^y▅' , '^m^#^y▄' , '^m^#^y▃' , '^m^#^y▂' , '^m^#^y▁' , '^b^#^y ' , '^b^#^y▏' , '^b^#^y▎' , '^b^#^y▍' , '^b^#^y▌' , '^b^#^y▋' , '^b^#^y▊' , '^b^#^y▉'
		]
	} ,

	blackSquare: '■' ,
	whiteSquare: '□' ,
	blackCircle: '●' ,
	whiteCircle: '○' ,
	blackUpTriangle: '▲' ,
	whiteUpTriangle: '△' ,
	blackDownTriangle: '▼' ,
	whiteDownTriangle: '▽' ,
	blackLeftTriangle: '◀' ,
	whiteLeftTriangle: '◁' ,
	blackRightTriangle: '▶' ,
	whiteRightTriangle: '▷' ,
	blackDiamond: '◆' ,
	whiteDiamond: '◇' ,
	blackStar: '★' ,
	whiteStar: '☆' ,
	spadeSuit: '♠' ,
	heartSuit: '♥' ,
	diamondSuit: '♦' ,
	clubSuit: '♣' ,

	// Powerline specific characters (https://powerline.readthedocs.io)
	// It is displayed only with the appropriate font
	powerline: {
		branch: '' ,
		line: '' ,
		readOnly: '' ,
		rightTriangleSeparator: '' ,
		rightArrowSeparator: '' ,
		leftTriangleSeparator: '' ,
		leftArrowSeparator: ''
	}
} ;


},{}],55:[function(require,module,exports){
(function (process){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const termkit = {} ;
module.exports = termkit ;

const lazy = require( 'lazyness' )( require ) ;



// Global config
termkit.globalConfig = {} ;



termkit.tty = require( './tty.js' ) ;

// For some reason, starting from node v4, once process.stdin getter is triggered, the 'tty' command would not work properly.
// This 'hack' cache the result of the command 'tty' if we are in the linux console, so 'gpm' can work.
if ( process.env.TERM === 'linux' ) { termkit.tty.getPath() ; }



// Core submodules
Object.assign( termkit , require( './misc.js' ) ) ;
Object.assign( termkit , require( './detectTerminal.js' ) ) ;

termkit.Terminal = require( './Terminal.js' ) ;
termkit.createTerminal = termkit.Terminal.create ;

// Windows patches
if ( process.platform === 'win32' ) { require( './windows.js' )( termkit ) ; }



termkit.image = require( './image.js' ) ;
termkit.Palette = require( './Palette.js' ) ;
termkit.Rect = require( './Rect.js' ) ;
termkit.ScreenBuffer = require( './ScreenBuffer.js' ) ;
termkit.ScreenBufferHD = require( './ScreenBufferHD.js' ) ;
termkit.TextBuffer = require( './TextBuffer.js' ) ;
termkit.Vte = require( './vte/Vte.js' ) ;
termkit.autoComplete = require( './autoComplete.js' ) ;
termkit.spChars = require( './spChars.js' ) ;

// Document model
termkit.Element = require( './document/Element.js' ) ;
termkit.Document = require( './document/Document.js' ) ;
termkit.Container = require( './document/Container.js' ) ;
termkit.Text = require( './document/Text.js' ) ;
termkit.AnimatedText = require( './document/AnimatedText.js' ) ;
termkit.Button = require( './document/Button.js' ) ;
termkit.ToggleButton = require( './document/ToggleButton.js' ) ;
termkit.TextBox = require( './document/TextBox.js' ) ;
termkit.EditableTextBox = require( './document/EditableTextBox.js' ) ;
termkit.Slider = require( './document/Slider.js' ) ;
termkit.Bar = require( './document/Bar.js' ) ;
termkit.LabeledInput = require( './document/LabeledInput.js' ) ;
termkit.InlineInput = require( './document/InlineInput.js' ) ;
termkit.InlineFileInput = require( './document/InlineFileInput.js' ) ;
termkit.InlineMenu = require( './document/InlineMenu.js' ) ;
termkit.Inspector = require( './document/Inspector.js' ) ;
termkit.Form = require( './document/Form.js' ) ;
termkit.RowMenu = require( './document/RowMenu.js' ) ;
termkit.ColumnMenu = require( './document/ColumnMenu.js' ) ;
termkit.ColumnMenuMulti = require( './document/ColumnMenuMulti.js' ) ;
termkit.ColumnMenuMixed = require( './document/ColumnMenuMixed.js' ) ;
termkit.SelectList = require( './document/SelectList.js' ) ;
termkit.SelectListMulti = require( './document/SelectListMulti.js' ) ;
termkit.DropDownMenu = require( './document/DropDownMenu.js' ) ;
termkit.TextTable = require( './document/TextTable.js' ) ;
termkit.Layout = require( './document/Layout.js' ) ;
termkit.Border = require( './document/Border.js' ) ;
termkit.Window = require( './document/Window.js' ) ;

// External modules
termkit.chroma = require( 'chroma-js' ) ;



lazy.properties( termkit , {
	terminal: () => {
		var guessed = termkit.guessTerminal() ;
		return termkit.createTerminal( {
			stdin: process.stdin ,
			stdout: process.stdout ,
			stderr: process.stderr ,
			generic: guessed.generic || 'unknown' ,
			appId: guessed.safe ? guessed.appId : undefined ,
			//	appName: guessed.safe ? guessed.appName : undefined ,
			isTTY: guessed.isTTY ,
			isSSH: guessed.isSSH ,
			processSigwinch: true ,
			preferProcessSigwinch: !! termkit.globalConfig.preferProcessSigwinch
		} ) ;
	} ,
	realTerminal: () => {
		var guessed = termkit.guessTerminal( true ) ;
		var input = termkit.tty.getInput() ;
		var output = termkit.tty.getOutput() ;

		return termkit.createTerminal( {
			stdin: input ,
			stdout: output ,
			stderr: process.stderr ,
			generic: guessed.generic || 'unknown' ,
			appId: guessed.safe ? guessed.appId : undefined ,
			//	appName: guessed.safe ? guessed.appName : undefined ,
			isTTY: true ,
			isSSH: guessed.isSSH ,
			processSigwinch: true ,
			preferProcessSigwinch: !! termkit.globalConfig.preferProcessSigwinch
		} ) ;
	}
} , true ) ;


}).call(this)}).call(this,require('_process'))
},{"./Palette.js":1,"./Rect.js":2,"./ScreenBuffer.js":3,"./ScreenBufferHD.js":4,"./Terminal.js":5,"./TextBuffer.js":6,"./autoComplete.js":7,"./detectTerminal.js":12,"./document/AnimatedText.js":13,"./document/Bar.js":14,"./document/Border.js":16,"./document/Button.js":17,"./document/ColumnMenu.js":18,"./document/ColumnMenuMixed.js":19,"./document/ColumnMenuMulti.js":20,"./document/Container.js":21,"./document/Document.js":22,"./document/DropDownMenu.js":23,"./document/EditableTextBox.js":24,"./document/Element.js":25,"./document/Form.js":26,"./document/InlineFileInput.js":27,"./document/InlineInput.js":28,"./document/InlineMenu.js":29,"./document/Inspector.js":30,"./document/LabeledInput.js":31,"./document/Layout.js":32,"./document/RowMenu.js":33,"./document/SelectList.js":34,"./document/SelectListMulti.js":35,"./document/Slider.js":36,"./document/Text.js":37,"./document/TextBox.js":38,"./document/TextTable.js":39,"./document/ToggleButton.js":40,"./document/Window.js":41,"./image.js":46,"./misc.js":48,"./spChars.js":54,"./tty.js":57,"./vte/Vte.js":59,"./windows.js":62,"_process":191,"chroma-js":65,"lazyness":74}],56:[function(require,module,exports){
(function (process,__dirname){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const path = require( 'path' ) ;

if ( process.browser || require.cache[ path.join( __dirname , 'termkit-no-lazy-require.js' ) ] ) {
	console.log( 'using termkit-no-lazy-require.js' ) ;
	module.exports = require( './termkit-no-lazy-require.js' ) ;
	return ;
}



const termkit = {} ;
module.exports = termkit ;

const lazy = require( 'lazyness' )( require ) ;



// Global config
termkit.globalConfig = {} ;



lazy.requireProperty( termkit , 'tty' , './tty.js' ) ;

// For some reason, starting from node v4, once process.stdin getter is triggered, the 'tty' command would not work properly.
// This 'hack' cache the result of the command 'tty' if we are in the linux console, so 'gpm' can work.
if ( process.env.TERM === 'linux' ) { termkit.tty.getPath() ; }



// Core submodules
Object.assign( termkit , require( './misc.js' ) ) ;
Object.assign( termkit , require( './detectTerminal.js' ) ) ;

termkit.Terminal = require( './Terminal.js' ) ;
termkit.createTerminal = termkit.Terminal.create ;

// Windows patches
if ( process.platform === 'win32' ) { require( './windows.js' )( termkit ) ; }



// Lazy submodules
lazy.requireProperties( termkit , {
	image: './image.js' ,
	Palette: './Palette.js' ,
	Rect: './Rect.js' ,
	ScreenBuffer: './ScreenBuffer.js' ,
	ScreenBufferHD: './ScreenBufferHD.js' ,
	TextBuffer: './TextBuffer.js' ,
	Vte: './vte/Vte.js' ,
	autoComplete: './autoComplete.js' ,
	spChars: './spChars.js' ,

	// Document model
	Element: './document/Element.js' ,
	Document: './document/Document.js' ,
	Container: './document/Container.js' ,
	Text: './document/Text.js' ,
	AnimatedText: './document/AnimatedText.js' ,
	Button: './document/Button.js' ,
	ToggleButton: './document/ToggleButton.js' ,
	TextBox: './document/TextBox.js' ,
	EditableTextBox: './document/EditableTextBox.js' ,
	Slider: './document/Slider.js' ,
	Bar: './document/Bar.js' ,
	LabeledInput: './document/LabeledInput.js' ,
	InlineInput: './document/InlineInput.js' ,
	InlineFileInput: './document/InlineFileInput.js' ,
	InlineMenu: './document/InlineMenu.js' ,
	Inspector: './document/Inspector.js' ,
	Form: './document/Form.js' ,
	RowMenu: './document/RowMenu.js' ,
	ColumnMenu: './document/ColumnMenu.js' ,
	ColumnMenuMulti: './document/ColumnMenuMulti.js' ,
	ColumnMenuMixed: './document/ColumnMenuMixed.js' ,
	SelectList: './document/SelectList.js' ,
	SelectListMulti: './document/SelectListMulti.js' ,
	DropDownMenu: './document/DropDownMenu.js' ,
	TextTable: './document/TextTable.js' ,
	Layout: './document/Layout.js' ,
	Border: './document/Border.js' ,
	Window: './document/Window.js' ,

	// External modules
	chroma: 'chroma-js'
} ) ;



lazy.properties( termkit , {
	terminal: () => {
		var guessed = termkit.guessTerminal() ;
		return termkit.createTerminal( {
			stdin: process.stdin ,
			stdout: process.stdout ,
			stderr: process.stderr ,
			generic: guessed.generic || 'unknown' ,
			appId: guessed.safe ? guessed.appId : undefined ,
			//	appName: guessed.safe ? guessed.appName : undefined ,
			isTTY: guessed.isTTY ,
			isSSH: guessed.isSSH ,
			processSigwinch: true ,
			preferProcessSigwinch: !! termkit.globalConfig.preferProcessSigwinch
		} ) ;
	} ,
	realTerminal: () => {
		var guessed = termkit.guessTerminal( true ) ;
		var input = termkit.tty.getInput() ;
		var output = termkit.tty.getOutput() ;

		return termkit.createTerminal( {
			stdin: input ,
			stdout: output ,
			stderr: process.stderr ,
			generic: guessed.generic || 'unknown' ,
			appId: guessed.safe ? guessed.appId : undefined ,
			//	appName: guessed.safe ? guessed.appName : undefined ,
			isTTY: true ,
			isSSH: guessed.isSSH ,
			processSigwinch: true ,
			preferProcessSigwinch: !! termkit.globalConfig.preferProcessSigwinch
		} ) ;
	}
} , true ) ;


}).call(this)}).call(this,require('_process'),"/lib")
},{"./Terminal.js":5,"./detectTerminal.js":12,"./misc.js":48,"./termkit-no-lazy-require.js":55,"./windows.js":62,"_process":191,"lazyness":74,"path":190}],57:[function(require,module,exports){
(function (process){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



//var exec = require( 'child_process' ).exec ;
//var spawn = require( 'child_process' ).spawn ;
var execSync = require( 'child_process' ).execSync ;
var fs = require( 'fs' ) ;
var tty = require( 'tty' ) ;




var tty_ = {} ;
module.exports = tty_ ;



var cachedGetPath ;

/*
	getPath( [stdin] )
		* stdin: a stream that is the current STDIN of the terminal

	Returns an object, where:
		* ttyPath: the path of the tty
		* ttyIndex: the index number of the tty, only if it is a /dev/tty*, /dev/pts/* return null
*/
tty_.getPath = function getPath( stdin ) {
	var cacheIt , result , ttyPath , ttyIndex , matches ;

	// Manage arguments
	if ( ! stdin ) {
		// getPath() does not work as soon as process.stdin getter is triggered (since node v4)
		// So 0 should be used instead of process.stdin
		stdin = 0 ;
		//stdin = process.stdin ;
	}

	if ( stdin === 0 || stdin === process.stdin ) {
		if ( cachedGetPath ) { return cachedGetPath ; }
		cacheIt = true ;
	}


	try {
		// if no stdio are passed, the command will report 'not a TTY'
		ttyPath = execSync( 'tty' , { stdio: [ stdin , null , null ] } ).toString() ;
	}
	catch ( error ) {
		ttyPath = error.stdout.toString() ;
	}

	ttyPath = ttyPath.trim() ;

	//console.log( 'TTY path:' , ttyPath ) ;

	matches = ttyPath.match( /\/dev\/tty([0-9]*)/ ) ;

	ttyIndex = matches ? matches[ 1 ] || null : null ;

	result = {
		path: ttyPath ,
		index: ttyIndex
	} ;

	if ( cacheIt ) { cachedGetPath = result ; }

	return result ;
} ;



/*
	getInput()

	Open a TTY input file descriptor and transform it into a regular node.js TTY input stream.
	It returns the TTY input `Stream` use instead of process.stdin

	This code was borrowed from the 'ttys' module by Nathan Rajlich.
*/
tty_.getInput = function getInput() {
	var inputFd , input ;

	inputFd = fs.openSync( '/dev/tty' , 'r' ) ;
	if ( ! tty.isatty( inputFd ) ) { throw new Error( 'Input file descriptor is not a TTY.' ) ; }
	input = new tty.ReadStream( inputFd ) ;
	input._type = 'tty' ;

	return input ;
} ;



/*
	getOutput()

	Open a TTY output file descriptor and transform it into a regular node.js TTY output stream.
	It returns the TTY output `Stream` use instead of process.stdin

	This code was borrowed from the 'ttys' module by Nathan Rajlich.
*/
tty_.getOutput = function getOutput() {
	var outputFd , output ;

	outputFd = fs.openSync( '/dev/tty' , 'w' ) ;
	if ( ! tty.isatty( outputFd ) ) { throw new Error( 'Output file descriptor is not a TTY.' ) ; }
	output = new tty.WriteStream( outputFd ) ;
	output._type = 'tty' ;

	// Hack to have the stdout stream not keep the event loop alive.
	// See: https://github.com/joyent/node/issues/1726
	// XXX: remove/fix this once src/node.js does something different as well.
	// @cronvel: that doesn't work much either...
	if ( output._handle && output._handle.unref ) {
		output._handle.unref() ;
	}

	// Update the "columns" and "rows" properties on the stdout stream
	// whenever the console window gets resized.
	if ( output._refreshSize ) {
		process.on( 'SIGWINCH' , () => {
			output._refreshSize() ;
		} ) ;
	}

	return output ;
} ;



}).call(this)}).call(this,require('_process'))
},{"_process":191,"child_process":148,"fs":148,"tty":210}],58:[function(require,module,exports){
(function (Buffer){(function (){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const fromOutputSequence = require( './fromOutputSequence.js' ) ;
const string = require( 'string-kit' ) ;
const NextGenEvents = require( 'nextgen-events' ) ;
const Promise = require( 'seventh' ) ;



function SequencesReader( options = {} ) {
}

module.exports = SequencesReader ;

SequencesReader.prototype = Object.create( NextGenEvents.prototype ) ;
SequencesReader.prototype.constructor = SequencesReader ;



async function readStream( stream , size = 1 ) {
	var data ;

	//stream.on( 'readable' , () => console.error( 'yay readable' ) ) ;
	data = stream.read( size ) ;
	//console.error( 'read stream:' , data ) ;

	while ( data === null ) {
		await Promise.onceEventOrError( stream , 'readable' , [ 'close' , 'end' ] ) ;
		data = stream.read( size ) ;
		//console.error( 'read stream (loop):' , data ) ;
	}

	return data ;
}



function charCodeIsAlpha( charCode ) {
	return ( charCode >= 0x41 && charCode <= 0x5a ) || ( charCode >= 0x61 && charCode <= 0x7a ) ;
}

function toCharCodeStr( charCode ) {
	var charCodeStr = charCode.toString( 16 ) ;
	charCodeStr = charCodeStr.length > 1 ? '\\x' + charCodeStr : '\\x0' + charCodeStr ;
	return charCodeStr ;
}

function toCharOrCharCodeStr( charCode ) {
	if ( charCode <= 0x1f || charCode === 0x7f ) { return toCharCodeStr( charCode ) ; }
	return String.fromCharCode( charCode ) ;
}

function defaultArgs( args , defaultArgs_ ) {
	if ( ! defaultArgs_ || ! defaultArgs_.length ) { return args ; }

	args = Array.from( args ) ;

	defaultArgs_.forEach( ( e , index ) => {
		if ( e !== undefined && args[ index ] === undefined ) {
			args[ index ] = e ;
		}
	} ) ;

	return args ;
}

// ESC sequence type that eat 3 bytes
const ESC_3_BYTES = new Set(
	// Set character set
	'(' , ')' , '*' , '+' , '-' , '.' , '/' ,
	// Misc
	' ' , '#' , '%'
) ;

// E.g.: ESC [ ? ... letter
const CSI_TYPE_EXTRA_CHAR_BEFORE = new Set(
	'?' , '>' , '<'
) ;

// E.g.: ESC [ ... space letter
const CSI_TYPE_EXTRA_CHAR_AFTER = new Set(
	' ' , '$' , '#' , '"' , "'" , '*'
) ;

// E.g.: ESC [ ... ; ? \x07
const OSC_TYPE_EXTRA_PARAM_AFTER = new Set(
	'?'
) ;



SequencesReader.prototype.streamToEvent = async function( stream ) {
	var charCode , charCodeStr , char , bytes , codepoint ,
		type , args , argIndex , emitParsedSequence , event , subTree , subSubTree , basePtr ,
		charBuffer = Buffer.alloc( 6 ) ;

	for ( ;; ) {
		charCode = ( await readStream( stream ) )[ 0 ] ;
		//console.error( 'got charCode:' , charCode.toString(16) ) ;

		if ( charCode <= 0x1f || charCode === 0x7f ) {

			charCodeStr = toCharCodeStr( charCode ) ;
			//console.error( 'control:' , charCodeStr ) ;

			if ( charCode === 0x1b ) {	// Escape, probably an escape sequence!
				charCode = ( await readStream( stream ) )[ 0 ] ;

				if ( charCode === 0x5b ) {	// [ =CSI
					args = '' ;

					while ( ! charCodeIsAlpha( charCode = ( await readStream( stream ) )[ 0 ] ) ) {
						args += String.fromCharCode( charCode ) ;
					}

					type = String.fromCharCode( charCode ) ;
					args = args ? args.split( ';' ) : [] ;
					emitParsedSequence = true ;

					if ( args.length && CSI_TYPE_EXTRA_CHAR_BEFORE.has( args[ 0 ][ 0 ] ) ) {
						type = args[ 0 ][ 0 ] + type ;
						args[ 0 ] = args[ 0 ].slice( 1 ) ;
					}

					if ( args.length && CSI_TYPE_EXTRA_CHAR_AFTER.has( args[ args.length - 1 ][ args[ args.length - 1 ] - 1 ] ) ) {
						type = args[ args.length - 1 ][ args[ args.length - 1 ] - 1 ] + type ;
						args[ args.length - 1 ] = args[ args.length - 1 ].slice( 0 , -1 ) ;
					}

					subTree = fromOutputSequence.CSI[ type ] ;
					console.error( ">>>>>>>>>> CSI parsing:" , type , args ) ;

					if ( subTree ) {
						if ( subTree.subTree && args.length ) {
							basePtr = subTree ;

							for ( argIndex = 0 ; argIndex < args.length ; argIndex ++ ) {
								subSubTree = basePtr.subTree[ args[ argIndex ] ] ;

								if ( subSubTree ) {
									if ( subSubTree.subTree ) {
										basePtr = subSubTree ;
										continue ;
									}

									event = subSubTree.event || basePtr.event || subTree.event ;

									if ( event !== 'none' ) {
										this.emit(
											event ,
											subSubTree.subType || basePtr.subType || subTree.subType ,
											subSubTree.arg !== undefined ? subSubTree.arg : ( basePtr.arg !== undefined ? basePtr.arg : subTree.arg ) ,	/* eslint-disable-line no-nested-ternary */
											subSubTree.extraArgs || basePtr.extraArgs || subTree.extraArgs || defaultArgs( args.slice( argIndex + 1 ) , subSubTree.defaultExtraArgs )
										) ;
									}

									emitParsedSequence = false ;

									if ( subSubTree.continue ) {
										basePtr = subTree ;
										continue ;
									}
								}
								else {
									emitParsedSequence = true ;
								}
								break ;
							}
						}
						else if ( subTree.event ) {
							if ( subTree.event !== 'none' ) {
								this.emit(
									subTree.event ,
									subTree.subType ,
									subTree.arg ,
									subTree.extraArgs || defaultArgs( args , subTree.defaultExtraArgs )
								) ;
							}

							emitParsedSequence = false ;
						}
					}

					if ( emitParsedSequence ) {
						this.emit( 'CSI' , type , args ) ;
					}

					continue ;
				}
				else if ( charCode === 0x5d ) {	// ] =OSC
					args = '' ;

					for ( ;; ) {
						charCode = ( await readStream( stream ) )[ 0 ] ;

						if ( charCode === 0x07 ) { break ; }	// This is the OSC terminator, leave

						if ( charCode === 0x1b ) {
							charCode = ( await readStream( stream ) )[ 0 ] ;
							if ( charCode === 0x5c ) { break ; }	// ESC \ =string terminator, so we leave
							// Ok, that was not the string terminator, so add the ESC key to the arg, but it is a bit strange to do that...
							args += String.fromCharCode( 0x1b ) ;
						}

						args += String.fromCharCode( charCode ) ;
					}

					args = args ? args.split( ';' ) : [] ;
					type = args.shift() ;

					if ( OSC_TYPE_EXTRA_PARAM_AFTER.has( args[ args.length - 1 ] ) ) {
						type += args.pop() ;
					}

					emitParsedSequence = true ;

					subTree = fromOutputSequence.OSC[ type ] ;

					if ( subTree ) {
						if ( subTree.subTree ) {
							for ( argIndex = 0 ; argIndex < args.length ; argIndex ++ ) {
								subSubTree = subTree.subTree[ args[ argIndex ] ] ;
								if ( subSubTree ) {
									event = subSubTree.event || subTree.event ;

									if ( event !== 'none' ) {
										this.emit( event , subSubTree.subType || subTree.subType , args.slice( argIndex + 1 ) ) ;
									}

									emitParsedSequence = false ;
								}
								else {
									emitParsedSequence = true ;
								}
								break ;
							}
						}
						else if ( subTree.event ) {
							if ( subTree.event !== 'none' ) {
								this.emit( subTree.event , subTree.subType , args ) ;
							}

							emitParsedSequence = false ;
						}
					}

					if ( emitParsedSequence ) {
						this.emit( 'OSC' , type , args ) ;
					}

					continue ;
				}
				else {
					// Single/simple escape
					type = toCharOrCharCodeStr( charCode ) ;
					args = null ;

					if ( ESC_3_BYTES.has( type ) ) {
						// This is a 3 bytes ESC
						args = [ toCharOrCharCodeStr( ( await readStream( stream ) )[ 0 ] ) ] ;
					}

					emitParsedSequence = true ;
					subTree = fromOutputSequence.ESC[ type ] ;

					if ( subTree ) {
						if ( subTree.event !== 'none' ) {
							this.emit( subTree.event , subTree.subType , subTree.arg , args || subTree.extraArgs ) ;
						}

						emitParsedSequence = false ;
					}

					if ( emitParsedSequence ) {
						this.emit( 'ESC' , type , args ) ;
					}

					continue ;
				}
			}
			else if ( fromOutputSequence.control[ charCodeStr ] ) {
				subTree = fromOutputSequence.control[ charCodeStr ] ;

				if ( subTree.event !== 'none' ) {
					this.emit( subTree.event , subTree.subType , subTree.arg , subTree.extraArgs ) ;
				}

				continue ;
			}
			else {
				this.emit( 'control' , charCodeStr ) ;
				continue ;
			}
		}

		if ( charCode >= 0x80 ) {
			// Unicode bytes per char guessing
			if ( charCode < 0xc0 ) { continue ; }	// We are in a middle of an unicode multibyte sequence... Something fails somewhere, we will just continue for now...
			else if ( charCode < 0xe0 ) { bytes = 2 ; }
			else if ( charCode < 0xf0 ) { bytes = 3 ; }
			else if ( charCode < 0xf8 ) { bytes = 4 ; }
			else if ( charCode < 0xfc ) { bytes = 5 ; }
			else { bytes = 6 ; }

			charBuffer[ 0 ] = charCode ;
			charBuffer[ 1 ] = charBuffer[ 2 ] = charBuffer[ 3 ] = charBuffer[ 4 ] = charBuffer[ 5 ] = 0 ;
			( await readStream( stream , bytes - 1 ) ).copy( charBuffer , 1 ) ;

			char = charBuffer.toString( 'utf8' ) ;
			codepoint = string.unicode.firstCodePoint( char ) ;

			//this.emit( 'key' , char , [ char ] , { isCharacter: true , codepoint: codepoint , code: charBuffer } ) ;
			this.emit( 'char' , char , codepoint ) ;
		}
		else {
			// Standard ASCII
			char = String.fromCharCode( charCode ) ;
			//this.emit( 'key' , char , [ char ] , { isCharacter: true , codepoint: charCode , code: charCode } ) ;
			this.emit( 'char' , char , charCode ) ;
		}
	}
} ;


}).call(this)}).call(this,require("buffer").Buffer)
},{"./fromOutputSequence.js":60,"buffer":159,"nextgen-events":78,"seventh":114,"string-kit":133}],59:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const ScreenBuffer = require( '../ScreenBuffer.js' ) ;
const Rect = require( '../Rect.js' ) ;
const string = require( 'string-kit' ) ;
const toInputSequence = require( './toInputSequence.js' ) ;
const SequencesReader = require( './SequencesReader.js' ) ;

const NextGenEvents = require( 'nextgen-events' ) ;
const Promise = require( 'seventh' ) ;

const spawn = require( 'child_process' ).spawn ;

const logRed = ( ... args ) => console.error( '\x1b[31m' , ... args , '\x1b[m' ) ;
const log = ( ... args ) => console.error( ... args ) ;



/*
	options:
		* width: buffer width (default to dst.width)
		* height: buffer height (default to dst.height)
		* dst: writting destination
		* palette: Palette instance
		* eventInput: optional, an event emitter used as input source, can be a Terminal instance
		* + any ScreenBuffer options

	Properties:
		* childStdin: the stdin stream for the child, that will be used for its keyboard/mouse event
		* childStdout: the stdout stream for the child, that will be displayed on the terminal
*/
function Vte( options = {} ) {
	this.width = Math.floor( options.width ) || ( options.dst ? options.dst.width : 80 ) ;
	this.height = Math.floor( options.height ) || ( options.dst ? options.dst.height : 25 ) ;
	this.palette = options.palette || ( this.dst && this.dst.palette ) ;

	this.screenBuffer = new ScreenBuffer( Object.assign( {} , options , this , { wrap: true } ) ) ;
	this.screenBuffer.setClearAttr( { defaultColor: true , bgDefaultColor: true } ) ;

	// To avoid mistake, cx/cy are starting at 0, like the internal screenBuffer does
	this.cx = 0 ;
	this.cy = 0 ;
	this.savedCx = 0 ;
	this.savedCy = 0 ;

	this.attr = 0 ;
	this.resetAttr() ;

	this.scrollingRegion = null ;
	this.tabWidth = 8 ;
	this.mouseEvent = null ;
	this.focusEvent = false ;
	this.mouseIsDragging = false ;

	this.eventInput = options.eventInput ;
	this.childSequencesReader = new SequencesReader() ;
	this.childProcess = null ;

	this.onEventInputKey = this.onEventInputKey.bind( this ) ;
	this.onEventInputMouse = this.onEventInputMouse.bind( this ) ;
	this.onEventInputTerminal = this.onEventInputTerminal.bind( this ) ;

	this.onChildOutputReset = this.onChildOutputReset.bind( this ) ;
	this.onChildOutputChar = this.onChildOutputChar.bind( this ) ;
	this.onChildOutputCursor = this.onChildOutputCursor.bind( this ) ;
	this.onChildOutputEdit = this.onChildOutputEdit.bind( this ) ;
	this.onChildOutputAttr = this.onChildOutputAttr.bind( this ) ;
	this.onChildOutputPalette = this.onChildOutputPalette.bind( this ) ;
	this.onChildOutputCursorAttr = this.onChildOutputCursorAttr.bind( this ) ;
	this.onChildOutputBell = this.onChildOutputBell.bind( this ) ;
	this.onChildOutputDevice = this.onChildOutputDevice.bind( this ) ;
	this.onChildOutputSystem = this.onChildOutputSystem.bind( this ) ;

	this.onChildOutputControl = this.onChildOutputControl.bind( this ) ;
	this.onChildOutputESC = this.onChildOutputESC.bind( this ) ;
	this.onChildOutputCSI = this.onChildOutputCSI.bind( this ) ;
	this.onChildOutputOSC = this.onChildOutputOSC.bind( this ) ;

	// The real draw is sync, so there is no need for Promise.debounceUpdate(), Promise.debounce() avoid an extra draw with no delta
	//this.drawDebounce = Promise.debounceUpdate( this.drawDelay.bind( this ) ) ;
	this.drawDebounce = Promise.debounce( this.drawDelay.bind( this ) ) ;
}

module.exports = Vte ;

Vte.prototype = Object.create( NextGenEvents.prototype ) ;
Vte.prototype.constructor = Vte ;



// Run a child process
Vte.prototype.run = function( command , args ) {
	var childPty , child ,
		promise = new Promise() ;

	if ( this.childProcess ) { return ; }

	this.start() ;

	try {
		// If child_pty is present, then use it instead of spawn
		// So programs launched would think they truly run inside a TTY
		childPty = require( 'child_pty' ) ;
		child = childPty.spawn( command , args , {
			columns: this.width ,
			rows: this.height
			//stdio: [ 'pty' , 'pty' , 'pty' ]
		} ) ;
	}
	catch ( error ) {
		logRed( "'child_pty' optional dependency not found, using regular child_process.spawn()" ) ;
		child = spawn( command , args ) ;
	}

	this.childProcess = child ;

	//this.on( 'input' , data => { log( 'stdin:' , data ) ; child.stdin.write( data ) ; } ) ;
	this.on( 'input' , data => child.stdin.write( data ) ) ;
	this.childSequencesReader.streamToEvent( child.stdout ) ;

	//child.stdout.on( 'data' , this.onChildOutput ) ;
	//child.stderr.on( 'data' , this.onChildOutput ) ;

	// Tmp, to close the app during alpha phase
	child.on( 'close' , code => {
		this.childProcess = null ;
		promise.resolve() ;
	} ) ;

	return promise ;
} ;



Vte.prototype.start = function() {
	if ( this.eventInput ) {
		this.eventInput.on( 'key' , this.onEventInputKey ) ;
		this.eventInput.on( 'mouse' , this.onEventInputMouse ) ;
		this.eventInput.on( 'terminal' , this.onEventInputTerminal ) ;
	}

	this.childSequencesReader.on( 'reset' , this.onChildOutputReset ) ;
	this.childSequencesReader.on( 'char' , this.onChildOutputChar ) ;
	this.childSequencesReader.on( 'cursor' , this.onChildOutputCursor ) ;
	this.childSequencesReader.on( 'edit' , this.onChildOutputEdit ) ;
	this.childSequencesReader.on( 'attr' , this.onChildOutputAttr ) ;
	this.childSequencesReader.on( 'palette' , this.onChildOutputPalette ) ;
	this.childSequencesReader.on( 'cursorAttr' , this.onChildOutputCursorAttr ) ;
	this.childSequencesReader.on( 'bell' , this.onChildOutputBell ) ;
	this.childSequencesReader.on( 'device' , this.onChildOutputDevice ) ;
	this.childSequencesReader.on( 'system' , this.onChildOutputSystem ) ;

	this.childSequencesReader.on( 'control' , this.onChildOutputControl ) ;
	this.childSequencesReader.on( 'ESC' , this.onChildOutputESC ) ;
	this.childSequencesReader.on( 'CSI' , this.onChildOutputCSI ) ;
	this.childSequencesReader.on( 'OSC' , this.onChildOutputOSC ) ;
} ;



Vte.prototype.draw = function() {
	var stats = this.screenBuffer.draw( { delta: true } ) ;
	this.screenBuffer.drawCursor() ;
	log( 'draw stats:' , stats ) ;
} ;



// Full redraw, no delta
Vte.prototype.redraw = function() {
	var stats = this.screenBuffer.draw( { delta: false } ) ;
	this.screenBuffer.drawCursor() ;
	log( 'redraw stats:' , stats ) ;
} ;



Vte.prototype.drawDelay = async function() {
	//await Promise.resolveTimeout( 10 ) ;
	await Promise.resolveNextTick() ;
	this.draw() ;
} ;



Vte.prototype.putChar = function( char ) {
	var charCode = char.charCodeAt( 0 ) ; log( 'putChar:' , charCode <= 0x1f || charCode === 0x7f ? '(ctrl)' : char , charCode >= 0x10 ? '\\x' + charCode.toString( 16 ) : '\\x0' + charCode.toString( 16 ) , 'at:' , this.cx , this.cy ) ;
	this.screenBuffer.put( { x: this.cx , y: this.cy , attr: this.attr } , char ) ;
	this.cx ++ ;

	if ( this.cx >= this.width ) {
		this.newLine() ;
	}
	else {
		this.drawDebounce() ;
	}
} ;



// internal = don't adjust
Vte.prototype.moveCursorTo = function( x , y , internal = false ) {
	if ( internal ) {
		if ( x !== undefined ) { this.cx = x ; }
		if ( y !== undefined ) { this.cy = y ; }
	}
	else {
		if ( x !== undefined ) { this.cx = x - 1 ; }
		if ( y !== undefined ) { this.cy = y - 1 ; }
	}

	if ( this.cx < 0 ) { this.cx = 0 ; }
	else if ( this.cx >= this.width - 1 ) { this.cx = this.width - 1 ; }

	if ( this.cy < 0 ) { this.cy = 0 ; }
	else if ( this.cy >= this.height - 1 ) { this.cy = this.height - 1 ; }

	this.screenBuffer.cx = this.cx ;
	this.screenBuffer.cy = this.cy ;
	this.screenBuffer.drawCursor() ;
} ;



// Relative move
Vte.prototype.moveCursor = function( x , y ) {
	this.moveCursorTo( this.cx + x , this.cy + y , true ) ;
} ;



// Next horizontal tab
Vte.prototype.nextTab = function() {
	this.moveCursorTo( Math.ceil( ( this.cx + 1 ) / this.tabWidth ) * this.tabWidth , undefined , true ) ;
} ;



Vte.prototype.vScroll = function( lineOffset , noDraw ) {
	var ymin = 0 ,
		ymax = this.height - 1 ;

	if ( this.scrollingRegion && this.cy >= this.scrollingRegion.ymin && this.cy <= this.scrollingRegion.ymax ) {
		( { ymin , ymax } = this.scrollingRegion ) ;
	}

	log( '################### vScroll:' , lineOffset , ymin , ymax  ) ;
	this.screenBuffer.vScroll( lineOffset , this.attr , ymin , ymax , true ) ;

	if ( ! noDraw ) { this.drawDebounce() ; }
} ;



Vte.prototype.lineFeed = function( carriageReturn , noDraw ) {
	var ymin = 0 ,
		ymax = this.height - 1 ;

	if ( this.scrollingRegion && this.cy >= this.scrollingRegion.ymin && this.cy <= this.scrollingRegion.ymax ) {
		( { ymin , ymax } = this.scrollingRegion ) ;
	}

	this.screenBuffer.cy = ++ this.cy ;

	if ( carriageReturn ) {
		this.screenBuffer.cx = this.cx = 0 ;
	}

	if ( this.cy > ymax ) {
		// Scroll up!
		// This will immediately scroll the underlying terminal using the scrolling region capability
		this.screenBuffer.cy = this.cy = ymax ;
		this.vScroll( -1 , noDraw ) ;
		if ( ! noDraw ) { this.screenBuffer.drawCursor() ; }
	}
	else if ( ! noDraw ) {
		this.drawDebounce() ;
	}
} ;

Vte.prototype.newLine = function( noDraw ) { return this.lineFeed( true , noDraw ) ; } ;



Vte.prototype.reverseLineFeed = function( carriageReturn , noDraw ) {
	var ymin = 0 ,
		ymax = this.height - 1 ;

	if ( this.scrollingRegion && this.cy >= this.scrollingRegion.ymin && this.cy <= this.scrollingRegion.ymax ) {
		( { ymin , ymax } = this.scrollingRegion ) ;
	}

	this.screenBuffer.cy = -- this.cy ;

	if ( carriageReturn ) {
		this.screenBuffer.cx = this.cx = 0 ;
	}

	if ( this.cy < ymin ) {
		// Scroll down!
		// This will immediately scroll the underlying terminal using the scrolling region capability
		this.screenBuffer.cy = this.cy = ymin ;
		this.vScroll( 1 , noDraw ) ;
		if ( ! noDraw ) { this.screenBuffer.drawCursor() ; }
	}
	else if ( ! noDraw ) {
		this.drawDebounce() ;
	}
} ;



Vte.prototype.eraseLine = function( mode , noDraw ) {
	if ( mode === 'after' ) {
		this.screenBuffer.fill( {
			region: {
				xmin: this.cx , xmax: this.width - 1 , ymin: this.cy , ymax: this.cy
			} ,
			attr: this.attr
		} ) ;
	}
	else if ( mode === 'before' ) {
		this.screenBuffer.fill( {
			region: {
				xmin: 0 , xmax: this.cx , ymin: this.cy , ymax: this.cy
			} ,
			attr: this.attr
		} ) ;
	}
	else {
		this.screenBuffer.fill( {
			region: {
				xmin: 0 , xmax: this.width - 1 , ymin: this.cy , ymax: this.cy
			} ,
			attr: this.attr
		} ) ;
	}

	if ( ! noDraw ) { this.drawDebounce() ; }
} ;



Vte.prototype.eraseDisplay = function( mode , noDraw ) {
	if ( mode === 'after' ) {
		// First, erase the current line from the cursor
		this.screenBuffer.fill( {
			region: {
				xmin: this.cx , xmax: this.width - 1 , ymin: this.cy , ymax: this.cy
			} ,
			attr: this.attr
		} ) ;
		// Then, erase all lines below the current line
		this.screenBuffer.fill( {
			region: {
				xmin: 0 , xmax: this.width - 1 , ymin: this.cy + 1 , ymax: this.height - 1
			} ,
			attr: this.attr
		} ) ;
	}
	else if ( mode === 'before' ) {
		// First, erase all lines above the current line
		this.screenBuffer.fill( {
			region: {
				xmin: 0 , xmax: this.width - 1 , ymin: 0 , ymax: this.cy - 1
			} ,
			attr: this.attr
		} ) ;
		// Then, erase the current line up to the cursor
		this.screenBuffer.fill( {
			region: {
				xmin: 0 , xmax: this.cx , ymin: this.cy , ymax: this.cy
			} ,
			attr: this.attr
		} ) ;
	}
	else {
		this.screenBuffer.fill( { attr: this.attr } ) ;
	}

	if ( ! noDraw ) { this.drawDebounce() ; }
} ;



Vte.prototype.backDelete = function( count = 1 ) {
	if ( count > this.cx ) { count = this.cx ; }
	if ( count <= 0 ) { return ; }

	// Shift the end of the line
	this.screenBuffer.copyRegion( {
		xmin: this.cx , ymin: this.cy , xmax: this.width - 1 , ymax: this.cy
	} , { x: this.cx - count , y: this.cy } ) ;
	this.cx -= count ;

	// Fill the end of the line with blank
	this.screenBuffer.fill( { region: {
		xmin: this.width - count , ymin: this.cy , xmax: this.width - 1 , ymax: this.cy
	} ,
	attr: this.attr } , ' ' ) ;

	this.screenBuffer.cx = this.cx ;

	this.drawDebounce() ;
} ;



Vte.prototype.delete = function( count = 1 ) {
	if ( count > this.width - this.cx ) { count = this.width - this.cx ; }
	if ( count <= 0 ) { return ; }

	// Shift the end of the line
	if ( this.cx + count < this.width ) {
		this.screenBuffer.copyRegion( {
			xmin: this.cx + count , ymin: this.cy , xmax: this.width - 1 , ymax: this.cy
		} , { x: this.cx , y: this.cy } ) ;
		log( "delete:" , count , "copy region:" , {
			xmin: this.cx + count , ymin: this.cy , xmax: this.width - 1 , ymax: this.cy
		} , { x: this.cx , y: this.cy } ) ;
	}

	// Fill the end of the line with blank
	this.screenBuffer.fill( { region: {
		xmin: this.width - count , ymin: this.cy , xmax: this.width - 1 , ymax: this.cy
	} ,
	attr: this.attr } , ' ' ) ;
	log( "delete:" , count , "fill region:" , {
		xmin: this.width - count , ymin: this.cy , xmax: this.width - 1 , ymax: this.cy
	} ) ;

	this.drawDebounce() ;
} ;



Vte.prototype.erase = function( count = 1 ) {
	if ( count > this.width - this.cx ) { count = this.width - this.cx ; }
	if ( count <= 0 ) { return ; }

	// Fill with blank
	this.screenBuffer.fill( { region: {
		xmin: this.cx , ymin: this.cy , xmax: this.cx + count - 1 , ymax: this.cy
	} ,
	attr: this.attr } , ' ' ) ;
	log( "erase:" , count , "fill region:" , {
		xmin: this.cx , ymin: this.cy , xmax: this.cx + count - 1 , ymax: this.cy
	} ) ;

	this.drawDebounce() ;
} ;



Vte.prototype.deleteLine = function( count = 1 ) {
	if ( count > this.height - this.cy ) { count = this.height - this.cy ; }
	if ( count <= 0 ) { return ; }

	// Shift from the end of the screen
	if ( this.cy + count < this.height ) {
		this.screenBuffer.copyRegion( {
			xmin: 0 , ymin: this.cy + count , xmax: this.width - 1 , ymax: this.height - 1
		} , { x: 0 , y: this.cy } ) ;
		log( "deleteLine:" , count , "copy region:" , {
			xmin: 0 , ymin: this.cy + count , xmax: this.width - 1 , ymax: this.height - 1
		} , { x: 0 , y: this.cy } ) ;
	}

	// Fill the end of the screen with blank
	this.screenBuffer.fill( { region: {
		xmin: 0 , ymin: this.height - count , xmax: this.width - 1 , ymax: this.height - 1
	} ,
	attr: this.attr } , ' ' ) ;
	log( "deleteLine:" , count , "fill region:" , {
		xmin: 0 , ymin: this.height - count , xmax: this.width - 1 , ymax: this.height - 1
	} ) ;

	// This move x to the left
	this.cx = this.screenBuffer.cx = 0 ;

	this.drawDebounce() ;
} ;



Vte.prototype.insertLine = function( count = 1 ) {
	if ( count > this.height - this.cy ) { count = this.height - this.cy ; }
	if ( count <= 0 ) { return ; }

	// Shift to the end of the screen
	if ( this.cy + count < this.height ) {
		//this.screenBuffer.copyRegion( { xmin: 0 , ymin: this.cy + count , xmax: this.width - 1 , ymax: this.height - 1 } , { x: 0 , y: this.cy } ) ;
		this.screenBuffer.copyRegion( {
			xmin: 0 , ymin: this.cy , xmax: this.width - 1 , ymax: this.height - count
		} , { x: 0 , y: this.cy + count } ) ;
		log( "insertLine:" , count , "copy region:" , {
			xmin: 0 , ymin: this.cy + count , xmax: this.width - 1 , ymax: this.height - 1
		} , { x: 0 , y: this.cy } ) ;
	}

	// Fill the inserted lines with blank
	//this.screenBuffer.fill( { region: { xmin: 0 , ymin: this.height - count , xmax: this.width - 1 , ymax: this.height - 1 } , attr: this.attr } , ' ' ) ;
	this.screenBuffer.fill( { region: {
		xmin: 0 , ymin: this.cy , xmax: this.width - 1 , ymax: this.cy + count
	} ,
	attr: this.attr } , ' ' ) ;
	log( "insertLine:" , count , "fill region:" , {
		xmin: 0 , ymin: this.height - count , xmax: this.width - 1 , ymax: this.height - 1
	} ) ;

	// This move x to the left
	this.cx = this.screenBuffer.cx = 0 ;

	this.drawDebounce() ;
} ;



Vte.prototype.resetAttr = function() { this.attr = this.screenBuffer.DEFAULT_ATTR ; } ;
Vte.prototype.setAttr = function( attrObject ) { this.attr = this.screenBuffer.object2attr( attrObject ) ; } ;
Vte.prototype.addAttr = function( attrObject ) { this.attr = this.screenBuffer.attrAndObject( this.attr , attrObject ) ; } ;



Vte.prototype.setVScrollingRegion = function( ymin = null , ymax = null , internal = false ) {
	log( "########################### setVScrollingRegion:" , ymin , ymax , internal ) ;
	if ( ymin === null || ymax === null ) {
		this.scrollingRegion = null ;
	}
	else if ( internal ) {
		this.scrollingRegion = new Rect( 0 , Math.max( 0 , ymin ) , this.width - 1 , Math.min( this.height - 1 , ymax ) ) ;
	}
	else {
		this.scrollingRegion = new Rect( 0 , Math.max( 0 , ymin - 1 ) , this.width - 1 , Math.min( this.height - 1 , ymax - 1 ) ) ;
	}
	log( "########################### setVScrollingRegion region:" , this.scrollingRegion ) ;
} ;



// Emit cursor location escape sequence
Vte.prototype.emitCursorLocation = function( decVariant ) {
	//this.emit( 'input' , '\x1b[' + ( decVariant ? '?' : '' ) + this.cy + ';' + this.cx + 'R' ) ;
	this.emit( 'input' , string.format( toInputSequence.reports[ decVariant ? 'cursorLocationDecVariant' : 'cursorLocation' ] , this.cx , this.cy ) ) ;
} ;



// Emit the screen size
Vte.prototype.emitScreenSize = function( decVariant ) {
	this.emit( 'input' , string.format( toInputSequence.reports.screenSize , this.width , this.height ) ) ;
} ;



// Emit the focus
Vte.prototype.emitFocus = function( isIn ) {
	this.emit( 'input' , toInputSequence.reports[ isIn ? 'focusIn' : 'focusOut' ] ) ;
} ;



// Emit the focus
Vte.prototype.emitRegisterColor = function( register ) {
	logRed( "emitRegisterColor" , register ) ;
	var rgb = this.screenBuffer.palette.getRgb( register ) ;
	logRed( "emitRegisterColor >>> " , rgb ) ;
	if ( ! rgb ) { return ; }
	this.emit( 'input' , string.format( toInputSequence.reports.registerColor , register , rgb.r , rgb.g , rgb.b ) ) ;
} ;



// Emit mouse event escape sequence using the SGR protocol
Vte.prototype.emitMouseSGR = function( type , data ) {
	var code = 0 , released = false ;

	if ( data.shift ) { code |= 4 ; }
	if ( data.alt ) { code |= 8 ; }
	if ( data.ctrl ) { code |= 16 ; }

	switch ( type ) {
		case 'MOUSE_LEFT_BUTTON_PRESSED' :
			break ;
		case 'MOUSE_MIDDLE_BUTTON_PRESSED' :
			code |= 1 ;
			break ;
		case 'MOUSE_RIGHT_BUTTON_PRESSED' :
			code |= 2 ;
			break ;
		case 'MOUSE_OTHER_BUTTON_PRESSED' :
			code |= 3 ;
			break ;
		case 'MOUSE_LEFT_BUTTON_RELEASED' :
			released = true ;
			break ;
		case 'MOUSE_MIDDLE_BUTTON_RELEASED' :
			code |= 1 ;
			released = true ;
			break ;
		case 'MOUSE_RIGHT_BUTTON_RELEASED' :
			code |= 2 ;
			released = true ;
			break ;
		case 'MOUSE_OTHER_BUTTON_RELEASED' :
			code |= 3 ;
			released = true ;
			break ;
		case 'MOUSE_WHEEL_UP' :
			code |= 64 ;
			break ;
		case 'MOUSE_WHEEL_DOWN' :
			code |= 65 ;
			break ;
		case 'MOUSE_MOTION' :
			code |= 32 ;
			break ;
	}

	this.emit( 'input' , '\x1b[<' + code + ';' + data.x + ';' + data.y + ( released ? 'm' : 'M' ) ) ;
} ;



// Event handlers



Vte.prototype.onEventInputKey = function( key , altKeys , data ) {
	log( 'onEventInputKey:' , key ) ;
	if ( data.isCharacter ) {
		this.emit( 'input' , key ) ;
	}
	else if ( toInputSequence.specialKeys[ key ] ) {
		this.emit( 'input' , toInputSequence.specialKeys[ key ] ) ;
	}
} ;



Vte.prototype.onEventInputMouse = function( type , data ) {
	if ( ! this.mouseEvent ) { return ; }
	//log( 'onEventInputMouse:' , type , data ) ;

	// /!\ Not sure if it's the most reliable way to do that
	if ( this.eventInput === this.screenBuffer.dst ) {
		// We MUST add an offset to the coordinate
		data.x -= this.screenBuffer.x - 1 ;
		data.y -= this.screenBuffer.y - 1 ;
	}

	switch ( type ) {
		case 'MOUSE_LEFT_BUTTON_PRESSED' :
		case 'MOUSE_MIDDLE_BUTTON_PRESSED' :
		case 'MOUSE_RIGHT_BUTTON_PRESSED' :
		case 'MOUSE_OTHER_BUTTON_PRESSED' :
			this.mouseIsDragging = true ;
			this.emitMouseSGR( type , data ) ;
			break ;
		case 'MOUSE_LEFT_BUTTON_RELEASED' :
		case 'MOUSE_MIDDLE_BUTTON_RELEASED' :
		case 'MOUSE_RIGHT_BUTTON_RELEASED' :
		case 'MOUSE_OTHER_BUTTON_RELEASED' :
			this.mouseIsDragging = false ;
			this.emitMouseSGR( type , data ) ;
			break ;
		case 'MOUSE_WHEEL_UP' :
		case 'MOUSE_WHEEL_DOWN' :
			this.emitMouseSGR( type , data ) ;
			break ;
		case 'MOUSE_MOTION' :
			if ( this.mouseEvent === 'motion' || ( this.mouseEvent === 'drag' && this.mouseIsDragging ) ) {
				this.emitMouseSGR( type , data ) ;
			}
			break ;
	}
} ;



Vte.prototype.onEventInputTerminal = function( type , data ) {
	switch ( type ) {
		case 'FOCUS_IN' :
			if ( this.focusEvent ) { this.emitFocus( true ) ; }
			break ;
		case 'FOCUS_OUT' :
			if ( this.focusEvent ) { this.emitFocus( false ) ; }
			break ;
	}
} ;



Vte.prototype.onChildOutputReset = function() {
	logRed( 'full reset' ) ;
} ;



Vte.prototype.onChildOutputChar = function( char , charCode ) {
	//log( '>>> put char charCode:' , charCode ) ;
	//log( 'put char:' , charCode <= 0x1f || charCode === 0x7f ? '(ctrl)' : char , charCode >= 0x10 ? '\\x' + charCode.toString( 16 ) : '\\x0' + charCode.toString( 16 ) ) ;
	this.putChar( char ) ;
} ;



Vte.prototype.onChildOutputCursor = function( subType , arg , extraArgs ) {
	log( 'cursor:' , subType , arg , extraArgs ) ;

	var arg1 = extraArgs && extraArgs[ 0 ] ? + extraArgs[ 0 ] : undefined ;
	var arg2 = extraArgs && extraArgs[ 1 ] ? + extraArgs[ 1 ] : undefined ;

	switch ( subType ) {
		//case 'newLine' : return this.newLine() ;
		case 'lineFeed' :
			return this.lineFeed() ;
		case 'carriageReturn' :
			return this.moveCursorTo( 0 , undefined , true ) ;
		case 'tab' :
			return this.nextTab() ;
		case 'move' :
			// unused
			this.moveCursor( arg1 , arg2 ) ;
			break ;
		case 'up' :
			this.moveCursor( 0 , -arg1 ) ;
			break ;
		case 'down' :
			this.moveCursor( 0 , arg1 ) ;
			break ;
		case 'right' :
			this.moveCursor( arg1 , 0 ) ;
			break ;
		case 'left' :
			this.moveCursor( -arg1 , 0 ) ;
			break ;
		case 'moveToYX' :
			// Swap the args
			this.moveCursorTo( arg2 , arg1 ) ;
			break ;
		case 'column' :
			this.moveCursorTo( arg1 ) ;
			break ;
		case 'row' :
			this.moveCursorTo( undefined , arg1 ) ;
			break ;
		case 'previousLine' :
			this.moveCursor( -this.cx , -arg1 ) ;
			break ;
		case 'nextLine' :
			this.moveCursor( -this.cx , arg1 ) ;
			break ;
		case 'save' :
			this.savedCx = this.cx ;
			this.savedCy = this.cy ;
			break ;
		case 'restore' :
			this.moveCursorTo( this.savedCx , this.savedCy , true ) ;
			break ;
		default :
			logRed( 'Unknown/unsupported cursor action' , subType , arg , extraArgs ) ;
	}
} ;



Vte.prototype.onChildOutputEdit = function( subType , arg , extraArgs ) {
	var arg1 = extraArgs && extraArgs[ 0 ] ? + extraArgs[ 0 ] : undefined ;
	var arg2 = extraArgs && extraArgs[ 1 ] ? + extraArgs[ 1 ] : undefined ;

	switch ( subType ) {
		case 'backDelete' :
			log( 'backDelete' , arg1 ) ;
			return this.backDelete( arg1 ) ;
		case 'delete' :
			log( 'delete' , arg1 ) ;
			return this.delete( arg1 ) ;
		case 'erase' :
			log( 'erase' , arg ) ;
			this.erase( arg ) ;
			break ;
		case 'deleteLine' :
			log( 'deleteLine' , arg1 ) ;
			this.deleteLine( arg1 ) ;
			break ;
		case 'insertLine' :
			log( 'insertLine' , arg1 ) ;
			this.insertLine( arg1 ) ;
			break ;
		case 'eraseLine' :
			log( 'eraseLine' , arg ) ;
			this.eraseLine( arg ) ;
			break ;
		case 'eraseDisplay' :
			log( 'eraseDisplay' , arg ) ;
			this.eraseDisplay( arg ) ;
			break ;
		case 'reverseLineFeed' :
			log( 'reverseLineFeed' ) ;
			this.reverseLineFeed( arg ) ;
			break ;
		case 'vScrollingRegion' :
			log( 'vScrollingRegion' , arg1 , arg2 ) ;
			this.setVScrollingRegion( arg1 , arg2 ) ;
			//this.setVScrollingRegion( arg1 , arg2 , true ) ;
			break ;
		case 'vScrollUp' :
			log( 'vScrollUp' , arg1 ) ;
			this.vScroll( -arg1 ) ;
			break ;
		case 'vScrollDown' :
			log( 'vScrollDown' , arg1 ) ;
			this.vScroll( arg1 ) ;
			break ;
		default :
			logRed( 'Unknown/unsupported edit action' , subType , arg , extraArgs ) ;
	}
} ;



Vte.prototype.onChildOutputAttr = function( subType , arg , extraArgs ) {
	switch ( subType ) {
		case 'reset' :
			log( 'ATTR reset' ) ;
			this.resetAttr() ;
			break ;
		case 'bold' :
			log( 'ATTR bold:' , arg ) ;
			this.addAttr( { bold: arg } ) ;
			break ;
		case 'dim' :
			log( 'ATTR dim:' , arg ) ;
			this.addAttr( { dim: arg } ) ;
			break ;
		case 'italic' :
			log( 'ATTR italic:' , arg ) ;
			this.addAttr( { italic: arg } ) ;
			break ;
		case 'underline' :
			log( 'ATTR underline:' , arg ) ;
			this.addAttr( { underline: arg } ) ;
			break ;
		case 'blink' :
			log( 'ATTR blink:' , arg ) ;
			this.addAttr( { blink: arg } ) ;
			break ;
		case 'inverse' :
			log( 'ATTR inverse:' , arg ) ;
			this.addAttr( { inverse: arg } ) ;
			break ;
		case 'hidden' :
			log( 'ATTR hidden:' , arg ) ;
			this.addAttr( { hidden: arg } ) ;
			break ;
		case 'strike' :
			log( 'ATTR strike:' , arg ) ;
			this.addAttr( { strike: arg } ) ;
			break ;
		case 'noDimNoBold' :
			log( 'ATTR noDimNoBold' ) ;
			this.addAttr( { bold: false , dim: false } ) ;
			break ;
		case 'color' :
			log( 'ATTR color:' , arg ) ;
			this.addAttr( { color: arg } ) ;
			break ;
		case 'color256' :
			log( 'ATTR color256:' , extraArgs ) ;
			this.addAttr( { color: + extraArgs[ 0 ] } ) ;
			break ;
		case 'colorRgb' :
			log( 'ATTR colorRgb:' , extraArgs , 'not supported ATM' ) ;
			break ;
		case 'bgColor' :
			log( 'ATTR bgColor:' , arg ) ;
			this.addAttr( { bgColor: arg } ) ;
			break ;
		case 'bgColor256' :
			log( 'ATTR bgColor256:' , extraArgs ) ;
			this.addAttr( { bgColor: + extraArgs[ 0 ] } ) ;
			break ;
		case 'bgColorRgb' :
			log( 'ATTR bgColorRgb:' , extraArgs , 'not supported ATM' ) ;
			break ;
		default :
			logRed( 'Unknown/unsupported ATTR' , subType , arg , extraArgs ) ;
	}
} ;



Vte.prototype.onChildOutputPalette = function( subType , extraArgs ) {
	logRed( 'Palette command:' , subType , extraArgs ) ;

	var arg1 = extraArgs && extraArgs[ 0 ] ? + extraArgs[ 0 ] : undefined ;

	switch ( subType ) {
		case 'getColor' :
			if ( ! isNaN( arg1 ) ) { this.emitRegisterColor( arg1 ) ; }
			break ;
	}
} ;



Vte.prototype.onChildOutputCursorAttr = function( subType , args ) {
	logRed( 'Cursor ATTR command:' , subType , args ) ;
} ;



Vte.prototype.onChildOutputBell = function() {
	logRed( 'bell' ) ;
} ;



Vte.prototype.onChildOutputDevice = function( subType , arg , extraArgs ) {
	logRed( 'Device command:' , subType , arg , extraArgs ) ;
	switch ( subType ) {
		case 'mouseButton' :
			this.mouseEvent = arg ? 'button' : null ;
			break ;
		case 'mouseDrag' :
			this.mouseEvent = arg ? 'drag' : null ;
			break ;
		case 'mouseMotion' :
			this.mouseEvent = arg ? 'motion' : null ;
			break ;
		case 'focusEvent' :
			this.focusEvent = !! arg ;
			break ;
		case 'cursorLocation' :
			// Arg is true for DEC mode (add a '?' to the sequence)
			this.emitCursorLocation( arg ) ;
			break ;
		case 'screenSize' :
			this.emitScreenSize( arg ) ;
			break ;
		default :
			logRed( 'Unknown/unsupported device command' , subType , arg , extraArgs ) ;
	}
} ;



Vte.prototype.onChildOutputSystem = function( subType , args ) {
	logRed( 'System command:' , subType , args ) ;
} ;



// Triggered when unknown/unsupported sequences are produced

Vte.prototype.onChildOutputControl = function( charCodeStr ) {
	logRed( 'control' , charCodeStr ) ;
} ;



Vte.prototype.onChildOutputESC = function( type , args ) {
	logRed( 'ESC -- type:' , type , args ) ;
} ;



Vte.prototype.onChildOutputCSI = function( type , args ) {
	logRed( 'CSI -- type:' , type , ', args:' , args ) ;
} ;



Vte.prototype.onChildOutputOSC = function( type , args ) {
	logRed( 'OSC -- type:' , type , ', args:' , args ) ;
} ;


},{"../Rect.js":2,"../ScreenBuffer.js":3,"./SequencesReader.js":58,"./toInputSequence.js":61,"child_process":148,"child_pty":148,"nextgen-events":78,"seventh":114,"string-kit":133}],60:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



// Control char (not escape sequence)
exports.control = {
	'\\x07': { event: 'bell' } ,

	// Looks like it just moves to the left
	//'\\x08': { event: 'edit' , subType: 'backDelete' , extraArgs: [ 1 ] } ,
	'\\x08': { event: 'cursor' , subType: 'left' , extraArgs: [ 1 ] } ,

	'\\x09': { event: 'cursor' , subType: 'tab' } ,
	'\\x0a': { event: 'cursor' , subType: 'lineFeed' } ,
	'\\x0d': { event: 'cursor' , subType: 'carriageReturn' } ,

	// Looks like it does nothing at all
	//'\\x7f': { event: 'edit' , subType: 'delete' , extraArgs: [ 1 ] } ,
	'\\x7f': { event: 'none' }
} ;



// ESC (simple ESC + char sequence)
exports.ESC = {
	'7': { event: 'cursor' , subType: 'save' } ,
	'8': { event: 'cursor' , subType: 'restore' } ,
	'c': { event: 'reset' } ,
	'M': { event: 'edit' , subType: 'reverseLineFeed' }
} ;



// CSI tree (Control Sequence Introducer)
exports.CSI = {
	A: { event: 'cursor' , subType: 'up' , defaultExtraArgs: [ 1 ] } ,
	B: { event: 'cursor' , subType: 'down' , defaultExtraArgs: [ 1 ] } ,
	C: { event: 'cursor' , subType: 'right' , defaultExtraArgs: [ 1 ] } ,
	D: { event: 'cursor' , subType: 'left' , defaultExtraArgs: [ 1 ] } ,
	E: { event: 'cursor' , subType: 'nextLine' , defaultExtraArgs: [ 1 ] } ,
	F: { event: 'cursor' , subType: 'previousLine' , defaultExtraArgs: [ 1 ] } ,
	G: { event: 'cursor' , subType: 'column' } ,
	H: { event: 'cursor' , subType: 'moveToYX' , defaultExtraArgs: [ 1 , 1 ] } ,

	J: {
		event: 'edit' ,
		subType: 'eraseDisplay' ,
		arg: 'after' ,
		subTree: {
			'0': { arg: 'after' } ,
			'1': { arg: 'before' } ,
			'2': { arg: 'display' }
		}
	} ,

	K: {
		event: 'edit' ,
		subType: 'eraseLine' ,
		arg: 'after' ,
		subTree: {
			'0': { arg: 'after' } ,
			'1': { arg: 'before' } ,
			'2': { arg: 'line' }
		}
	} ,

	L: { event: 'edit' , subType: 'insertLine' , defaultExtraArgs: [ 1 ] } ,
	M: { event: 'edit' , subType: 'deleteLine' , defaultExtraArgs: [ 1 ] } ,

	P: { event: 'edit' , subType: 'delete' , defaultExtraArgs: [ 1 ] } ,
	S: { event: 'edit' , subType: 'vScrollUp' , defaultExtraArgs: [ 1 ] } ,
	T: { event: 'edit' , subType: 'vScrollDown' , defaultExtraArgs: [ 1 ] } ,
	X: { event: 'edit' , subType: 'erase' , defaultExtraArgs: [ 1 ] } ,

	d: { event: 'cursor' , subType: 'row' } ,

	'?h': {
		event: 'device' ,
		arg: true ,
		subTree: {
			'1000': { subType: 'mouseButton' , continue: true } ,
			'1002': { subType: 'mouseDrag' , continue: true } ,
			'1003': { subType: 'mouseMotion' , continue: true } ,
			'1004': { subType: 'focusEvent' , continue: true } ,
			'1006': { event: 'none' , continue: true } 	// we only support SGR anyway
		}
	} ,
	'?l': {
		// This is the "turn off" counter-part of '?h' type, the subTree is copied from '?h' after the current assignment
		event: 'device' ,
		arg: false ,
		subTree: null
	} ,

	n: {
		// Device status report
		event: 'device' ,
		subTree: {
			'6': { subType: 'cursorLocation' }
		}
	} ,
	'?n': {
		// Device status report (again)
		event: 'device' ,
		subTree: {
			'6': { subType: 'cursorLocation' , arg: true }
		}
	} ,

	m: {
		// Known as SGR (Select Graphic Rendition)
		event: 'attr' ,
		subType: 'reset' ,	// if empty, it is usually a reset
		subTree: {
			'0': { subType: 'reset' , continue: true } ,

			'1': { subType: 'bold' , arg: true , continue: true } ,
			'2': { subType: 'dim' , arg: true , continue: true } ,
			'3': { subType: 'italic' , arg: true , continue: true } ,
			'4': { subType: 'underline' , arg: true , continue: true } ,
			'5': { subType: 'blink' , arg: true , continue: true } ,
			'7': { subType: 'inverse' , arg: true , continue: true } ,
			'8': { subType: 'hidden' , arg: true , continue: true } ,
			'9': { subType: 'strike' , arg: true , continue: true } ,

			'21': { subType: 'bold' , arg: false , continue: true } ,
			'22': { subType: 'noDimNoBold' , continue: true } ,
			'23': { subType: 'italic' , arg: false , continue: true } ,
			'24': { subType: 'underline' , arg: false , continue: true } ,
			'25': { subType: 'blink' , arg: false , continue: true } ,
			'27': { subType: 'inverse' , arg: false , continue: true } ,
			'28': { subType: 'hidden' , arg: false , continue: true } ,
			'29': { subType: 'strike' , arg: false , continue: true } ,

			'30': { subType: 'color' , arg: 'black' , continue: true } ,
			'31': { subType: 'color' , arg: 'red' , continue: true } ,
			'32': { subType: 'color' , arg: 'green' , continue: true } ,
			'33': { subType: 'color' , arg: 'yellow' , continue: true } ,
			'34': { subType: 'color' , arg: 'blue' , continue: true } ,
			'35': { subType: 'color' , arg: 'magenta' , continue: true } ,
			'36': { subType: 'color' , arg: 'cyan' , continue: true } ,
			'37': { subType: 'color' , arg: 'white' , continue: true } ,
			'38': {
				subTree: {
					'2': { subType: 'colorRgb' } ,
					'5': { subType: 'color256' }
				}
			} ,
			'39': { subType: 'color' , arg: 'default' , continue: true } ,

			'40': { subType: 'bgColor' , arg: 'black' , continue: true } ,
			'41': { subType: 'bgColor' , arg: 'red' , continue: true } ,
			'42': { subType: 'bgColor' , arg: 'green' , continue: true } ,
			'43': { subType: 'bgColor' , arg: 'yellow' , continue: true } ,
			'44': { subType: 'bgColor' , arg: 'blue' , continue: true } ,
			'45': { subType: 'bgColor' , arg: 'magenta' , continue: true } ,
			'46': { subType: 'bgColor' , arg: 'cyan' , continue: true } ,
			'47': { subType: 'bgColor' , arg: 'white' , continue: true } ,
			'48': {
				subTree: {
					'2': { subType: 'bgColorRgb' } ,
					'5': { subType: 'bgColor256' }
				}
			} ,
			'49': { subType: 'bgColor' , arg: 'default' , continue: true } ,

			'90': { subType: 'color' , arg: 'gray' , continue: true } ,
			'91': { subType: 'color' , arg: 'brightRed' , continue: true } ,
			'92': { subType: 'color' , arg: 'brightGreen' , continue: true } ,
			'93': { subType: 'color' , arg: 'brightYellow' , continue: true } ,
			'94': { subType: 'color' , arg: 'brightBlue' , continue: true } ,
			'95': { subType: 'color' , arg: 'brightMagenta' , continue: true } ,
			'96': { subType: 'color' , arg: 'brightCyan' , continue: true } ,
			'97': { subType: 'color' , arg: 'brightWhite' , continue: true } ,

			'100': { subType: 'bgColor' , arg: 'gray' , continue: true } ,
			'101': { subType: 'bgColor' , arg: 'brightRed' , continue: true } ,
			'102': { subType: 'bgColor' , arg: 'brightGreen' , continue: true } ,
			'103': { subType: 'bgColor' , arg: 'brightYellow' , continue: true } ,
			'104': { subType: 'bgColor' , arg: 'brightBlue' , continue: true } ,
			'105': { subType: 'bgColor' , arg: 'brightMagenta' , continue: true } ,
			'106': { subType: 'bgColor' , arg: 'brightCyan' , continue: true } ,
			'107': { subType: 'bgColor' , arg: 'brightWhite' , continue: true }
		}
	} ,

	r: { event: 'edit' , subType: 'vScrollingRegion' } ,
	t: {
		event: 'device' ,
		subTree: {
			'18': { subType: 'screenSize' }
		}
	}
} ;

exports.CSI['?l'].subTree = exports.CSI['?h'].subTree ;



// OSC tree (OS Command)
exports.OSC = {
	'0': { event: 'system' , subType: 'setWindowTitle' } ,
	'1': { event: 'system' , subType: 'setIconName' } ,
	'2': { event: 'system' , subType: 'setWindowTitle' } ,
	'4': { event: 'palette' , subType: 'setColor' } ,
	'4?': { event: 'palette' , subType: 'getColor' } ,
	'7': { event: 'system' , subType: 'setCwd' } ,
	'9': { event: 'system' , subType: 'notify' } ,	// iTerm2 growl notification, only a body argument
	'10': { event: 'palette' , subType: 'setDefaultColor' } ,
	'11': { event: 'palette' , subType: 'setDefaultBgColor' } ,
	'12': { event: 'cursorAttr' , subType: 'setColor' } ,
	'17': { event: 'palette' , subType: 'setHighlightBgColor' } ,
	'50': { event: 'cursorAttr' , subType: 'setShape' } ,
	'104': { event: 'palette' , subType: 'resetColor' } ,
	'110': { event: 'palette' , subType: 'resetDefaultColor' } ,
	'111': { event: 'palette' , subType: 'resetDefaultBgColor' } ,
	'112': { event: 'cursorAttr' , subType: 'resetColor' } ,
	'117': { event: 'palette' , subType: 'resetHighlightBgColor' } ,
	'777': {	// rxvt/urxvt module, only support notifications
		event: 'system' ,
		subTree: {
			'notify': { subType: 'notify' }	// notify with a title and body arguments
		}
	}
} ;


},{}],61:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



exports.specialKeys = {
	/*
		From the Xterm config (lib/termconfig/xterm.js).
		When multiple keys exist, the chosen one is (by order of preference):
			- ultimately the sequence that avoid overlapping
			- the more consistent sequence
			- the actual Gnome-terminal sequence
			- the actual Xterm sequence
	*/

	ESCAPE: '\x1b' ,
	TAB: '\x09' ,
	ENTER: '\x0d' ,

	SHIFT_TAB: '\x1b[Z' ,
	ALT_TAB: '\x1b\x09' ,	// Also CTRL_ALT_I, most of time it is grabbed by the window manager before reaching the terminal
	ALT_ENTER: '\x1b\x0d' ,

	UP: '\x1bOA' ,
	DOWN: '\x1bOB' ,
	RIGHT: '\x1bOC' ,
	LEFT: '\x1bOD' ,

	SHIFT_UP: '\x1b[1;2A' ,
	SHIFT_DOWN: '\x1b[1;2B' ,
	SHIFT_RIGHT: '\x1b[1;2C' ,
	SHIFT_LEFT: '\x1b[1;2D' ,
	ALT_UP: '\x1b[1;3A' ,
	ALT_DOWN: '\x1b[1;3B' ,
	ALT_RIGHT: '\x1b[1;3C' ,
	ALT_LEFT: '\x1b[1;3D' ,
	CTRL_UP: '\x1b[1;5A' ,
	CTRL_DOWN: '\x1b[1;5B' ,
	CTRL_RIGHT: '\x1b[1;5C' ,
	CTRL_LEFT: '\x1b[1;5D' ,

	BACKSPACE: '\x7f' ,
	INSERT: '\x1b[2~' ,
	DELETE: '\x1b[3~' ,
	HOME: '\x1b[1~' ,
	END: '\x1b[4~' ,
	PAGE_UP: '\x1b[5~' ,
	PAGE_DOWN: '\x1b[6~' ,

	CTRL_BACKSPACE: '\x08' ,
	CTRL_INSERT: '\x1b[2;5~' ,
	CTRL_DELETE: '\x1b[3;5~' ,
	CTRL_HOME: '\x1b[1;5~' ,
	CTRL_END: '\x1b[4;5~' ,
	CTRL_PAGE_UP: '\x1b[5;5~' ,
	CTRL_PAGE_DOWN: '\x1b[6;5~' ,

	SHIFT_INSERT: '\x1b[2;2~' ,
	SHIFT_DELETE: '\x1b[3;2~' ,
	SHIFT_HOME: '\x1b[1;2~' ,
	SHIFT_END: '\x1b[4;2~' ,
	SHIFT_PAGE_UP: '\x1b[5;2~' ,
	SHIFT_PAGE_DOWN: '\x1b[6;2~' ,

	ALT_BACKSPACE: '\x1b\x7f' ,
	ALT_INSERT: '\x1b[2;3~' ,
	ALT_DELETE: '\x1b[3;3~' ,
	ALT_HOME: '\x1b[1;3~' ,
	ALT_END: '\x1b[4;3~' ,
	ALT_PAGE_UP: '\x1b[5;3~' ,
	ALT_PAGE_DOWN: '\x1b[6;3~' ,

	// Application Keypad
	/*
	KP_NUMLOCK: [] ,	// '\x1bOP' ,
	KP_DIVIDE: '\x1bOo' ,
	KP_MULTIPLY: '\x1bOj' ,
	KP_MINUS: '\x1bOm' ,
	KP_0: [] ,	// '\x1b[2~' ,
	KP_1: [] ,	// '\x1bOF' ,
	KP_2: [] ,	// '\x1b[B' ,
	KP_3: [] ,	// '\x1b[6~' ,
	KP_4: [] ,	// '\x1b[D' ,
	KP_5: [ '\x1bOE' , '\x1b[E' ] ,
	KP_6: [] ,	// '\x1b[C' ,
	KP_7: [] ,	// '\x1bOH' ,
	KP_8: [] ,	// '\x1b[A' ,
	KP_9: [] ,	// '\x1b[5~' ,
	KP_PLUS: '\x1bOk' ,
	KP_DELETE: [] ,	// '\x1b[3~' ,
	KP_ENTER: '\x1bOM' ,
	*/

	F1: '\x1bOP' ,
	F2: '\x1bOQ' ,
	F3: '\x1bOR' ,
	F4: '\x1bOS' ,
	F5: '\x1b[15~' ,
	F6: '\x1b[17~' ,
	F7: '\x1b[18~' ,
	F8: '\x1b[19~' ,
	F9: '\x1b[20~' ,
	F10: '\x1b[21~' ,
	F11: '\x1b[23~' ,
	F12: '\x1b[24~' ,

	SHIFT_F1: '\x1bO1;2P' ,
	SHIFT_F2: '\x1bO1;2Q' ,
	SHIFT_F3: '\x1bO1;2R' ,
	SHIFT_F4: '\x1bO1;2S' ,
	SHIFT_F5: '\x1b[15;2~' ,
	SHIFT_F6: '\x1b[17;2~' ,
	SHIFT_F7: '\x1b[18;2~' ,
	SHIFT_F8: '\x1b[19;2~' ,
	SHIFT_F9: '\x1b[20;2~' ,
	SHIFT_F10: '\x1b[21;2~' ,
	SHIFT_F11: '\x1b[23;2~' ,
	SHIFT_F12: '\x1b[24;2~' ,

	CTRL_F1: '\x1bO1;5P' ,
	CTRL_F2: '\x1bO1;5Q' ,
	CTRL_F3: '\x1bO1;5R' ,	// '\x1b[1;5R' is also used for cursor location response... :/
	CTRL_F4: '\x1bO1;5S' ,
	CTRL_F5: '\x1b[15;5~' ,
	CTRL_F6: '\x1b[17;5~' ,
	CTRL_F7: '\x1b[18;5~' ,
	CTRL_F8: '\x1b[19;5~' ,
	CTRL_F9: '\x1b[20;5~' ,
	CTRL_F10: '\x1b[21;5~' ,
	CTRL_F11: '\x1b[23;5~' ,
	CTRL_F12: '\x1b[24;5~' ,

	CTRL_SHIFT_F1: '\x1bO1;6P' ,
	CTRL_SHIFT_F2: '\x1bO1;6Q' ,
	CTRL_SHIFT_F3: '\x1bO1;6R' ,
	CTRL_SHIFT_F4: '\x1bO1;6S' ,
	CTRL_SHIFT_F5: '\x1b[15;6~' ,
	CTRL_SHIFT_F6: '\x1b[17;6~' ,
	CTRL_SHIFT_F7: '\x1b[18;6~' ,
	CTRL_SHIFT_F8: '\x1b[19;6~' ,
	CTRL_SHIFT_F9: '\x1b[20;6~' ,
	CTRL_SHIFT_F10: '\x1b[21;6~' ,
	CTRL_SHIFT_F11: '\x1b[23;6~' ,
	CTRL_SHIFT_F12: '\x1b[24;6~' ,

	NUL: '\x00' ,

	//CTRL_SPACE: '\x00' ,	// also NUL
	ALT_SPACE: '\x1b ' ,
	CTRL_ALT_SPACE: '\x1b\x00'
} ;



// Complete with Modifier + [A-Z]
for ( let i = 1 ; i <= 26 ; i ++ ) {
	exports.specialKeys[ 'CTRL_' + String.fromCharCode( 64 + i ) ] = String.fromCharCode( i ) ;
	exports.specialKeys[ 'ALT_' + String.fromCharCode( 64 + i ) ] = '\x1b' + String.fromCharCode( 96 + i ) ;
	exports.specialKeys[ 'CTRL_ALT_' + String.fromCharCode( 64 + i ) ] = '\x1b' + String.fromCharCode( i ) ;
	exports.specialKeys[ 'ALT_SHIFT_' + String.fromCharCode( 64 + i ) ] = '\x1b' + String.fromCharCode( 64 + i ) ;
}



exports.reports = {
	cursorLocation: '\x1b[%+1u;%-1uR' ,
	cursorLocationDecVariant: '\x1b[?%+1u;%-1uR' ,
	screenSize: '\x1b[8;%+1u;%-1ut' ,
	focusIn: '\x1b[I' ,
	focusOut: '\x1b[O' ,
	registerColor: '\x1b]4;%u;rgb:%x/%x/%x\x07'  // register,r,g,b
} ;


},{}],62:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



/*
	Windows compatibility.
*/

module.exports = function( termkit ) {
	termkit.globalConfig.preferProcessSigwinch = true ;
} ;



},{}],63:[function(require,module,exports){
/*
	Terminal Kit

	Copyright (c) 2009 - 2022 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



var Promise = require( 'seventh' ) ;



/*
	yesOrNo( [yes] , [no] , callback )
		* options `Object`
			* yes `string` or `Array` contains a key code or an array of key code that will trigger the yes
			* no `string` or `Array` contains a key code or an array of key code that will trigger the no
			* echoYes `string` if defined this will be what will be outputed in case of yes
			* echoNo `string` if defined this will be what will be outputed in case of no
		* callback( error , result )
			* result: true for 'yes' or false for 'no'
*/
module.exports = function yesOrNo( options , callback ) {
	if ( typeof options === 'function' ) { callback = options ; options = undefined ; }

	if ( ! options || typeof options !== 'object' ) {
		options = {
			yes: [ 'y' , 'Y' ] ,
			no: [ 'n' , 'N' ] ,
			echoYes: 'yes' ,
			echoNo: 'no'
		} ;
	}

	if ( typeof options.yes === 'string' ) { options.yes = [ options.yes ] ; }
	if ( ! Array.isArray( options.yes ) ) { options.yes = [ 'y' , 'Y' ] ; }

	if ( typeof options.no === 'string' ) { options.no = [ options.no ] ; }
	if ( ! Array.isArray( options.no ) ) { options.no = [ 'n' , 'N' ] ; }

	if ( ! this.grabbing ) { this.grabInput() ; }

	var onKey = key => {
		if ( options.yes.indexOf( key ) !== -1 ) {
			if ( options.echoYes ) { this( options.echoYes ) ; }
			this.removeListener( 'key' , onKey ) ;

			if ( callback ) { callback( undefined , true ) ; }
			else { controller.promise.resolve( true ) ; }
		}
		else if ( options.no.indexOf( key ) !== -1 ) {
			if ( options.echoNo ) { this( options.echoNo ) ; }
			this.removeListener( 'key' , onKey ) ;

			if ( callback ) { callback( undefined , false ) ; }
			else { controller.promise.resolve( false ) ; }
		}
	} ;

	this.on( 'key' , onKey ) ;

	var controller = {} ; //Object.create( NextGenEvents.prototype ) ;

	// Stop everything and do not even call the callback
	controller.abort = () => {
		this.removeListener( 'key' , onKey ) ;
	} ;

	controller.promise = new Promise() ;

	return controller ;
} ;


},{"seventh":114}],64:[function(require,module,exports){
(function (Buffer){(function (){
'use strict'

var ndarray       = require('ndarray')
var PNG           = require('pngjs').PNG
var jpeg          = require('jpeg-js')
var pack          = require('ndarray-pack')
var GifReader     = require('omggif').GifReader
var Bitmap        = require('node-bitmap')
var fs            = require('fs')
var extname       = require('path').extname

function handlePNG(data, cb) {
  var png = new PNG();
  png.parse(data, function(err, img_data) {
    if(err) {
      cb(err)
      return
    }
    cb(null, ndarray(new Uint8Array(img_data.data),
      [img_data.width|0, img_data.height|0, 4],
      [4, 4*img_data.width|0, 1],
      0))
  })
}

function handleJPEG(data, cb) {
  var jpegData
  try {
    jpegData = jpeg.decode(data)
  }
  catch(e) {
    cb(e)
    return
  }
  if(!jpegData) {
    cb(new Error("Error decoding jpeg"))
    return
  }
  var nshape = [ jpegData.height, jpegData.width, 4 ]
  var result = ndarray(jpegData.data, nshape)
  cb(null, result.transpose(1,0))
}

function handleGIF(data, cb) {
  var reader, nshape, ndata, result
  try {
    reader = new GifReader(data)
  } catch(err) {
    cb(err)
    return
  }
  if(reader.numFrames() > 0) {
    nshape = [reader.numFrames(), reader.height, reader.width, 4]
    ndata = new Uint8Array(nshape[0] * nshape[1] * nshape[2] * nshape[3])
    result = ndarray(ndata, nshape)
    try {
      for(var i=0; i<reader.numFrames(); ++i) {
        reader.decodeAndBlitFrameRGBA(i, ndata.subarray(
          result.index(i, 0, 0, 0),
          result.index(i+1, 0, 0, 0)))
      }
    } catch(err) {
      cb(err)
      return
    }
    cb(null, result.transpose(0,2,1))
  } else {
    nshape = [reader.height, reader.width, 4]
    ndata = new Uint8Array(nshape[0] * nshape[1] * nshape[2])
    result = ndarray(ndata, nshape)
    try {
      reader.decodeAndBlitFrameRGBA(0, ndata)
    } catch(err) {
      cb(err)
      return
    }
    cb(null, result.transpose(1,0))
  }
}

function handleBMP(data, cb) {
  var bmp = new Bitmap(data)
  try {
    bmp.init()
  } catch(e) {
    cb(e)
    return
  }
  var bmpData = bmp.getData()
  var nshape = [ bmpData.getHeight(), bmpData.getWidth(), 4 ]
  var ndata = new Uint8Array(nshape[0] * nshape[1] * nshape[2])
  var result = ndarray(ndata, nshape)
  pack(bmpData, result)
  cb(null, result.transpose(1,0))
}


function doParse(type, data, cb) {
  switch(type) {
    case 'png':
      handlePNG(data, cb)
    break

    case 'jpg':
    case 'jpeg':
      handleJPEG(data, cb)
    break

    case 'gif':
      handleGIF(data, cb)
    break

    case 'bmp':
      handleBMP(data, cb)
    break

    default:
      cb(new Error("Unsupported file type: " + type))
  }
}

module.exports = function getPixels(url, type, cb) {
  if(!cb) {
    cb = type
    type = ''
  }
  if(Buffer.isBuffer(url)) {
    if(!type) {
      cb(new Error('Invalid file type'))
      return
    }
    doParse(type, url, cb)
  } else {
    fs.readFile(url, function(err, data) {
      if(err) {
        cb(err)
        return
      }
      type = type || extname(url).toLowerCase().substr(1)

      if(!type) {
        cb(new Error('Invalid file type'))
        return
      }
      doParse(type, data, cb)
    })
  }
}

}).call(this)}).call(this,{"isBuffer":require("../../../../../../../../opt/node-v14.15.4/lib/node_modules/browserify/node_modules/is-buffer/index.js")})
},{"../../../../../../../../opt/node-v14.15.4/lib/node_modules/browserify/node_modules/is-buffer/index.js":174,"fs":148,"jpeg-js":71,"ndarray":77,"ndarray-pack":75,"node-bitmap":81,"omggif":83,"path":190,"pngjs":103}],65:[function(require,module,exports){
/**
 * chroma.js - JavaScript library for color conversions
 *
 * Copyright (c) 2011-2019, Gregor Aisch
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name Gregor Aisch may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL GREGOR AISCH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * -------------------------------------------------------
 *
 * chroma.js includes colors from colorbrewer2.org, which are released under
 * the following license:
 *
 * Copyright (c) 2002 Cynthia Brewer, Mark Harrower,
 * and The Pennsylvania State University.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * ------------------------------------------------------
 *
 * Named colors are taken from X11 Color Names.
 * http://www.w3.org/TR/css3-color/#svg-color
 *
 * @preserve
 */

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.chroma = factory());
})(this, (function () { 'use strict';

    var limit$2 = function (x, min, max) {
        if ( min === void 0 ) min=0;
        if ( max === void 0 ) max=1;

        return x < min ? min : x > max ? max : x;
    };

    var limit$1 = limit$2;

    var clip_rgb$3 = function (rgb) {
        rgb._clipped = false;
        rgb._unclipped = rgb.slice(0);
        for (var i=0; i<=3; i++) {
            if (i < 3) {
                if (rgb[i] < 0 || rgb[i] > 255) { rgb._clipped = true; }
                rgb[i] = limit$1(rgb[i], 0, 255);
            } else if (i === 3) {
                rgb[i] = limit$1(rgb[i], 0, 1);
            }
        }
        return rgb;
    };

    // ported from jQuery's $.type
    var classToType = {};
    for (var i$1 = 0, list$1 = ['Boolean', 'Number', 'String', 'Function', 'Array', 'Date', 'RegExp', 'Undefined', 'Null']; i$1 < list$1.length; i$1 += 1) {
        var name = list$1[i$1];

        classToType[("[object " + name + "]")] = name.toLowerCase();
    }
    var type$p = function(obj) {
        return classToType[Object.prototype.toString.call(obj)] || "object";
    };

    var type$o = type$p;

    var unpack$B = function (args, keyOrder) {
        if ( keyOrder === void 0 ) keyOrder=null;

    	// if called with more than 3 arguments, we return the arguments
        if (args.length >= 3) { return Array.prototype.slice.call(args); }
        // with less than 3 args we check if first arg is object
        // and use the keyOrder string to extract and sort properties
    	if (type$o(args[0]) == 'object' && keyOrder) {
    		return keyOrder.split('')
    			.filter(function (k) { return args[0][k] !== undefined; })
    			.map(function (k) { return args[0][k]; });
    	}
    	// otherwise we just return the first argument
    	// (which we suppose is an array of args)
        return args[0];
    };

    var type$n = type$p;

    var last$4 = function (args) {
        if (args.length < 2) { return null; }
        var l = args.length-1;
        if (type$n(args[l]) == 'string') { return args[l].toLowerCase(); }
        return null;
    };

    var PI$2 = Math.PI;

    var utils = {
    	clip_rgb: clip_rgb$3,
    	limit: limit$2,
    	type: type$p,
    	unpack: unpack$B,
    	last: last$4,
    	PI: PI$2,
    	TWOPI: PI$2*2,
    	PITHIRD: PI$2/3,
    	DEG2RAD: PI$2 / 180,
    	RAD2DEG: 180 / PI$2
    };

    var input$h = {
    	format: {},
    	autodetect: []
    };

    var last$3 = utils.last;
    var clip_rgb$2 = utils.clip_rgb;
    var type$m = utils.type;
    var _input = input$h;

    var Color$D = function Color() {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var me = this;
        if (type$m(args[0]) === 'object' &&
            args[0].constructor &&
            args[0].constructor === this.constructor) {
            // the argument is already a Color instance
            return args[0];
        }

        // last argument could be the mode
        var mode = last$3(args);
        var autodetect = false;

        if (!mode) {
            autodetect = true;
            if (!_input.sorted) {
                _input.autodetect = _input.autodetect.sort(function (a,b) { return b.p - a.p; });
                _input.sorted = true;
            }
            // auto-detect format
            for (var i = 0, list = _input.autodetect; i < list.length; i += 1) {
                var chk = list[i];

                mode = chk.test.apply(chk, args);
                if (mode) { break; }
            }
        }

        if (_input.format[mode]) {
            var rgb = _input.format[mode].apply(null, autodetect ? args : args.slice(0,-1));
            me._rgb = clip_rgb$2(rgb);
        } else {
            throw new Error('unknown format: '+args);
        }

        // add alpha channel
        if (me._rgb.length === 3) { me._rgb.push(1); }
    };

    Color$D.prototype.toString = function toString () {
        if (type$m(this.hex) == 'function') { return this.hex(); }
        return ("[" + (this._rgb.join(',')) + "]");
    };

    var Color_1 = Color$D;

    var chroma$k = function () {
    	var args = [], len = arguments.length;
    	while ( len-- ) args[ len ] = arguments[ len ];

    	return new (Function.prototype.bind.apply( chroma$k.Color, [ null ].concat( args) ));
    };

    chroma$k.Color = Color_1;
    chroma$k.version = '2.4.2';

    var chroma_1 = chroma$k;

    var unpack$A = utils.unpack;
    var max$2 = Math.max;

    var rgb2cmyk$1 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var ref = unpack$A(args, 'rgb');
        var r = ref[0];
        var g = ref[1];
        var b = ref[2];
        r = r / 255;
        g = g / 255;
        b = b / 255;
        var k = 1 - max$2(r,max$2(g,b));
        var f = k < 1 ? 1 / (1-k) : 0;
        var c = (1-r-k) * f;
        var m = (1-g-k) * f;
        var y = (1-b-k) * f;
        return [c,m,y,k];
    };

    var rgb2cmyk_1 = rgb2cmyk$1;

    var unpack$z = utils.unpack;

    var cmyk2rgb = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        args = unpack$z(args, 'cmyk');
        var c = args[0];
        var m = args[1];
        var y = args[2];
        var k = args[3];
        var alpha = args.length > 4 ? args[4] : 1;
        if (k === 1) { return [0,0,0,alpha]; }
        return [
            c >= 1 ? 0 : 255 * (1-c) * (1-k), // r
            m >= 1 ? 0 : 255 * (1-m) * (1-k), // g
            y >= 1 ? 0 : 255 * (1-y) * (1-k), // b
            alpha
        ];
    };

    var cmyk2rgb_1 = cmyk2rgb;

    var chroma$j = chroma_1;
    var Color$C = Color_1;
    var input$g = input$h;
    var unpack$y = utils.unpack;
    var type$l = utils.type;

    var rgb2cmyk = rgb2cmyk_1;

    Color$C.prototype.cmyk = function() {
        return rgb2cmyk(this._rgb);
    };

    chroma$j.cmyk = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$C, [ null ].concat( args, ['cmyk']) ));
    };

    input$g.format.cmyk = cmyk2rgb_1;

    input$g.autodetect.push({
        p: 2,
        test: function () {
            var args = [], len = arguments.length;
            while ( len-- ) args[ len ] = arguments[ len ];

            args = unpack$y(args, 'cmyk');
            if (type$l(args) === 'array' && args.length === 4) {
                return 'cmyk';
            }
        }
    });

    var unpack$x = utils.unpack;
    var last$2 = utils.last;
    var rnd = function (a) { return Math.round(a*100)/100; };

    /*
     * supported arguments:
     * - hsl2css(h,s,l)
     * - hsl2css(h,s,l,a)
     * - hsl2css([h,s,l], mode)
     * - hsl2css([h,s,l,a], mode)
     * - hsl2css({h,s,l,a}, mode)
     */
    var hsl2css$1 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var hsla = unpack$x(args, 'hsla');
        var mode = last$2(args) || 'lsa';
        hsla[0] = rnd(hsla[0] || 0);
        hsla[1] = rnd(hsla[1]*100) + '%';
        hsla[2] = rnd(hsla[2]*100) + '%';
        if (mode === 'hsla' || (hsla.length > 3 && hsla[3]<1)) {
            hsla[3] = hsla.length > 3 ? hsla[3] : 1;
            mode = 'hsla';
        } else {
            hsla.length = 3;
        }
        return (mode + "(" + (hsla.join(',')) + ")");
    };

    var hsl2css_1 = hsl2css$1;

    var unpack$w = utils.unpack;

    /*
     * supported arguments:
     * - rgb2hsl(r,g,b)
     * - rgb2hsl(r,g,b,a)
     * - rgb2hsl([r,g,b])
     * - rgb2hsl([r,g,b,a])
     * - rgb2hsl({r,g,b,a})
     */
    var rgb2hsl$3 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        args = unpack$w(args, 'rgba');
        var r = args[0];
        var g = args[1];
        var b = args[2];

        r /= 255;
        g /= 255;
        b /= 255;

        var min = Math.min(r, g, b);
        var max = Math.max(r, g, b);

        var l = (max + min) / 2;
        var s, h;

        if (max === min){
            s = 0;
            h = Number.NaN;
        } else {
            s = l < 0.5 ? (max - min) / (max + min) : (max - min) / (2 - max - min);
        }

        if (r == max) { h = (g - b) / (max - min); }
        else if (g == max) { h = 2 + (b - r) / (max - min); }
        else if (b == max) { h = 4 + (r - g) / (max - min); }

        h *= 60;
        if (h < 0) { h += 360; }
        if (args.length>3 && args[3]!==undefined) { return [h,s,l,args[3]]; }
        return [h,s,l];
    };

    var rgb2hsl_1 = rgb2hsl$3;

    var unpack$v = utils.unpack;
    var last$1 = utils.last;
    var hsl2css = hsl2css_1;
    var rgb2hsl$2 = rgb2hsl_1;
    var round$6 = Math.round;

    /*
     * supported arguments:
     * - rgb2css(r,g,b)
     * - rgb2css(r,g,b,a)
     * - rgb2css([r,g,b], mode)
     * - rgb2css([r,g,b,a], mode)
     * - rgb2css({r,g,b,a}, mode)
     */
    var rgb2css$1 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var rgba = unpack$v(args, 'rgba');
        var mode = last$1(args) || 'rgb';
        if (mode.substr(0,3) == 'hsl') {
            return hsl2css(rgb2hsl$2(rgba), mode);
        }
        rgba[0] = round$6(rgba[0]);
        rgba[1] = round$6(rgba[1]);
        rgba[2] = round$6(rgba[2]);
        if (mode === 'rgba' || (rgba.length > 3 && rgba[3]<1)) {
            rgba[3] = rgba.length > 3 ? rgba[3] : 1;
            mode = 'rgba';
        }
        return (mode + "(" + (rgba.slice(0,mode==='rgb'?3:4).join(',')) + ")");
    };

    var rgb2css_1 = rgb2css$1;

    var unpack$u = utils.unpack;
    var round$5 = Math.round;

    var hsl2rgb$1 = function () {
        var assign;

        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];
        args = unpack$u(args, 'hsl');
        var h = args[0];
        var s = args[1];
        var l = args[2];
        var r,g,b;
        if (s === 0) {
            r = g = b = l*255;
        } else {
            var t3 = [0,0,0];
            var c = [0,0,0];
            var t2 = l < 0.5 ? l * (1+s) : l+s-l*s;
            var t1 = 2 * l - t2;
            var h_ = h / 360;
            t3[0] = h_ + 1/3;
            t3[1] = h_;
            t3[2] = h_ - 1/3;
            for (var i=0; i<3; i++) {
                if (t3[i] < 0) { t3[i] += 1; }
                if (t3[i] > 1) { t3[i] -= 1; }
                if (6 * t3[i] < 1)
                    { c[i] = t1 + (t2 - t1) * 6 * t3[i]; }
                else if (2 * t3[i] < 1)
                    { c[i] = t2; }
                else if (3 * t3[i] < 2)
                    { c[i] = t1 + (t2 - t1) * ((2 / 3) - t3[i]) * 6; }
                else
                    { c[i] = t1; }
            }
            (assign = [round$5(c[0]*255),round$5(c[1]*255),round$5(c[2]*255)], r = assign[0], g = assign[1], b = assign[2]);
        }
        if (args.length > 3) {
            // keep alpha channel
            return [r,g,b,args[3]];
        }
        return [r,g,b,1];
    };

    var hsl2rgb_1 = hsl2rgb$1;

    var hsl2rgb = hsl2rgb_1;
    var input$f = input$h;

    var RE_RGB = /^rgb\(\s*(-?\d+),\s*(-?\d+)\s*,\s*(-?\d+)\s*\)$/;
    var RE_RGBA = /^rgba\(\s*(-?\d+),\s*(-?\d+)\s*,\s*(-?\d+)\s*,\s*([01]|[01]?\.\d+)\)$/;
    var RE_RGB_PCT = /^rgb\(\s*(-?\d+(?:\.\d+)?)%,\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*\)$/;
    var RE_RGBA_PCT = /^rgba\(\s*(-?\d+(?:\.\d+)?)%,\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*,\s*([01]|[01]?\.\d+)\)$/;
    var RE_HSL = /^hsl\(\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*\)$/;
    var RE_HSLA = /^hsla\(\s*(-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)%\s*,\s*(-?\d+(?:\.\d+)?)%\s*,\s*([01]|[01]?\.\d+)\)$/;

    var round$4 = Math.round;

    var css2rgb$1 = function (css) {
        css = css.toLowerCase().trim();
        var m;

        if (input$f.format.named) {
            try {
                return input$f.format.named(css);
            } catch (e) {
                // eslint-disable-next-line
            }
        }

        // rgb(250,20,0)
        if ((m = css.match(RE_RGB))) {
            var rgb = m.slice(1,4);
            for (var i=0; i<3; i++) {
                rgb[i] = +rgb[i];
            }
            rgb[3] = 1;  // default alpha
            return rgb;
        }

        // rgba(250,20,0,0.4)
        if ((m = css.match(RE_RGBA))) {
            var rgb$1 = m.slice(1,5);
            for (var i$1=0; i$1<4; i$1++) {
                rgb$1[i$1] = +rgb$1[i$1];
            }
            return rgb$1;
        }

        // rgb(100%,0%,0%)
        if ((m = css.match(RE_RGB_PCT))) {
            var rgb$2 = m.slice(1,4);
            for (var i$2=0; i$2<3; i$2++) {
                rgb$2[i$2] = round$4(rgb$2[i$2] * 2.55);
            }
            rgb$2[3] = 1;  // default alpha
            return rgb$2;
        }

        // rgba(100%,0%,0%,0.4)
        if ((m = css.match(RE_RGBA_PCT))) {
            var rgb$3 = m.slice(1,5);
            for (var i$3=0; i$3<3; i$3++) {
                rgb$3[i$3] = round$4(rgb$3[i$3] * 2.55);
            }
            rgb$3[3] = +rgb$3[3];
            return rgb$3;
        }

        // hsl(0,100%,50%)
        if ((m = css.match(RE_HSL))) {
            var hsl = m.slice(1,4);
            hsl[1] *= 0.01;
            hsl[2] *= 0.01;
            var rgb$4 = hsl2rgb(hsl);
            rgb$4[3] = 1;
            return rgb$4;
        }

        // hsla(0,100%,50%,0.5)
        if ((m = css.match(RE_HSLA))) {
            var hsl$1 = m.slice(1,4);
            hsl$1[1] *= 0.01;
            hsl$1[2] *= 0.01;
            var rgb$5 = hsl2rgb(hsl$1);
            rgb$5[3] = +m[4];  // default alpha = 1
            return rgb$5;
        }
    };

    css2rgb$1.test = function (s) {
        return RE_RGB.test(s) ||
            RE_RGBA.test(s) ||
            RE_RGB_PCT.test(s) ||
            RE_RGBA_PCT.test(s) ||
            RE_HSL.test(s) ||
            RE_HSLA.test(s);
    };

    var css2rgb_1 = css2rgb$1;

    var chroma$i = chroma_1;
    var Color$B = Color_1;
    var input$e = input$h;
    var type$k = utils.type;

    var rgb2css = rgb2css_1;
    var css2rgb = css2rgb_1;

    Color$B.prototype.css = function(mode) {
        return rgb2css(this._rgb, mode);
    };

    chroma$i.css = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$B, [ null ].concat( args, ['css']) ));
    };

    input$e.format.css = css2rgb;

    input$e.autodetect.push({
        p: 5,
        test: function (h) {
            var rest = [], len = arguments.length - 1;
            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];

            if (!rest.length && type$k(h) === 'string' && css2rgb.test(h)) {
                return 'css';
            }
        }
    });

    var Color$A = Color_1;
    var chroma$h = chroma_1;
    var input$d = input$h;
    var unpack$t = utils.unpack;

    input$d.format.gl = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var rgb = unpack$t(args, 'rgba');
        rgb[0] *= 255;
        rgb[1] *= 255;
        rgb[2] *= 255;
        return rgb;
    };

    chroma$h.gl = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$A, [ null ].concat( args, ['gl']) ));
    };

    Color$A.prototype.gl = function() {
        var rgb = this._rgb;
        return [rgb[0]/255, rgb[1]/255, rgb[2]/255, rgb[3]];
    };

    var unpack$s = utils.unpack;

    var rgb2hcg$1 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var ref = unpack$s(args, 'rgb');
        var r = ref[0];
        var g = ref[1];
        var b = ref[2];
        var min = Math.min(r, g, b);
        var max = Math.max(r, g, b);
        var delta = max - min;
        var c = delta * 100 / 255;
        var _g = min / (255 - delta) * 100;
        var h;
        if (delta === 0) {
            h = Number.NaN;
        } else {
            if (r === max) { h = (g - b) / delta; }
            if (g === max) { h = 2+(b - r) / delta; }
            if (b === max) { h = 4+(r - g) / delta; }
            h *= 60;
            if (h < 0) { h += 360; }
        }
        return [h, c, _g];
    };

    var rgb2hcg_1 = rgb2hcg$1;

    var unpack$r = utils.unpack;
    var floor$3 = Math.floor;

    /*
     * this is basically just HSV with some minor tweaks
     *
     * hue.. [0..360]
     * chroma .. [0..1]
     * grayness .. [0..1]
     */

    var hcg2rgb = function () {
        var assign, assign$1, assign$2, assign$3, assign$4, assign$5;

        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];
        args = unpack$r(args, 'hcg');
        var h = args[0];
        var c = args[1];
        var _g = args[2];
        var r,g,b;
        _g = _g * 255;
        var _c = c * 255;
        if (c === 0) {
            r = g = b = _g;
        } else {
            if (h === 360) { h = 0; }
            if (h > 360) { h -= 360; }
            if (h < 0) { h += 360; }
            h /= 60;
            var i = floor$3(h);
            var f = h - i;
            var p = _g * (1 - c);
            var q = p + _c * (1 - f);
            var t = p + _c * f;
            var v = p + _c;
            switch (i) {
                case 0: (assign = [v, t, p], r = assign[0], g = assign[1], b = assign[2]); break
                case 1: (assign$1 = [q, v, p], r = assign$1[0], g = assign$1[1], b = assign$1[2]); break
                case 2: (assign$2 = [p, v, t], r = assign$2[0], g = assign$2[1], b = assign$2[2]); break
                case 3: (assign$3 = [p, q, v], r = assign$3[0], g = assign$3[1], b = assign$3[2]); break
                case 4: (assign$4 = [t, p, v], r = assign$4[0], g = assign$4[1], b = assign$4[2]); break
                case 5: (assign$5 = [v, p, q], r = assign$5[0], g = assign$5[1], b = assign$5[2]); break
            }
        }
        return [r, g, b, args.length > 3 ? args[3] : 1];
    };

    var hcg2rgb_1 = hcg2rgb;

    var unpack$q = utils.unpack;
    var type$j = utils.type;
    var chroma$g = chroma_1;
    var Color$z = Color_1;
    var input$c = input$h;

    var rgb2hcg = rgb2hcg_1;

    Color$z.prototype.hcg = function() {
        return rgb2hcg(this._rgb);
    };

    chroma$g.hcg = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$z, [ null ].concat( args, ['hcg']) ));
    };

    input$c.format.hcg = hcg2rgb_1;

    input$c.autodetect.push({
        p: 1,
        test: function () {
            var args = [], len = arguments.length;
            while ( len-- ) args[ len ] = arguments[ len ];

            args = unpack$q(args, 'hcg');
            if (type$j(args) === 'array' && args.length === 3) {
                return 'hcg';
            }
        }
    });

    var unpack$p = utils.unpack;
    var last = utils.last;
    var round$3 = Math.round;

    var rgb2hex$2 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var ref = unpack$p(args, 'rgba');
        var r = ref[0];
        var g = ref[1];
        var b = ref[2];
        var a = ref[3];
        var mode = last(args) || 'auto';
        if (a === undefined) { a = 1; }
        if (mode === 'auto') {
            mode = a < 1 ? 'rgba' : 'rgb';
        }
        r = round$3(r);
        g = round$3(g);
        b = round$3(b);
        var u = r << 16 | g << 8 | b;
        var str = "000000" + u.toString(16); //#.toUpperCase();
        str = str.substr(str.length - 6);
        var hxa = '0' + round$3(a * 255).toString(16);
        hxa = hxa.substr(hxa.length - 2);
        switch (mode.toLowerCase()) {
            case 'rgba': return ("#" + str + hxa);
            case 'argb': return ("#" + hxa + str);
            default: return ("#" + str);
        }
    };

    var rgb2hex_1 = rgb2hex$2;

    var RE_HEX = /^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
    var RE_HEXA = /^#?([A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/;

    var hex2rgb$1 = function (hex) {
        if (hex.match(RE_HEX)) {
            // remove optional leading #
            if (hex.length === 4 || hex.length === 7) {
                hex = hex.substr(1);
            }
            // expand short-notation to full six-digit
            if (hex.length === 3) {
                hex = hex.split('');
                hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
            }
            var u = parseInt(hex, 16);
            var r = u >> 16;
            var g = u >> 8 & 0xFF;
            var b = u & 0xFF;
            return [r,g,b,1];
        }

        // match rgba hex format, eg #FF000077
        if (hex.match(RE_HEXA)) {
            if (hex.length === 5 || hex.length === 9) {
                // remove optional leading #
                hex = hex.substr(1);
            }
            // expand short-notation to full eight-digit
            if (hex.length === 4) {
                hex = hex.split('');
                hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]+hex[3]+hex[3];
            }
            var u$1 = parseInt(hex, 16);
            var r$1 = u$1 >> 24 & 0xFF;
            var g$1 = u$1 >> 16 & 0xFF;
            var b$1 = u$1 >> 8 & 0xFF;
            var a = Math.round((u$1 & 0xFF) / 0xFF * 100) / 100;
            return [r$1,g$1,b$1,a];
        }

        // we used to check for css colors here
        // if _input.css? and rgb = _input.css hex
        //     return rgb

        throw new Error(("unknown hex color: " + hex));
    };

    var hex2rgb_1 = hex2rgb$1;

    var chroma$f = chroma_1;
    var Color$y = Color_1;
    var type$i = utils.type;
    var input$b = input$h;

    var rgb2hex$1 = rgb2hex_1;

    Color$y.prototype.hex = function(mode) {
        return rgb2hex$1(this._rgb, mode);
    };

    chroma$f.hex = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$y, [ null ].concat( args, ['hex']) ));
    };

    input$b.format.hex = hex2rgb_1;
    input$b.autodetect.push({
        p: 4,
        test: function (h) {
            var rest = [], len = arguments.length - 1;
            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];

            if (!rest.length && type$i(h) === 'string' && [3,4,5,6,7,8,9].indexOf(h.length) >= 0) {
                return 'hex';
            }
        }
    });

    var unpack$o = utils.unpack;
    var TWOPI$2 = utils.TWOPI;
    var min$2 = Math.min;
    var sqrt$4 = Math.sqrt;
    var acos = Math.acos;

    var rgb2hsi$1 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        /*
        borrowed from here:
        http://hummer.stanford.edu/museinfo/doc/examples/humdrum/keyscape2/rgb2hsi.cpp
        */
        var ref = unpack$o(args, 'rgb');
        var r = ref[0];
        var g = ref[1];
        var b = ref[2];
        r /= 255;
        g /= 255;
        b /= 255;
        var h;
        var min_ = min$2(r,g,b);
        var i = (r+g+b) / 3;
        var s = i > 0 ? 1 - min_/i : 0;
        if (s === 0) {
            h = NaN;
        } else {
            h = ((r-g)+(r-b)) / 2;
            h /= sqrt$4((r-g)*(r-g) + (r-b)*(g-b));
            h = acos(h);
            if (b > g) {
                h = TWOPI$2 - h;
            }
            h /= TWOPI$2;
        }
        return [h*360,s,i];
    };

    var rgb2hsi_1 = rgb2hsi$1;

    var unpack$n = utils.unpack;
    var limit = utils.limit;
    var TWOPI$1 = utils.TWOPI;
    var PITHIRD = utils.PITHIRD;
    var cos$4 = Math.cos;

    /*
     * hue [0..360]
     * saturation [0..1]
     * intensity [0..1]
     */
    var hsi2rgb = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        /*
        borrowed from here:
        http://hummer.stanford.edu/museinfo/doc/examples/humdrum/keyscape2/hsi2rgb.cpp
        */
        args = unpack$n(args, 'hsi');
        var h = args[0];
        var s = args[1];
        var i = args[2];
        var r,g,b;

        if (isNaN(h)) { h = 0; }
        if (isNaN(s)) { s = 0; }
        // normalize hue
        if (h > 360) { h -= 360; }
        if (h < 0) { h += 360; }
        h /= 360;
        if (h < 1/3) {
            b = (1-s)/3;
            r = (1+s*cos$4(TWOPI$1*h)/cos$4(PITHIRD-TWOPI$1*h))/3;
            g = 1 - (b+r);
        } else if (h < 2/3) {
            h -= 1/3;
            r = (1-s)/3;
            g = (1+s*cos$4(TWOPI$1*h)/cos$4(PITHIRD-TWOPI$1*h))/3;
            b = 1 - (r+g);
        } else {
            h -= 2/3;
            g = (1-s)/3;
            b = (1+s*cos$4(TWOPI$1*h)/cos$4(PITHIRD-TWOPI$1*h))/3;
            r = 1 - (g+b);
        }
        r = limit(i*r*3);
        g = limit(i*g*3);
        b = limit(i*b*3);
        return [r*255, g*255, b*255, args.length > 3 ? args[3] : 1];
    };

    var hsi2rgb_1 = hsi2rgb;

    var unpack$m = utils.unpack;
    var type$h = utils.type;
    var chroma$e = chroma_1;
    var Color$x = Color_1;
    var input$a = input$h;

    var rgb2hsi = rgb2hsi_1;

    Color$x.prototype.hsi = function() {
        return rgb2hsi(this._rgb);
    };

    chroma$e.hsi = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$x, [ null ].concat( args, ['hsi']) ));
    };

    input$a.format.hsi = hsi2rgb_1;

    input$a.autodetect.push({
        p: 2,
        test: function () {
            var args = [], len = arguments.length;
            while ( len-- ) args[ len ] = arguments[ len ];

            args = unpack$m(args, 'hsi');
            if (type$h(args) === 'array' && args.length === 3) {
                return 'hsi';
            }
        }
    });

    var unpack$l = utils.unpack;
    var type$g = utils.type;
    var chroma$d = chroma_1;
    var Color$w = Color_1;
    var input$9 = input$h;

    var rgb2hsl$1 = rgb2hsl_1;

    Color$w.prototype.hsl = function() {
        return rgb2hsl$1(this._rgb);
    };

    chroma$d.hsl = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$w, [ null ].concat( args, ['hsl']) ));
    };

    input$9.format.hsl = hsl2rgb_1;

    input$9.autodetect.push({
        p: 2,
        test: function () {
            var args = [], len = arguments.length;
            while ( len-- ) args[ len ] = arguments[ len ];

            args = unpack$l(args, 'hsl');
            if (type$g(args) === 'array' && args.length === 3) {
                return 'hsl';
            }
        }
    });

    var unpack$k = utils.unpack;
    var min$1 = Math.min;
    var max$1 = Math.max;

    /*
     * supported arguments:
     * - rgb2hsv(r,g,b)
     * - rgb2hsv([r,g,b])
     * - rgb2hsv({r,g,b})
     */
    var rgb2hsl = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        args = unpack$k(args, 'rgb');
        var r = args[0];
        var g = args[1];
        var b = args[2];
        var min_ = min$1(r, g, b);
        var max_ = max$1(r, g, b);
        var delta = max_ - min_;
        var h,s,v;
        v = max_ / 255.0;
        if (max_ === 0) {
            h = Number.NaN;
            s = 0;
        } else {
            s = delta / max_;
            if (r === max_) { h = (g - b) / delta; }
            if (g === max_) { h = 2+(b - r) / delta; }
            if (b === max_) { h = 4+(r - g) / delta; }
            h *= 60;
            if (h < 0) { h += 360; }
        }
        return [h, s, v]
    };

    var rgb2hsv$1 = rgb2hsl;

    var unpack$j = utils.unpack;
    var floor$2 = Math.floor;

    var hsv2rgb = function () {
        var assign, assign$1, assign$2, assign$3, assign$4, assign$5;

        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];
        args = unpack$j(args, 'hsv');
        var h = args[0];
        var s = args[1];
        var v = args[2];
        var r,g,b;
        v *= 255;
        if (s === 0) {
            r = g = b = v;
        } else {
            if (h === 360) { h = 0; }
            if (h > 360) { h -= 360; }
            if (h < 0) { h += 360; }
            h /= 60;

            var i = floor$2(h);
            var f = h - i;
            var p = v * (1 - s);
            var q = v * (1 - s * f);
            var t = v * (1 - s * (1 - f));

            switch (i) {
                case 0: (assign = [v, t, p], r = assign[0], g = assign[1], b = assign[2]); break
                case 1: (assign$1 = [q, v, p], r = assign$1[0], g = assign$1[1], b = assign$1[2]); break
                case 2: (assign$2 = [p, v, t], r = assign$2[0], g = assign$2[1], b = assign$2[2]); break
                case 3: (assign$3 = [p, q, v], r = assign$3[0], g = assign$3[1], b = assign$3[2]); break
                case 4: (assign$4 = [t, p, v], r = assign$4[0], g = assign$4[1], b = assign$4[2]); break
                case 5: (assign$5 = [v, p, q], r = assign$5[0], g = assign$5[1], b = assign$5[2]); break
            }
        }
        return [r,g,b,args.length > 3?args[3]:1];
    };

    var hsv2rgb_1 = hsv2rgb;

    var unpack$i = utils.unpack;
    var type$f = utils.type;
    var chroma$c = chroma_1;
    var Color$v = Color_1;
    var input$8 = input$h;

    var rgb2hsv = rgb2hsv$1;

    Color$v.prototype.hsv = function() {
        return rgb2hsv(this._rgb);
    };

    chroma$c.hsv = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$v, [ null ].concat( args, ['hsv']) ));
    };

    input$8.format.hsv = hsv2rgb_1;

    input$8.autodetect.push({
        p: 2,
        test: function () {
            var args = [], len = arguments.length;
            while ( len-- ) args[ len ] = arguments[ len ];

            args = unpack$i(args, 'hsv');
            if (type$f(args) === 'array' && args.length === 3) {
                return 'hsv';
            }
        }
    });

    var labConstants = {
        // Corresponds roughly to RGB brighter/darker
        Kn: 18,

        // D65 standard referent
        Xn: 0.950470,
        Yn: 1,
        Zn: 1.088830,

        t0: 0.137931034,  // 4 / 29
        t1: 0.206896552,  // 6 / 29
        t2: 0.12841855,   // 3 * t1 * t1
        t3: 0.008856452,  // t1 * t1 * t1
    };

    var LAB_CONSTANTS$3 = labConstants;
    var unpack$h = utils.unpack;
    var pow$a = Math.pow;

    var rgb2lab$2 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var ref = unpack$h(args, 'rgb');
        var r = ref[0];
        var g = ref[1];
        var b = ref[2];
        var ref$1 = rgb2xyz(r,g,b);
        var x = ref$1[0];
        var y = ref$1[1];
        var z = ref$1[2];
        var l = 116 * y - 16;
        return [l < 0 ? 0 : l, 500 * (x - y), 200 * (y - z)];
    };

    var rgb_xyz = function (r) {
        if ((r /= 255) <= 0.04045) { return r / 12.92; }
        return pow$a((r + 0.055) / 1.055, 2.4);
    };

    var xyz_lab = function (t) {
        if (t > LAB_CONSTANTS$3.t3) { return pow$a(t, 1 / 3); }
        return t / LAB_CONSTANTS$3.t2 + LAB_CONSTANTS$3.t0;
    };

    var rgb2xyz = function (r,g,b) {
        r = rgb_xyz(r);
        g = rgb_xyz(g);
        b = rgb_xyz(b);
        var x = xyz_lab((0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / LAB_CONSTANTS$3.Xn);
        var y = xyz_lab((0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / LAB_CONSTANTS$3.Yn);
        var z = xyz_lab((0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / LAB_CONSTANTS$3.Zn);
        return [x,y,z];
    };

    var rgb2lab_1 = rgb2lab$2;

    var LAB_CONSTANTS$2 = labConstants;
    var unpack$g = utils.unpack;
    var pow$9 = Math.pow;

    /*
     * L* [0..100]
     * a [-100..100]
     * b [-100..100]
     */
    var lab2rgb$1 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        args = unpack$g(args, 'lab');
        var l = args[0];
        var a = args[1];
        var b = args[2];
        var x,y,z, r,g,b_;

        y = (l + 16) / 116;
        x = isNaN(a) ? y : y + a / 500;
        z = isNaN(b) ? y : y - b / 200;

        y = LAB_CONSTANTS$2.Yn * lab_xyz(y);
        x = LAB_CONSTANTS$2.Xn * lab_xyz(x);
        z = LAB_CONSTANTS$2.Zn * lab_xyz(z);

        r = xyz_rgb(3.2404542 * x - 1.5371385 * y - 0.4985314 * z);  // D65 -> sRGB
        g = xyz_rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z);
        b_ = xyz_rgb(0.0556434 * x - 0.2040259 * y + 1.0572252 * z);

        return [r,g,b_,args.length > 3 ? args[3] : 1];
    };

    var xyz_rgb = function (r) {
        return 255 * (r <= 0.00304 ? 12.92 * r : 1.055 * pow$9(r, 1 / 2.4) - 0.055)
    };

    var lab_xyz = function (t) {
        return t > LAB_CONSTANTS$2.t1 ? t * t * t : LAB_CONSTANTS$2.t2 * (t - LAB_CONSTANTS$2.t0)
    };

    var lab2rgb_1 = lab2rgb$1;

    var unpack$f = utils.unpack;
    var type$e = utils.type;
    var chroma$b = chroma_1;
    var Color$u = Color_1;
    var input$7 = input$h;

    var rgb2lab$1 = rgb2lab_1;

    Color$u.prototype.lab = function() {
        return rgb2lab$1(this._rgb);
    };

    chroma$b.lab = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$u, [ null ].concat( args, ['lab']) ));
    };

    input$7.format.lab = lab2rgb_1;

    input$7.autodetect.push({
        p: 2,
        test: function () {
            var args = [], len = arguments.length;
            while ( len-- ) args[ len ] = arguments[ len ];

            args = unpack$f(args, 'lab');
            if (type$e(args) === 'array' && args.length === 3) {
                return 'lab';
            }
        }
    });

    var unpack$e = utils.unpack;
    var RAD2DEG = utils.RAD2DEG;
    var sqrt$3 = Math.sqrt;
    var atan2$2 = Math.atan2;
    var round$2 = Math.round;

    var lab2lch$2 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var ref = unpack$e(args, 'lab');
        var l = ref[0];
        var a = ref[1];
        var b = ref[2];
        var c = sqrt$3(a * a + b * b);
        var h = (atan2$2(b, a) * RAD2DEG + 360) % 360;
        if (round$2(c*10000) === 0) { h = Number.NaN; }
        return [l, c, h];
    };

    var lab2lch_1 = lab2lch$2;

    var unpack$d = utils.unpack;
    var rgb2lab = rgb2lab_1;
    var lab2lch$1 = lab2lch_1;

    var rgb2lch$1 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var ref = unpack$d(args, 'rgb');
        var r = ref[0];
        var g = ref[1];
        var b = ref[2];
        var ref$1 = rgb2lab(r,g,b);
        var l = ref$1[0];
        var a = ref$1[1];
        var b_ = ref$1[2];
        return lab2lch$1(l,a,b_);
    };

    var rgb2lch_1 = rgb2lch$1;

    var unpack$c = utils.unpack;
    var DEG2RAD = utils.DEG2RAD;
    var sin$3 = Math.sin;
    var cos$3 = Math.cos;

    var lch2lab$2 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        /*
        Convert from a qualitative parameter h and a quantitative parameter l to a 24-bit pixel.
        These formulas were invented by David Dalrymple to obtain maximum contrast without going
        out of gamut if the parameters are in the range 0-1.

        A saturation multiplier was added by Gregor Aisch
        */
        var ref = unpack$c(args, 'lch');
        var l = ref[0];
        var c = ref[1];
        var h = ref[2];
        if (isNaN(h)) { h = 0; }
        h = h * DEG2RAD;
        return [l, cos$3(h) * c, sin$3(h) * c]
    };

    var lch2lab_1 = lch2lab$2;

    var unpack$b = utils.unpack;
    var lch2lab$1 = lch2lab_1;
    var lab2rgb = lab2rgb_1;

    var lch2rgb$1 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        args = unpack$b(args, 'lch');
        var l = args[0];
        var c = args[1];
        var h = args[2];
        var ref = lch2lab$1 (l,c,h);
        var L = ref[0];
        var a = ref[1];
        var b_ = ref[2];
        var ref$1 = lab2rgb (L,a,b_);
        var r = ref$1[0];
        var g = ref$1[1];
        var b = ref$1[2];
        return [r, g, b, args.length > 3 ? args[3] : 1];
    };

    var lch2rgb_1 = lch2rgb$1;

    var unpack$a = utils.unpack;
    var lch2rgb = lch2rgb_1;

    var hcl2rgb = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var hcl = unpack$a(args, 'hcl').reverse();
        return lch2rgb.apply(void 0, hcl);
    };

    var hcl2rgb_1 = hcl2rgb;

    var unpack$9 = utils.unpack;
    var type$d = utils.type;
    var chroma$a = chroma_1;
    var Color$t = Color_1;
    var input$6 = input$h;

    var rgb2lch = rgb2lch_1;

    Color$t.prototype.lch = function() { return rgb2lch(this._rgb); };
    Color$t.prototype.hcl = function() { return rgb2lch(this._rgb).reverse(); };

    chroma$a.lch = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$t, [ null ].concat( args, ['lch']) ));
    };
    chroma$a.hcl = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$t, [ null ].concat( args, ['hcl']) ));
    };

    input$6.format.lch = lch2rgb_1;
    input$6.format.hcl = hcl2rgb_1;

    ['lch','hcl'].forEach(function (m) { return input$6.autodetect.push({
        p: 2,
        test: function () {
            var args = [], len = arguments.length;
            while ( len-- ) args[ len ] = arguments[ len ];

            args = unpack$9(args, m);
            if (type$d(args) === 'array' && args.length === 3) {
                return m;
            }
        }
    }); });

    /**
    	X11 color names

    	http://www.w3.org/TR/css3-color/#svg-color
    */

    var w3cx11$1 = {
        aliceblue: '#f0f8ff',
        antiquewhite: '#faebd7',
        aqua: '#00ffff',
        aquamarine: '#7fffd4',
        azure: '#f0ffff',
        beige: '#f5f5dc',
        bisque: '#ffe4c4',
        black: '#000000',
        blanchedalmond: '#ffebcd',
        blue: '#0000ff',
        blueviolet: '#8a2be2',
        brown: '#a52a2a',
        burlywood: '#deb887',
        cadetblue: '#5f9ea0',
        chartreuse: '#7fff00',
        chocolate: '#d2691e',
        coral: '#ff7f50',
        cornflower: '#6495ed',
        cornflowerblue: '#6495ed',
        cornsilk: '#fff8dc',
        crimson: '#dc143c',
        cyan: '#00ffff',
        darkblue: '#00008b',
        darkcyan: '#008b8b',
        darkgoldenrod: '#b8860b',
        darkgray: '#a9a9a9',
        darkgreen: '#006400',
        darkgrey: '#a9a9a9',
        darkkhaki: '#bdb76b',
        darkmagenta: '#8b008b',
        darkolivegreen: '#556b2f',
        darkorange: '#ff8c00',
        darkorchid: '#9932cc',
        darkred: '#8b0000',
        darksalmon: '#e9967a',
        darkseagreen: '#8fbc8f',
        darkslateblue: '#483d8b',
        darkslategray: '#2f4f4f',
        darkslategrey: '#2f4f4f',
        darkturquoise: '#00ced1',
        darkviolet: '#9400d3',
        deeppink: '#ff1493',
        deepskyblue: '#00bfff',
        dimgray: '#696969',
        dimgrey: '#696969',
        dodgerblue: '#1e90ff',
        firebrick: '#b22222',
        floralwhite: '#fffaf0',
        forestgreen: '#228b22',
        fuchsia: '#ff00ff',
        gainsboro: '#dcdcdc',
        ghostwhite: '#f8f8ff',
        gold: '#ffd700',
        goldenrod: '#daa520',
        gray: '#808080',
        green: '#008000',
        greenyellow: '#adff2f',
        grey: '#808080',
        honeydew: '#f0fff0',
        hotpink: '#ff69b4',
        indianred: '#cd5c5c',
        indigo: '#4b0082',
        ivory: '#fffff0',
        khaki: '#f0e68c',
        laserlemon: '#ffff54',
        lavender: '#e6e6fa',
        lavenderblush: '#fff0f5',
        lawngreen: '#7cfc00',
        lemonchiffon: '#fffacd',
        lightblue: '#add8e6',
        lightcoral: '#f08080',
        lightcyan: '#e0ffff',
        lightgoldenrod: '#fafad2',
        lightgoldenrodyellow: '#fafad2',
        lightgray: '#d3d3d3',
        lightgreen: '#90ee90',
        lightgrey: '#d3d3d3',
        lightpink: '#ffb6c1',
        lightsalmon: '#ffa07a',
        lightseagreen: '#20b2aa',
        lightskyblue: '#87cefa',
        lightslategray: '#778899',
        lightslategrey: '#778899',
        lightsteelblue: '#b0c4de',
        lightyellow: '#ffffe0',
        lime: '#00ff00',
        limegreen: '#32cd32',
        linen: '#faf0e6',
        magenta: '#ff00ff',
        maroon: '#800000',
        maroon2: '#7f0000',
        maroon3: '#b03060',
        mediumaquamarine: '#66cdaa',
        mediumblue: '#0000cd',
        mediumorchid: '#ba55d3',
        mediumpurple: '#9370db',
        mediumseagreen: '#3cb371',
        mediumslateblue: '#7b68ee',
        mediumspringgreen: '#00fa9a',
        mediumturquoise: '#48d1cc',
        mediumvioletred: '#c71585',
        midnightblue: '#191970',
        mintcream: '#f5fffa',
        mistyrose: '#ffe4e1',
        moccasin: '#ffe4b5',
        navajowhite: '#ffdead',
        navy: '#000080',
        oldlace: '#fdf5e6',
        olive: '#808000',
        olivedrab: '#6b8e23',
        orange: '#ffa500',
        orangered: '#ff4500',
        orchid: '#da70d6',
        palegoldenrod: '#eee8aa',
        palegreen: '#98fb98',
        paleturquoise: '#afeeee',
        palevioletred: '#db7093',
        papayawhip: '#ffefd5',
        peachpuff: '#ffdab9',
        peru: '#cd853f',
        pink: '#ffc0cb',
        plum: '#dda0dd',
        powderblue: '#b0e0e6',
        purple: '#800080',
        purple2: '#7f007f',
        purple3: '#a020f0',
        rebeccapurple: '#663399',
        red: '#ff0000',
        rosybrown: '#bc8f8f',
        royalblue: '#4169e1',
        saddlebrown: '#8b4513',
        salmon: '#fa8072',
        sandybrown: '#f4a460',
        seagreen: '#2e8b57',
        seashell: '#fff5ee',
        sienna: '#a0522d',
        silver: '#c0c0c0',
        skyblue: '#87ceeb',
        slateblue: '#6a5acd',
        slategray: '#708090',
        slategrey: '#708090',
        snow: '#fffafa',
        springgreen: '#00ff7f',
        steelblue: '#4682b4',
        tan: '#d2b48c',
        teal: '#008080',
        thistle: '#d8bfd8',
        tomato: '#ff6347',
        turquoise: '#40e0d0',
        violet: '#ee82ee',
        wheat: '#f5deb3',
        white: '#ffffff',
        whitesmoke: '#f5f5f5',
        yellow: '#ffff00',
        yellowgreen: '#9acd32'
    };

    var w3cx11_1 = w3cx11$1;

    var Color$s = Color_1;
    var input$5 = input$h;
    var type$c = utils.type;

    var w3cx11 = w3cx11_1;
    var hex2rgb = hex2rgb_1;
    var rgb2hex = rgb2hex_1;

    Color$s.prototype.name = function() {
        var hex = rgb2hex(this._rgb, 'rgb');
        for (var i = 0, list = Object.keys(w3cx11); i < list.length; i += 1) {
            var n = list[i];

            if (w3cx11[n] === hex) { return n.toLowerCase(); }
        }
        return hex;
    };

    input$5.format.named = function (name) {
        name = name.toLowerCase();
        if (w3cx11[name]) { return hex2rgb(w3cx11[name]); }
        throw new Error('unknown color name: '+name);
    };

    input$5.autodetect.push({
        p: 5,
        test: function (h) {
            var rest = [], len = arguments.length - 1;
            while ( len-- > 0 ) rest[ len ] = arguments[ len + 1 ];

            if (!rest.length && type$c(h) === 'string' && w3cx11[h.toLowerCase()]) {
                return 'named';
            }
        }
    });

    var unpack$8 = utils.unpack;

    var rgb2num$1 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var ref = unpack$8(args, 'rgb');
        var r = ref[0];
        var g = ref[1];
        var b = ref[2];
        return (r << 16) + (g << 8) + b;
    };

    var rgb2num_1 = rgb2num$1;

    var type$b = utils.type;

    var num2rgb = function (num) {
        if (type$b(num) == "number" && num >= 0 && num <= 0xFFFFFF) {
            var r = num >> 16;
            var g = (num >> 8) & 0xFF;
            var b = num & 0xFF;
            return [r,g,b,1];
        }
        throw new Error("unknown num color: "+num);
    };

    var num2rgb_1 = num2rgb;

    var chroma$9 = chroma_1;
    var Color$r = Color_1;
    var input$4 = input$h;
    var type$a = utils.type;

    var rgb2num = rgb2num_1;

    Color$r.prototype.num = function() {
        return rgb2num(this._rgb);
    };

    chroma$9.num = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$r, [ null ].concat( args, ['num']) ));
    };

    input$4.format.num = num2rgb_1;

    input$4.autodetect.push({
        p: 5,
        test: function () {
            var args = [], len = arguments.length;
            while ( len-- ) args[ len ] = arguments[ len ];

            if (args.length === 1 && type$a(args[0]) === 'number' && args[0] >= 0 && args[0] <= 0xFFFFFF) {
                return 'num';
            }
        }
    });

    var chroma$8 = chroma_1;
    var Color$q = Color_1;
    var input$3 = input$h;
    var unpack$7 = utils.unpack;
    var type$9 = utils.type;
    var round$1 = Math.round;

    Color$q.prototype.rgb = function(rnd) {
        if ( rnd === void 0 ) rnd=true;

        if (rnd === false) { return this._rgb.slice(0,3); }
        return this._rgb.slice(0,3).map(round$1);
    };

    Color$q.prototype.rgba = function(rnd) {
        if ( rnd === void 0 ) rnd=true;

        return this._rgb.slice(0,4).map(function (v,i) {
            return i<3 ? (rnd === false ? v : round$1(v)) : v;
        });
    };

    chroma$8.rgb = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$q, [ null ].concat( args, ['rgb']) ));
    };

    input$3.format.rgb = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var rgba = unpack$7(args, 'rgba');
        if (rgba[3] === undefined) { rgba[3] = 1; }
        return rgba;
    };

    input$3.autodetect.push({
        p: 3,
        test: function () {
            var args = [], len = arguments.length;
            while ( len-- ) args[ len ] = arguments[ len ];

            args = unpack$7(args, 'rgba');
            if (type$9(args) === 'array' && (args.length === 3 ||
                args.length === 4 && type$9(args[3]) == 'number' && args[3] >= 0 && args[3] <= 1)) {
                return 'rgb';
            }
        }
    });

    /*
     * Based on implementation by Neil Bartlett
     * https://github.com/neilbartlett/color-temperature
     */

    var log$1 = Math.log;

    var temperature2rgb$1 = function (kelvin) {
        var temp = kelvin / 100;
        var r,g,b;
        if (temp < 66) {
            r = 255;
            g = temp < 6 ? 0 : -155.25485562709179 - 0.44596950469579133 * (g = temp-2) + 104.49216199393888 * log$1(g);
            b = temp < 20 ? 0 : -254.76935184120902 + 0.8274096064007395 * (b = temp-10) + 115.67994401066147 * log$1(b);
        } else {
            r = 351.97690566805693 + 0.114206453784165 * (r = temp-55) - 40.25366309332127 * log$1(r);
            g = 325.4494125711974 + 0.07943456536662342 * (g = temp-50) - 28.0852963507957 * log$1(g);
            b = 255;
        }
        return [r,g,b,1];
    };

    var temperature2rgb_1 = temperature2rgb$1;

    /*
     * Based on implementation by Neil Bartlett
     * https://github.com/neilbartlett/color-temperature
     **/

    var temperature2rgb = temperature2rgb_1;
    var unpack$6 = utils.unpack;
    var round = Math.round;

    var rgb2temperature$1 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var rgb = unpack$6(args, 'rgb');
        var r = rgb[0], b = rgb[2];
        var minTemp = 1000;
        var maxTemp = 40000;
        var eps = 0.4;
        var temp;
        while (maxTemp - minTemp > eps) {
            temp = (maxTemp + minTemp) * 0.5;
            var rgb$1 = temperature2rgb(temp);
            if ((rgb$1[2] / rgb$1[0]) >= (b / r)) {
                maxTemp = temp;
            } else {
                minTemp = temp;
            }
        }
        return round(temp);
    };

    var rgb2temperature_1 = rgb2temperature$1;

    var chroma$7 = chroma_1;
    var Color$p = Color_1;
    var input$2 = input$h;

    var rgb2temperature = rgb2temperature_1;

    Color$p.prototype.temp =
    Color$p.prototype.kelvin =
    Color$p.prototype.temperature = function() {
        return rgb2temperature(this._rgb);
    };

    chroma$7.temp =
    chroma$7.kelvin =
    chroma$7.temperature = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$p, [ null ].concat( args, ['temp']) ));
    };

    input$2.format.temp =
    input$2.format.kelvin =
    input$2.format.temperature = temperature2rgb_1;

    var unpack$5 = utils.unpack;
    var cbrt = Math.cbrt;
    var pow$8 = Math.pow;
    var sign$1 = Math.sign;

    var rgb2oklab$2 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        // OKLab color space implementation taken from
        // https://bottosson.github.io/posts/oklab/
        var ref = unpack$5(args, 'rgb');
        var r = ref[0];
        var g = ref[1];
        var b = ref[2];
        var ref$1 = [rgb2lrgb(r / 255), rgb2lrgb(g / 255), rgb2lrgb(b / 255)];
        var lr = ref$1[0];
        var lg = ref$1[1];
        var lb = ref$1[2];
        var l = cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);
        var m = cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);
        var s = cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);

        return [
            0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
            1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
            0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
        ];
    };

    var rgb2oklab_1 = rgb2oklab$2;

    function rgb2lrgb(c) {
        var abs = Math.abs(c);
        if (abs < 0.04045) {
            return c / 12.92;
        }
        return (sign$1(c) || 1) * pow$8((abs + 0.055) / 1.055, 2.4);
    }

    var unpack$4 = utils.unpack;
    var pow$7 = Math.pow;
    var sign = Math.sign;

    /*
     * L* [0..100]
     * a [-100..100]
     * b [-100..100]
     */
    var oklab2rgb$1 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        args = unpack$4(args, 'lab');
        var L = args[0];
        var a = args[1];
        var b = args[2];

        var l = pow$7(L + 0.3963377774 * a + 0.2158037573 * b, 3);
        var m = pow$7(L - 0.1055613458 * a - 0.0638541728 * b, 3);
        var s = pow$7(L - 0.0894841775 * a - 1.291485548 * b, 3);

        return [
            255 * lrgb2rgb(+4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),
            255 * lrgb2rgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),
            255 * lrgb2rgb(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s),
            args.length > 3 ? args[3] : 1
        ];
    };

    var oklab2rgb_1 = oklab2rgb$1;

    function lrgb2rgb(c) {
        var abs = Math.abs(c);
        if (abs > 0.0031308) {
            return (sign(c) || 1) * (1.055 * pow$7(abs, 1 / 2.4) - 0.055);
        }
        return c * 12.92;
    }

    var unpack$3 = utils.unpack;
    var type$8 = utils.type;
    var chroma$6 = chroma_1;
    var Color$o = Color_1;
    var input$1 = input$h;

    var rgb2oklab$1 = rgb2oklab_1;

    Color$o.prototype.oklab = function () {
        return rgb2oklab$1(this._rgb);
    };

    chroma$6.oklab = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$o, [ null ].concat( args, ['oklab']) ));
    };

    input$1.format.oklab = oklab2rgb_1;

    input$1.autodetect.push({
        p: 3,
        test: function () {
            var args = [], len = arguments.length;
            while ( len-- ) args[ len ] = arguments[ len ];

            args = unpack$3(args, 'oklab');
            if (type$8(args) === 'array' && args.length === 3) {
                return 'oklab';
            }
        }
    });

    var unpack$2 = utils.unpack;
    var rgb2oklab = rgb2oklab_1;
    var lab2lch = lab2lch_1;

    var rgb2oklch$1 = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        var ref = unpack$2(args, 'rgb');
        var r = ref[0];
        var g = ref[1];
        var b = ref[2];
        var ref$1 = rgb2oklab(r, g, b);
        var l = ref$1[0];
        var a = ref$1[1];
        var b_ = ref$1[2];
        return lab2lch(l, a, b_);
    };

    var rgb2oklch_1 = rgb2oklch$1;

    var unpack$1 = utils.unpack;
    var lch2lab = lch2lab_1;
    var oklab2rgb = oklab2rgb_1;

    var oklch2rgb = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        args = unpack$1(args, 'lch');
        var l = args[0];
        var c = args[1];
        var h = args[2];
        var ref = lch2lab(l, c, h);
        var L = ref[0];
        var a = ref[1];
        var b_ = ref[2];
        var ref$1 = oklab2rgb(L, a, b_);
        var r = ref$1[0];
        var g = ref$1[1];
        var b = ref$1[2];
        return [r, g, b, args.length > 3 ? args[3] : 1];
    };

    var oklch2rgb_1 = oklch2rgb;

    var unpack = utils.unpack;
    var type$7 = utils.type;
    var chroma$5 = chroma_1;
    var Color$n = Color_1;
    var input = input$h;

    var rgb2oklch = rgb2oklch_1;

    Color$n.prototype.oklch = function () {
        return rgb2oklch(this._rgb);
    };

    chroma$5.oklch = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        return new (Function.prototype.bind.apply( Color$n, [ null ].concat( args, ['oklch']) ));
    };

    input.format.oklch = oklch2rgb_1;

    input.autodetect.push({
        p: 3,
        test: function () {
            var args = [], len = arguments.length;
            while ( len-- ) args[ len ] = arguments[ len ];

            args = unpack(args, 'oklch');
            if (type$7(args) === 'array' && args.length === 3) {
                return 'oklch';
            }
        }
    });

    var Color$m = Color_1;
    var type$6 = utils.type;

    Color$m.prototype.alpha = function(a, mutate) {
        if ( mutate === void 0 ) mutate=false;

        if (a !== undefined && type$6(a) === 'number') {
            if (mutate) {
                this._rgb[3] = a;
                return this;
            }
            return new Color$m([this._rgb[0], this._rgb[1], this._rgb[2], a], 'rgb');
        }
        return this._rgb[3];
    };

    var Color$l = Color_1;

    Color$l.prototype.clipped = function() {
        return this._rgb._clipped || false;
    };

    var Color$k = Color_1;
    var LAB_CONSTANTS$1 = labConstants;

    Color$k.prototype.darken = function(amount) {
    	if ( amount === void 0 ) amount=1;

    	var me = this;
    	var lab = me.lab();
    	lab[0] -= LAB_CONSTANTS$1.Kn * amount;
    	return new Color$k(lab, 'lab').alpha(me.alpha(), true);
    };

    Color$k.prototype.brighten = function(amount) {
    	if ( amount === void 0 ) amount=1;

    	return this.darken(-amount);
    };

    Color$k.prototype.darker = Color$k.prototype.darken;
    Color$k.prototype.brighter = Color$k.prototype.brighten;

    var Color$j = Color_1;

    Color$j.prototype.get = function (mc) {
        var ref = mc.split('.');
        var mode = ref[0];
        var channel = ref[1];
        var src = this[mode]();
        if (channel) {
            var i = mode.indexOf(channel) - (mode.substr(0, 2) === 'ok' ? 2 : 0);
            if (i > -1) { return src[i]; }
            throw new Error(("unknown channel " + channel + " in mode " + mode));
        } else {
            return src;
        }
    };

    var Color$i = Color_1;
    var type$5 = utils.type;
    var pow$6 = Math.pow;

    var EPS = 1e-7;
    var MAX_ITER = 20;

    Color$i.prototype.luminance = function(lum) {
        if (lum !== undefined && type$5(lum) === 'number') {
            if (lum === 0) {
                // return pure black
                return new Color$i([0,0,0,this._rgb[3]], 'rgb');
            }
            if (lum === 1) {
                // return pure white
                return new Color$i([255,255,255,this._rgb[3]], 'rgb');
            }
            // compute new color using...
            var cur_lum = this.luminance();
            var mode = 'rgb';
            var max_iter = MAX_ITER;

            var test = function (low, high) {
                var mid = low.interpolate(high, 0.5, mode);
                var lm = mid.luminance();
                if (Math.abs(lum - lm) < EPS || !max_iter--) {
                    // close enough
                    return mid;
                }
                return lm > lum ? test(low, mid) : test(mid, high);
            };

            var rgb = (cur_lum > lum ? test(new Color$i([0,0,0]), this) : test(this, new Color$i([255,255,255]))).rgb();
            return new Color$i(rgb.concat( [this._rgb[3]]));
        }
        return rgb2luminance.apply(void 0, (this._rgb).slice(0,3));
    };


    var rgb2luminance = function (r,g,b) {
        // relative luminance
        // see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
        r = luminance_x(r);
        g = luminance_x(g);
        b = luminance_x(b);
        return 0.2126 * r + 0.7152 * g + 0.0722 * b;
    };

    var luminance_x = function (x) {
        x /= 255;
        return x <= 0.03928 ? x/12.92 : pow$6((x+0.055)/1.055, 2.4);
    };

    var interpolator$1 = {};

    var Color$h = Color_1;
    var type$4 = utils.type;
    var interpolator = interpolator$1;

    var mix$1 = function (col1, col2, f) {
        if ( f === void 0 ) f=0.5;
        var rest = [], len = arguments.length - 3;
        while ( len-- > 0 ) rest[ len ] = arguments[ len + 3 ];

        var mode = rest[0] || 'lrgb';
        if (!interpolator[mode] && !rest.length) {
            // fall back to the first supported mode
            mode = Object.keys(interpolator)[0];
        }
        if (!interpolator[mode]) {
            throw new Error(("interpolation mode " + mode + " is not defined"));
        }
        if (type$4(col1) !== 'object') { col1 = new Color$h(col1); }
        if (type$4(col2) !== 'object') { col2 = new Color$h(col2); }
        return interpolator[mode](col1, col2, f)
            .alpha(col1.alpha() + f * (col2.alpha() - col1.alpha()));
    };

    var Color$g = Color_1;
    var mix = mix$1;

    Color$g.prototype.mix =
    Color$g.prototype.interpolate = function(col2, f) {
    	if ( f === void 0 ) f=0.5;
    	var rest = [], len = arguments.length - 2;
    	while ( len-- > 0 ) rest[ len ] = arguments[ len + 2 ];

    	return mix.apply(void 0, [ this, col2, f ].concat( rest ));
    };

    var Color$f = Color_1;

    Color$f.prototype.premultiply = function(mutate) {
    	if ( mutate === void 0 ) mutate=false;

    	var rgb = this._rgb;
    	var a = rgb[3];
    	if (mutate) {
    		this._rgb = [rgb[0]*a, rgb[1]*a, rgb[2]*a, a];
    		return this;
    	} else {
    		return new Color$f([rgb[0]*a, rgb[1]*a, rgb[2]*a, a], 'rgb');
    	}
    };

    var Color$e = Color_1;
    var LAB_CONSTANTS = labConstants;

    Color$e.prototype.saturate = function(amount) {
    	if ( amount === void 0 ) amount=1;

    	var me = this;
    	var lch = me.lch();
    	lch[1] += LAB_CONSTANTS.Kn * amount;
    	if (lch[1] < 0) { lch[1] = 0; }
    	return new Color$e(lch, 'lch').alpha(me.alpha(), true);
    };

    Color$e.prototype.desaturate = function(amount) {
    	if ( amount === void 0 ) amount=1;

    	return this.saturate(-amount);
    };

    var Color$d = Color_1;
    var type$3 = utils.type;

    Color$d.prototype.set = function (mc, value, mutate) {
        if ( mutate === void 0 ) mutate = false;

        var ref = mc.split('.');
        var mode = ref[0];
        var channel = ref[1];
        var src = this[mode]();
        if (channel) {
            var i = mode.indexOf(channel) - (mode.substr(0, 2) === 'ok' ? 2 : 0);
            if (i > -1) {
                if (type$3(value) == 'string') {
                    switch (value.charAt(0)) {
                        case '+':
                            src[i] += +value;
                            break;
                        case '-':
                            src[i] += +value;
                            break;
                        case '*':
                            src[i] *= +value.substr(1);
                            break;
                        case '/':
                            src[i] /= +value.substr(1);
                            break;
                        default:
                            src[i] = +value;
                    }
                } else if (type$3(value) === 'number') {
                    src[i] = value;
                } else {
                    throw new Error("unsupported value for Color.set");
                }
                var out = new Color$d(src, mode);
                if (mutate) {
                    this._rgb = out._rgb;
                    return this;
                }
                return out;
            }
            throw new Error(("unknown channel " + channel + " in mode " + mode));
        } else {
            return src;
        }
    };

    var Color$c = Color_1;

    var rgb = function (col1, col2, f) {
        var xyz0 = col1._rgb;
        var xyz1 = col2._rgb;
        return new Color$c(
            xyz0[0] + f * (xyz1[0]-xyz0[0]),
            xyz0[1] + f * (xyz1[1]-xyz0[1]),
            xyz0[2] + f * (xyz1[2]-xyz0[2]),
            'rgb'
        )
    };

    // register interpolator
    interpolator$1.rgb = rgb;

    var Color$b = Color_1;
    var sqrt$2 = Math.sqrt;
    var pow$5 = Math.pow;

    var lrgb = function (col1, col2, f) {
        var ref = col1._rgb;
        var x1 = ref[0];
        var y1 = ref[1];
        var z1 = ref[2];
        var ref$1 = col2._rgb;
        var x2 = ref$1[0];
        var y2 = ref$1[1];
        var z2 = ref$1[2];
        return new Color$b(
            sqrt$2(pow$5(x1,2) * (1-f) + pow$5(x2,2) * f),
            sqrt$2(pow$5(y1,2) * (1-f) + pow$5(y2,2) * f),
            sqrt$2(pow$5(z1,2) * (1-f) + pow$5(z2,2) * f),
            'rgb'
        )
    };

    // register interpolator
    interpolator$1.lrgb = lrgb;

    var Color$a = Color_1;

    var lab = function (col1, col2, f) {
        var xyz0 = col1.lab();
        var xyz1 = col2.lab();
        return new Color$a(
            xyz0[0] + f * (xyz1[0]-xyz0[0]),
            xyz0[1] + f * (xyz1[1]-xyz0[1]),
            xyz0[2] + f * (xyz1[2]-xyz0[2]),
            'lab'
        )
    };

    // register interpolator
    interpolator$1.lab = lab;

    var Color$9 = Color_1;

    var _hsx = function (col1, col2, f, m) {
        var assign, assign$1;

        var xyz0, xyz1;
        if (m === 'hsl') {
            xyz0 = col1.hsl();
            xyz1 = col2.hsl();
        } else if (m === 'hsv') {
            xyz0 = col1.hsv();
            xyz1 = col2.hsv();
        } else if (m === 'hcg') {
            xyz0 = col1.hcg();
            xyz1 = col2.hcg();
        } else if (m === 'hsi') {
            xyz0 = col1.hsi();
            xyz1 = col2.hsi();
        } else if (m === 'lch' || m === 'hcl') {
            m = 'hcl';
            xyz0 = col1.hcl();
            xyz1 = col2.hcl();
        } else if (m === 'oklch') {
            xyz0 = col1.oklch().reverse();
            xyz1 = col2.oklch().reverse();
        }

        var hue0, hue1, sat0, sat1, lbv0, lbv1;
        if (m.substr(0, 1) === 'h' || m === 'oklch') {
            (assign = xyz0, hue0 = assign[0], sat0 = assign[1], lbv0 = assign[2]);
            (assign$1 = xyz1, hue1 = assign$1[0], sat1 = assign$1[1], lbv1 = assign$1[2]);
        }

        var sat, hue, lbv, dh;

        if (!isNaN(hue0) && !isNaN(hue1)) {
            // both colors have hue
            if (hue1 > hue0 && hue1 - hue0 > 180) {
                dh = hue1 - (hue0 + 360);
            } else if (hue1 < hue0 && hue0 - hue1 > 180) {
                dh = hue1 + 360 - hue0;
            } else {
                dh = hue1 - hue0;
            }
            hue = hue0 + f * dh;
        } else if (!isNaN(hue0)) {
            hue = hue0;
            if ((lbv1 == 1 || lbv1 == 0) && m != 'hsv') { sat = sat0; }
        } else if (!isNaN(hue1)) {
            hue = hue1;
            if ((lbv0 == 1 || lbv0 == 0) && m != 'hsv') { sat = sat1; }
        } else {
            hue = Number.NaN;
        }

        if (sat === undefined) { sat = sat0 + f * (sat1 - sat0); }
        lbv = lbv0 + f * (lbv1 - lbv0);
        return m === 'oklch' ? new Color$9([lbv, sat, hue], m) : new Color$9([hue, sat, lbv], m);
    };

    var interpolate_hsx$5 = _hsx;

    var lch = function (col1, col2, f) {
    	return interpolate_hsx$5(col1, col2, f, 'lch');
    };

    // register interpolator
    interpolator$1.lch = lch;
    interpolator$1.hcl = lch;

    var Color$8 = Color_1;

    var num = function (col1, col2, f) {
        var c1 = col1.num();
        var c2 = col2.num();
        return new Color$8(c1 + f * (c2-c1), 'num')
    };

    // register interpolator
    interpolator$1.num = num;

    var interpolate_hsx$4 = _hsx;

    var hcg = function (col1, col2, f) {
    	return interpolate_hsx$4(col1, col2, f, 'hcg');
    };

    // register interpolator
    interpolator$1.hcg = hcg;

    var interpolate_hsx$3 = _hsx;

    var hsi = function (col1, col2, f) {
    	return interpolate_hsx$3(col1, col2, f, 'hsi');
    };

    // register interpolator
    interpolator$1.hsi = hsi;

    var interpolate_hsx$2 = _hsx;

    var hsl = function (col1, col2, f) {
    	return interpolate_hsx$2(col1, col2, f, 'hsl');
    };

    // register interpolator
    interpolator$1.hsl = hsl;

    var interpolate_hsx$1 = _hsx;

    var hsv = function (col1, col2, f) {
    	return interpolate_hsx$1(col1, col2, f, 'hsv');
    };

    // register interpolator
    interpolator$1.hsv = hsv;

    var Color$7 = Color_1;

    var oklab = function (col1, col2, f) {
        var xyz0 = col1.oklab();
        var xyz1 = col2.oklab();
        return new Color$7(
            xyz0[0] + f * (xyz1[0] - xyz0[0]),
            xyz0[1] + f * (xyz1[1] - xyz0[1]),
            xyz0[2] + f * (xyz1[2] - xyz0[2]),
            'oklab'
        );
    };

    // register interpolator
    interpolator$1.oklab = oklab;

    var interpolate_hsx = _hsx;

    var oklch = function (col1, col2, f) {
        return interpolate_hsx(col1, col2, f, 'oklch');
    };

    // register interpolator
    interpolator$1.oklch = oklch;

    var Color$6 = Color_1;
    var clip_rgb$1 = utils.clip_rgb;
    var pow$4 = Math.pow;
    var sqrt$1 = Math.sqrt;
    var PI$1 = Math.PI;
    var cos$2 = Math.cos;
    var sin$2 = Math.sin;
    var atan2$1 = Math.atan2;

    var average = function (colors, mode, weights) {
        if ( mode === void 0 ) mode='lrgb';
        if ( weights === void 0 ) weights=null;

        var l = colors.length;
        if (!weights) { weights = Array.from(new Array(l)).map(function () { return 1; }); }
        // normalize weights
        var k = l / weights.reduce(function(a, b) { return a + b; });
        weights.forEach(function (w,i) { weights[i] *= k; });
        // convert colors to Color objects
        colors = colors.map(function (c) { return new Color$6(c); });
        if (mode === 'lrgb') {
            return _average_lrgb(colors, weights)
        }
        var first = colors.shift();
        var xyz = first.get(mode);
        var cnt = [];
        var dx = 0;
        var dy = 0;
        // initial color
        for (var i=0; i<xyz.length; i++) {
            xyz[i] = (xyz[i] || 0) * weights[0];
            cnt.push(isNaN(xyz[i]) ? 0 : weights[0]);
            if (mode.charAt(i) === 'h' && !isNaN(xyz[i])) {
                var A = xyz[i] / 180 * PI$1;
                dx += cos$2(A) * weights[0];
                dy += sin$2(A) * weights[0];
            }
        }

        var alpha = first.alpha() * weights[0];
        colors.forEach(function (c,ci) {
            var xyz2 = c.get(mode);
            alpha += c.alpha() * weights[ci+1];
            for (var i=0; i<xyz.length; i++) {
                if (!isNaN(xyz2[i])) {
                    cnt[i] += weights[ci+1];
                    if (mode.charAt(i) === 'h') {
                        var A = xyz2[i] / 180 * PI$1;
                        dx += cos$2(A) * weights[ci+1];
                        dy += sin$2(A) * weights[ci+1];
                    } else {
                        xyz[i] += xyz2[i] * weights[ci+1];
                    }
                }
            }
        });

        for (var i$1=0; i$1<xyz.length; i$1++) {
            if (mode.charAt(i$1) === 'h') {
                var A$1 = atan2$1(dy / cnt[i$1], dx / cnt[i$1]) / PI$1 * 180;
                while (A$1 < 0) { A$1 += 360; }
                while (A$1 >= 360) { A$1 -= 360; }
                xyz[i$1] = A$1;
            } else {
                xyz[i$1] = xyz[i$1]/cnt[i$1];
            }
        }
        alpha /= l;
        return (new Color$6(xyz, mode)).alpha(alpha > 0.99999 ? 1 : alpha, true);
    };


    var _average_lrgb = function (colors, weights) {
        var l = colors.length;
        var xyz = [0,0,0,0];
        for (var i=0; i < colors.length; i++) {
            var col = colors[i];
            var f = weights[i] / l;
            var rgb = col._rgb;
            xyz[0] += pow$4(rgb[0],2) * f;
            xyz[1] += pow$4(rgb[1],2) * f;
            xyz[2] += pow$4(rgb[2],2) * f;
            xyz[3] += rgb[3] * f;
        }
        xyz[0] = sqrt$1(xyz[0]);
        xyz[1] = sqrt$1(xyz[1]);
        xyz[2] = sqrt$1(xyz[2]);
        if (xyz[3] > 0.9999999) { xyz[3] = 1; }
        return new Color$6(clip_rgb$1(xyz));
    };

    // minimal multi-purpose interface

    // @requires utils color analyze

    var chroma$4 = chroma_1;
    var type$2 = utils.type;

    var pow$3 = Math.pow;

    var scale$2 = function(colors) {

        // constructor
        var _mode = 'rgb';
        var _nacol = chroma$4('#ccc');
        var _spread = 0;
        // const _fixed = false;
        var _domain = [0, 1];
        var _pos = [];
        var _padding = [0,0];
        var _classes = false;
        var _colors = [];
        var _out = false;
        var _min = 0;
        var _max = 1;
        var _correctLightness = false;
        var _colorCache = {};
        var _useCache = true;
        var _gamma = 1;

        // private methods

        var setColors = function(colors) {
            colors = colors || ['#fff', '#000'];
            if (colors && type$2(colors) === 'string' && chroma$4.brewer &&
                chroma$4.brewer[colors.toLowerCase()]) {
                colors = chroma$4.brewer[colors.toLowerCase()];
            }
            if (type$2(colors) === 'array') {
                // handle single color
                if (colors.length === 1) {
                    colors = [colors[0], colors[0]];
                }
                // make a copy of the colors
                colors = colors.slice(0);
                // convert to chroma classes
                for (var c=0; c<colors.length; c++) {
                    colors[c] = chroma$4(colors[c]);
                }
                // auto-fill color position
                _pos.length = 0;
                for (var c$1=0; c$1<colors.length; c$1++) {
                    _pos.push(c$1/(colors.length-1));
                }
            }
            resetCache();
            return _colors = colors;
        };

        var getClass = function(value) {
            if (_classes != null) {
                var n = _classes.length-1;
                var i = 0;
                while (i < n && value >= _classes[i]) {
                    i++;
                }
                return i-1;
            }
            return 0;
        };

        var tMapLightness = function (t) { return t; };
        var tMapDomain = function (t) { return t; };

        // const classifyValue = function(value) {
        //     let val = value;
        //     if (_classes.length > 2) {
        //         const n = _classes.length-1;
        //         const i = getClass(value);
        //         const minc = _classes[0] + ((_classes[1]-_classes[0]) * (0 + (_spread * 0.5)));  // center of 1st class
        //         const maxc = _classes[n-1] + ((_classes[n]-_classes[n-1]) * (1 - (_spread * 0.5)));  // center of last class
        //         val = _min + ((((_classes[i] + ((_classes[i+1] - _classes[i]) * 0.5)) - minc) / (maxc-minc)) * (_max - _min));
        //     }
        //     return val;
        // };

        var getColor = function(val, bypassMap) {
            var col, t;
            if (bypassMap == null) { bypassMap = false; }
            if (isNaN(val) || (val === null)) { return _nacol; }
            if (!bypassMap) {
                if (_classes && (_classes.length > 2)) {
                    // find the class
                    var c = getClass(val);
                    t = c / (_classes.length-2);
                } else if (_max !== _min) {
                    // just interpolate between min/max
                    t = (val - _min) / (_max - _min);
                } else {
                    t = 1;
                }
            } else {
                t = val;
            }

            // domain map
            t = tMapDomain(t);

            if (!bypassMap) {
                t = tMapLightness(t);  // lightness correction
            }

            if (_gamma !== 1) { t = pow$3(t, _gamma); }

            t = _padding[0] + (t * (1 - _padding[0] - _padding[1]));

            t = Math.min(1, Math.max(0, t));

            var k = Math.floor(t * 10000);

            if (_useCache && _colorCache[k]) {
                col = _colorCache[k];
            } else {
                if (type$2(_colors) === 'array') {
                    //for i in [0.._pos.length-1]
                    for (var i=0; i<_pos.length; i++) {
                        var p = _pos[i];
                        if (t <= p) {
                            col = _colors[i];
                            break;
                        }
                        if ((t >= p) && (i === (_pos.length-1))) {
                            col = _colors[i];
                            break;
                        }
                        if (t > p && t < _pos[i+1]) {
                            t = (t-p)/(_pos[i+1]-p);
                            col = chroma$4.interpolate(_colors[i], _colors[i+1], t, _mode);
                            break;
                        }
                    }
                } else if (type$2(_colors) === 'function') {
                    col = _colors(t);
                }
                if (_useCache) { _colorCache[k] = col; }
            }
            return col;
        };

        var resetCache = function () { return _colorCache = {}; };

        setColors(colors);

        // public interface

        var f = function(v) {
            var c = chroma$4(getColor(v));
            if (_out && c[_out]) { return c[_out](); } else { return c; }
        };

        f.classes = function(classes) {
            if (classes != null) {
                if (type$2(classes) === 'array') {
                    _classes = classes;
                    _domain = [classes[0], classes[classes.length-1]];
                } else {
                    var d = chroma$4.analyze(_domain);
                    if (classes === 0) {
                        _classes = [d.min, d.max];
                    } else {
                        _classes = chroma$4.limits(d, 'e', classes);
                    }
                }
                return f;
            }
            return _classes;
        };


        f.domain = function(domain) {
            if (!arguments.length) {
                return _domain;
            }
            _min = domain[0];
            _max = domain[domain.length-1];
            _pos = [];
            var k = _colors.length;
            if ((domain.length === k) && (_min !== _max)) {
                // update positions
                for (var i = 0, list = Array.from(domain); i < list.length; i += 1) {
                    var d = list[i];

                  _pos.push((d-_min) / (_max-_min));
                }
            } else {
                for (var c=0; c<k; c++) {
                    _pos.push(c/(k-1));
                }
                if (domain.length > 2) {
                    // set domain map
                    var tOut = domain.map(function (d,i) { return i/(domain.length-1); });
                    var tBreaks = domain.map(function (d) { return (d - _min) / (_max - _min); });
                    if (!tBreaks.every(function (val, i) { return tOut[i] === val; })) {
                        tMapDomain = function (t) {
                            if (t <= 0 || t >= 1) { return t; }
                            var i = 0;
                            while (t >= tBreaks[i+1]) { i++; }
                            var f = (t - tBreaks[i]) / (tBreaks[i+1] - tBreaks[i]);
                            var out = tOut[i] + f * (tOut[i+1] - tOut[i]);
                            return out;
                        };
                    }

                }
            }
            _domain = [_min, _max];
            return f;
        };

        f.mode = function(_m) {
            if (!arguments.length) {
                return _mode;
            }
            _mode = _m;
            resetCache();
            return f;
        };

        f.range = function(colors, _pos) {
            setColors(colors);
            return f;
        };

        f.out = function(_o) {
            _out = _o;
            return f;
        };

        f.spread = function(val) {
            if (!arguments.length) {
                return _spread;
            }
            _spread = val;
            return f;
        };

        f.correctLightness = function(v) {
            if (v == null) { v = true; }
            _correctLightness = v;
            resetCache();
            if (_correctLightness) {
                tMapLightness = function(t) {
                    var L0 = getColor(0, true).lab()[0];
                    var L1 = getColor(1, true).lab()[0];
                    var pol = L0 > L1;
                    var L_actual = getColor(t, true).lab()[0];
                    var L_ideal = L0 + ((L1 - L0) * t);
                    var L_diff = L_actual - L_ideal;
                    var t0 = 0;
                    var t1 = 1;
                    var max_iter = 20;
                    while ((Math.abs(L_diff) > 1e-2) && (max_iter-- > 0)) {
                        (function() {
                            if (pol) { L_diff *= -1; }
                            if (L_diff < 0) {
                                t0 = t;
                                t += (t1 - t) * 0.5;
                            } else {
                                t1 = t;
                                t += (t0 - t) * 0.5;
                            }
                            L_actual = getColor(t, true).lab()[0];
                            return L_diff = L_actual - L_ideal;
                        })();
                    }
                    return t;
                };
            } else {
                tMapLightness = function (t) { return t; };
            }
            return f;
        };

        f.padding = function(p) {
            if (p != null) {
                if (type$2(p) === 'number') {
                    p = [p,p];
                }
                _padding = p;
                return f;
            } else {
                return _padding;
            }
        };

        f.colors = function(numColors, out) {
            // If no arguments are given, return the original colors that were provided
            if (arguments.length < 2) { out = 'hex'; }
            var result = [];

            if (arguments.length === 0) {
                result = _colors.slice(0);

            } else if (numColors === 1) {
                result = [f(0.5)];

            } else if (numColors > 1) {
                var dm = _domain[0];
                var dd = _domain[1] - dm;
                result = __range__(0, numColors, false).map(function (i) { return f( dm + ((i/(numColors-1)) * dd) ); });

            } else { // returns all colors based on the defined classes
                colors = [];
                var samples = [];
                if (_classes && (_classes.length > 2)) {
                    for (var i = 1, end = _classes.length, asc = 1 <= end; asc ? i < end : i > end; asc ? i++ : i--) {
                        samples.push((_classes[i-1]+_classes[i])*0.5);
                    }
                } else {
                    samples = _domain;
                }
                result = samples.map(function (v) { return f(v); });
            }

            if (chroma$4[out]) {
                result = result.map(function (c) { return c[out](); });
            }
            return result;
        };

        f.cache = function(c) {
            if (c != null) {
                _useCache = c;
                return f;
            } else {
                return _useCache;
            }
        };

        f.gamma = function(g) {
            if (g != null) {
                _gamma = g;
                return f;
            } else {
                return _gamma;
            }
        };

        f.nodata = function(d) {
            if (d != null) {
                _nacol = chroma$4(d);
                return f;
            } else {
                return _nacol;
            }
        };

        return f;
    };

    function __range__(left, right, inclusive) {
      var range = [];
      var ascending = left < right;
      var end = !inclusive ? right : ascending ? right + 1 : right - 1;
      for (var i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
        range.push(i);
      }
      return range;
    }

    //
    // interpolates between a set of colors uzing a bezier spline
    //

    // @requires utils lab
    var Color$5 = Color_1;

    var scale$1 = scale$2;

    // nth row of the pascal triangle
    var binom_row = function(n) {
        var row = [1, 1];
        for (var i = 1; i < n; i++) {
            var newrow = [1];
            for (var j = 1; j <= row.length; j++) {
                newrow[j] = (row[j] || 0) + row[j - 1];
            }
            row = newrow;
        }
        return row;
    };

    var bezier = function(colors) {
        var assign, assign$1, assign$2;

        var I, lab0, lab1, lab2;
        colors = colors.map(function (c) { return new Color$5(c); });
        if (colors.length === 2) {
            // linear interpolation
            (assign = colors.map(function (c) { return c.lab(); }), lab0 = assign[0], lab1 = assign[1]);
            I = function(t) {
                var lab = ([0, 1, 2].map(function (i) { return lab0[i] + (t * (lab1[i] - lab0[i])); }));
                return new Color$5(lab, 'lab');
            };
        } else if (colors.length === 3) {
            // quadratic bezier interpolation
            (assign$1 = colors.map(function (c) { return c.lab(); }), lab0 = assign$1[0], lab1 = assign$1[1], lab2 = assign$1[2]);
            I = function(t) {
                var lab = ([0, 1, 2].map(function (i) { return ((1-t)*(1-t) * lab0[i]) + (2 * (1-t) * t * lab1[i]) + (t * t * lab2[i]); }));
                return new Color$5(lab, 'lab');
            };
        } else if (colors.length === 4) {
            // cubic bezier interpolation
            var lab3;
            (assign$2 = colors.map(function (c) { return c.lab(); }), lab0 = assign$2[0], lab1 = assign$2[1], lab2 = assign$2[2], lab3 = assign$2[3]);
            I = function(t) {
                var lab = ([0, 1, 2].map(function (i) { return ((1-t)*(1-t)*(1-t) * lab0[i]) + (3 * (1-t) * (1-t) * t * lab1[i]) + (3 * (1-t) * t * t * lab2[i]) + (t*t*t * lab3[i]); }));
                return new Color$5(lab, 'lab');
            };
        } else if (colors.length >= 5) {
            // general case (degree n bezier)
            var labs, row, n;
            labs = colors.map(function (c) { return c.lab(); });
            n = colors.length - 1;
            row = binom_row(n);
            I = function (t) {
                var u = 1 - t;
                var lab = ([0, 1, 2].map(function (i) { return labs.reduce(function (sum, el, j) { return (sum + row[j] * Math.pow( u, (n - j) ) * Math.pow( t, j ) * el[i]); }, 0); }));
                return new Color$5(lab, 'lab');
            };
        } else {
            throw new RangeError("No point in running bezier with only one color.")
        }
        return I;
    };

    var bezier_1 = function (colors) {
        var f = bezier(colors);
        f.scale = function () { return scale$1(f); };
        return f;
    };

    /*
     * interpolates between a set of colors uzing a bezier spline
     * blend mode formulas taken from http://www.venture-ware.com/kevin/coding/lets-learn-math-photoshop-blend-modes/
     */

    var chroma$3 = chroma_1;

    var blend = function (bottom, top, mode) {
        if (!blend[mode]) {
            throw new Error('unknown blend mode ' + mode);
        }
        return blend[mode](bottom, top);
    };

    var blend_f = function (f) { return function (bottom,top) {
            var c0 = chroma$3(top).rgb();
            var c1 = chroma$3(bottom).rgb();
            return chroma$3.rgb(f(c0, c1));
        }; };

    var each = function (f) { return function (c0, c1) {
            var out = [];
            out[0] = f(c0[0], c1[0]);
            out[1] = f(c0[1], c1[1]);
            out[2] = f(c0[2], c1[2]);
            return out;
        }; };

    var normal = function (a) { return a; };
    var multiply = function (a,b) { return a * b / 255; };
    var darken = function (a,b) { return a > b ? b : a; };
    var lighten = function (a,b) { return a > b ? a : b; };
    var screen = function (a,b) { return 255 * (1 - (1-a/255) * (1-b/255)); };
    var overlay = function (a,b) { return b < 128 ? 2 * a * b / 255 : 255 * (1 - 2 * (1 - a / 255 ) * ( 1 - b / 255 )); };
    var burn = function (a,b) { return 255 * (1 - (1 - b / 255) / (a/255)); };
    var dodge = function (a,b) {
        if (a === 255) { return 255; }
        a = 255 * (b / 255) / (1 - a / 255);
        return a > 255 ? 255 : a
    };

    // # add = (a,b) ->
    // #     if (a + b > 255) then 255 else a + b

    blend.normal = blend_f(each(normal));
    blend.multiply = blend_f(each(multiply));
    blend.screen = blend_f(each(screen));
    blend.overlay = blend_f(each(overlay));
    blend.darken = blend_f(each(darken));
    blend.lighten = blend_f(each(lighten));
    blend.dodge = blend_f(each(dodge));
    blend.burn = blend_f(each(burn));
    // blend.add = blend_f(each(add));

    var blend_1 = blend;

    // cubehelix interpolation
    // based on D.A. Green "A colour scheme for the display of astronomical intensity images"
    // http://astron-soc.in/bulletin/11June/289392011.pdf

    var type$1 = utils.type;
    var clip_rgb = utils.clip_rgb;
    var TWOPI = utils.TWOPI;
    var pow$2 = Math.pow;
    var sin$1 = Math.sin;
    var cos$1 = Math.cos;
    var chroma$2 = chroma_1;

    var cubehelix = function(start, rotations, hue, gamma, lightness) {
        if ( start === void 0 ) start=300;
        if ( rotations === void 0 ) rotations=-1.5;
        if ( hue === void 0 ) hue=1;
        if ( gamma === void 0 ) gamma=1;
        if ( lightness === void 0 ) lightness=[0,1];

        var dh = 0, dl;
        if (type$1(lightness) === 'array') {
            dl = lightness[1] - lightness[0];
        } else {
            dl = 0;
            lightness = [lightness, lightness];
        }

        var f = function(fract) {
            var a = TWOPI * (((start+120)/360) + (rotations * fract));
            var l = pow$2(lightness[0] + (dl * fract), gamma);
            var h = dh !== 0 ? hue[0] + (fract * dh) : hue;
            var amp = (h * l * (1-l)) / 2;
            var cos_a = cos$1(a);
            var sin_a = sin$1(a);
            var r = l + (amp * ((-0.14861 * cos_a) + (1.78277* sin_a)));
            var g = l + (amp * ((-0.29227 * cos_a) - (0.90649* sin_a)));
            var b = l + (amp * (+1.97294 * cos_a));
            return chroma$2(clip_rgb([r*255,g*255,b*255,1]));
        };

        f.start = function(s) {
            if ((s == null)) { return start; }
            start = s;
            return f;
        };

        f.rotations = function(r) {
            if ((r == null)) { return rotations; }
            rotations = r;
            return f;
        };

        f.gamma = function(g) {
            if ((g == null)) { return gamma; }
            gamma = g;
            return f;
        };

        f.hue = function(h) {
            if ((h == null)) { return hue; }
            hue = h;
            if (type$1(hue) === 'array') {
                dh = hue[1] - hue[0];
                if (dh === 0) { hue = hue[1]; }
            } else {
                dh = 0;
            }
            return f;
        };

        f.lightness = function(h) {
            if ((h == null)) { return lightness; }
            if (type$1(h) === 'array') {
                lightness = h;
                dl = h[1] - h[0];
            } else {
                lightness = [h,h];
                dl = 0;
            }
            return f;
        };

        f.scale = function () { return chroma$2.scale(f); };

        f.hue(hue);

        return f;
    };

    var Color$4 = Color_1;
    var digits = '0123456789abcdef';

    var floor$1 = Math.floor;
    var random = Math.random;

    var random_1 = function () {
        var code = '#';
        for (var i=0; i<6; i++) {
            code += digits.charAt(floor$1(random() * 16));
        }
        return new Color$4(code, 'hex');
    };

    var type = type$p;
    var log = Math.log;
    var pow$1 = Math.pow;
    var floor = Math.floor;
    var abs$1 = Math.abs;


    var analyze = function (data, key) {
        if ( key === void 0 ) key=null;

        var r = {
            min: Number.MAX_VALUE,
            max: Number.MAX_VALUE*-1,
            sum: 0,
            values: [],
            count: 0
        };
        if (type(data) === 'object') {
            data = Object.values(data);
        }
        data.forEach(function (val) {
            if (key && type(val) === 'object') { val = val[key]; }
            if (val !== undefined && val !== null && !isNaN(val)) {
                r.values.push(val);
                r.sum += val;
                if (val < r.min) { r.min = val; }
                if (val > r.max) { r.max = val; }
                r.count += 1;
            }
        });

        r.domain = [r.min, r.max];

        r.limits = function (mode, num) { return limits(r, mode, num); };

        return r;
    };


    var limits = function (data, mode, num) {
        if ( mode === void 0 ) mode='equal';
        if ( num === void 0 ) num=7;

        if (type(data) == 'array') {
            data = analyze(data);
        }
        var min = data.min;
        var max = data.max;
        var values = data.values.sort(function (a,b) { return a-b; });

        if (num === 1) { return [min,max]; }

        var limits = [];

        if (mode.substr(0,1) === 'c') { // continuous
            limits.push(min);
            limits.push(max);
        }

        if (mode.substr(0,1) === 'e') { // equal interval
            limits.push(min);
            for (var i=1; i<num; i++) {
                limits.push(min+((i/num)*(max-min)));
            }
            limits.push(max);
        }

        else if (mode.substr(0,1) === 'l') { // log scale
            if (min <= 0) {
                throw new Error('Logarithmic scales are only possible for values > 0');
            }
            var min_log = Math.LOG10E * log(min);
            var max_log = Math.LOG10E * log(max);
            limits.push(min);
            for (var i$1=1; i$1<num; i$1++) {
                limits.push(pow$1(10, min_log + ((i$1/num) * (max_log - min_log))));
            }
            limits.push(max);
        }

        else if (mode.substr(0,1) === 'q') { // quantile scale
            limits.push(min);
            for (var i$2=1; i$2<num; i$2++) {
                var p = ((values.length-1) * i$2)/num;
                var pb = floor(p);
                if (pb === p) {
                    limits.push(values[pb]);
                } else { // p > pb
                    var pr = p - pb;
                    limits.push((values[pb]*(1-pr)) + (values[pb+1]*pr));
                }
            }
            limits.push(max);

        }

        else if (mode.substr(0,1) === 'k') { // k-means clustering
            /*
            implementation based on
            http://code.google.com/p/figue/source/browse/trunk/figue.js#336
            simplified for 1-d input values
            */
            var cluster;
            var n = values.length;
            var assignments = new Array(n);
            var clusterSizes = new Array(num);
            var repeat = true;
            var nb_iters = 0;
            var centroids = null;

            // get seed values
            centroids = [];
            centroids.push(min);
            for (var i$3=1; i$3<num; i$3++) {
                centroids.push(min + ((i$3/num) * (max-min)));
            }
            centroids.push(max);

            while (repeat) {
                // assignment step
                for (var j=0; j<num; j++) {
                    clusterSizes[j] = 0;
                }
                for (var i$4=0; i$4<n; i$4++) {
                    var value = values[i$4];
                    var mindist = Number.MAX_VALUE;
                    var best = (void 0);
                    for (var j$1=0; j$1<num; j$1++) {
                        var dist = abs$1(centroids[j$1]-value);
                        if (dist < mindist) {
                            mindist = dist;
                            best = j$1;
                        }
                        clusterSizes[best]++;
                        assignments[i$4] = best;
                    }
                }

                // update centroids step
                var newCentroids = new Array(num);
                for (var j$2=0; j$2<num; j$2++) {
                    newCentroids[j$2] = null;
                }
                for (var i$5=0; i$5<n; i$5++) {
                    cluster = assignments[i$5];
                    if (newCentroids[cluster] === null) {
                        newCentroids[cluster] = values[i$5];
                    } else {
                        newCentroids[cluster] += values[i$5];
                    }
                }
                for (var j$3=0; j$3<num; j$3++) {
                    newCentroids[j$3] *= 1/clusterSizes[j$3];
                }

                // check convergence
                repeat = false;
                for (var j$4=0; j$4<num; j$4++) {
                    if (newCentroids[j$4] !== centroids[j$4]) {
                        repeat = true;
                        break;
                    }
                }

                centroids = newCentroids;
                nb_iters++;

                if (nb_iters > 200) {
                    repeat = false;
                }
            }

            // finished k-means clustering
            // the next part is borrowed from gabrielflor.it
            var kClusters = {};
            for (var j$5=0; j$5<num; j$5++) {
                kClusters[j$5] = [];
            }
            for (var i$6=0; i$6<n; i$6++) {
                cluster = assignments[i$6];
                kClusters[cluster].push(values[i$6]);
            }
            var tmpKMeansBreaks = [];
            for (var j$6=0; j$6<num; j$6++) {
                tmpKMeansBreaks.push(kClusters[j$6][0]);
                tmpKMeansBreaks.push(kClusters[j$6][kClusters[j$6].length-1]);
            }
            tmpKMeansBreaks = tmpKMeansBreaks.sort(function (a,b){ return a-b; });
            limits.push(tmpKMeansBreaks[0]);
            for (var i$7=1; i$7 < tmpKMeansBreaks.length; i$7+= 2) {
                var v = tmpKMeansBreaks[i$7];
                if (!isNaN(v) && (limits.indexOf(v) === -1)) {
                    limits.push(v);
                }
            }
        }
        return limits;
    };

    var analyze_1 = {analyze: analyze, limits: limits};

    var Color$3 = Color_1;


    var contrast = function (a, b) {
        // WCAG contrast ratio
        // see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
        a = new Color$3(a);
        b = new Color$3(b);
        var l1 = a.luminance();
        var l2 = b.luminance();
        return l1 > l2 ? (l1 + 0.05) / (l2 + 0.05) : (l2 + 0.05) / (l1 + 0.05);
    };

    var Color$2 = Color_1;
    var sqrt = Math.sqrt;
    var pow = Math.pow;
    var min = Math.min;
    var max = Math.max;
    var atan2 = Math.atan2;
    var abs = Math.abs;
    var cos = Math.cos;
    var sin = Math.sin;
    var exp = Math.exp;
    var PI = Math.PI;

    var deltaE = function(a, b, Kl, Kc, Kh) {
        if ( Kl === void 0 ) Kl=1;
        if ( Kc === void 0 ) Kc=1;
        if ( Kh === void 0 ) Kh=1;

        // Delta E (CIE 2000)
        // see http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html
        var rad2deg = function(rad) {
            return 360 * rad / (2 * PI);
        };
        var deg2rad = function(deg) {
            return (2 * PI * deg) / 360;
        };
        a = new Color$2(a);
        b = new Color$2(b);
        var ref = Array.from(a.lab());
        var L1 = ref[0];
        var a1 = ref[1];
        var b1 = ref[2];
        var ref$1 = Array.from(b.lab());
        var L2 = ref$1[0];
        var a2 = ref$1[1];
        var b2 = ref$1[2];
        var avgL = (L1 + L2)/2;
        var C1 = sqrt(pow(a1, 2) + pow(b1, 2));
        var C2 = sqrt(pow(a2, 2) + pow(b2, 2));
        var avgC = (C1 + C2)/2;
        var G = 0.5*(1-sqrt(pow(avgC, 7)/(pow(avgC, 7) + pow(25, 7))));
        var a1p = a1*(1+G);
        var a2p = a2*(1+G);
        var C1p = sqrt(pow(a1p, 2) + pow(b1, 2));
        var C2p = sqrt(pow(a2p, 2) + pow(b2, 2));
        var avgCp = (C1p + C2p)/2;
        var arctan1 = rad2deg(atan2(b1, a1p));
        var arctan2 = rad2deg(atan2(b2, a2p));
        var h1p = arctan1 >= 0 ? arctan1 : arctan1 + 360;
        var h2p = arctan2 >= 0 ? arctan2 : arctan2 + 360;
        var avgHp = abs(h1p - h2p) > 180 ? (h1p + h2p + 360)/2 : (h1p + h2p)/2;
        var T = 1 - 0.17*cos(deg2rad(avgHp - 30)) + 0.24*cos(deg2rad(2*avgHp)) + 0.32*cos(deg2rad(3*avgHp + 6)) - 0.2*cos(deg2rad(4*avgHp - 63));
        var deltaHp = h2p - h1p;
        deltaHp = abs(deltaHp) <= 180 ? deltaHp : h2p <= h1p ? deltaHp + 360 : deltaHp - 360;
        deltaHp = 2*sqrt(C1p*C2p)*sin(deg2rad(deltaHp)/2);
        var deltaL = L2 - L1;
        var deltaCp = C2p - C1p;    
        var sl = 1 + (0.015*pow(avgL - 50, 2))/sqrt(20 + pow(avgL - 50, 2));
        var sc = 1 + 0.045*avgCp;
        var sh = 1 + 0.015*avgCp*T;
        var deltaTheta = 30*exp(-pow((avgHp - 275)/25, 2));
        var Rc = 2*sqrt(pow(avgCp, 7)/(pow(avgCp, 7) + pow(25, 7)));
        var Rt = -Rc*sin(2*deg2rad(deltaTheta));
        var result = sqrt(pow(deltaL/(Kl*sl), 2) + pow(deltaCp/(Kc*sc), 2) + pow(deltaHp/(Kh*sh), 2) + Rt*(deltaCp/(Kc*sc))*(deltaHp/(Kh*sh)));
        return max(0, min(100, result));
    };

    var Color$1 = Color_1;

    // simple Euclidean distance
    var distance = function(a, b, mode) {
        if ( mode === void 0 ) mode='lab';

        // Delta E (CIE 1976)
        // see http://www.brucelindbloom.com/index.html?Equations.html
        a = new Color$1(a);
        b = new Color$1(b);
        var l1 = a.get(mode);
        var l2 = b.get(mode);
        var sum_sq = 0;
        for (var i in l1) {
            var d = (l1[i] || 0) - (l2[i] || 0);
            sum_sq += d*d;
        }
        return Math.sqrt(sum_sq);
    };

    var Color = Color_1;

    var valid = function () {
        var args = [], len = arguments.length;
        while ( len-- ) args[ len ] = arguments[ len ];

        try {
            new (Function.prototype.bind.apply( Color, [ null ].concat( args) ));
            return true;
        } catch (e) {
            return false;
        }
    };

    // some pre-defined color scales:
    var chroma$1 = chroma_1;

    var scale = scale$2;

    var scales = {
    	cool: function cool() { return scale([chroma$1.hsl(180,1,.9), chroma$1.hsl(250,.7,.4)]) },
    	hot: function hot() { return scale(['#000','#f00','#ff0','#fff']).mode('rgb') }
    };

    /**
        ColorBrewer colors for chroma.js

        Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The
        Pennsylvania State University.

        Licensed under the Apache License, Version 2.0 (the "License");
        you may not use this file except in compliance with the License.
        You may obtain a copy of the License at
        http://www.apache.org/licenses/LICENSE-2.0

        Unless required by applicable law or agreed to in writing, software distributed
        under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
        CONDITIONS OF ANY KIND, either express or implied. See the License for the
        specific language governing permissions and limitations under the License.
    */

    var colorbrewer = {
        // sequential
        OrRd: ['#fff7ec', '#fee8c8', '#fdd49e', '#fdbb84', '#fc8d59', '#ef6548', '#d7301f', '#b30000', '#7f0000'],
        PuBu: ['#fff7fb', '#ece7f2', '#d0d1e6', '#a6bddb', '#74a9cf', '#3690c0', '#0570b0', '#045a8d', '#023858'],
        BuPu: ['#f7fcfd', '#e0ecf4', '#bfd3e6', '#9ebcda', '#8c96c6', '#8c6bb1', '#88419d', '#810f7c', '#4d004b'],
        Oranges: ['#fff5eb', '#fee6ce', '#fdd0a2', '#fdae6b', '#fd8d3c', '#f16913', '#d94801', '#a63603', '#7f2704'],
        BuGn: ['#f7fcfd', '#e5f5f9', '#ccece6', '#99d8c9', '#66c2a4', '#41ae76', '#238b45', '#006d2c', '#00441b'],
        YlOrBr: ['#ffffe5', '#fff7bc', '#fee391', '#fec44f', '#fe9929', '#ec7014', '#cc4c02', '#993404', '#662506'],
        YlGn: ['#ffffe5', '#f7fcb9', '#d9f0a3', '#addd8e', '#78c679', '#41ab5d', '#238443', '#006837', '#004529'],
        Reds: ['#fff5f0', '#fee0d2', '#fcbba1', '#fc9272', '#fb6a4a', '#ef3b2c', '#cb181d', '#a50f15', '#67000d'],
        RdPu: ['#fff7f3', '#fde0dd', '#fcc5c0', '#fa9fb5', '#f768a1', '#dd3497', '#ae017e', '#7a0177', '#49006a'],
        Greens: ['#f7fcf5', '#e5f5e0', '#c7e9c0', '#a1d99b', '#74c476', '#41ab5d', '#238b45', '#006d2c', '#00441b'],
        YlGnBu: ['#ffffd9', '#edf8b1', '#c7e9b4', '#7fcdbb', '#41b6c4', '#1d91c0', '#225ea8', '#253494', '#081d58'],
        Purples: ['#fcfbfd', '#efedf5', '#dadaeb', '#bcbddc', '#9e9ac8', '#807dba', '#6a51a3', '#54278f', '#3f007d'],
        GnBu: ['#f7fcf0', '#e0f3db', '#ccebc5', '#a8ddb5', '#7bccc4', '#4eb3d3', '#2b8cbe', '#0868ac', '#084081'],
        Greys: ['#ffffff', '#f0f0f0', '#d9d9d9', '#bdbdbd', '#969696', '#737373', '#525252', '#252525', '#000000'],
        YlOrRd: ['#ffffcc', '#ffeda0', '#fed976', '#feb24c', '#fd8d3c', '#fc4e2a', '#e31a1c', '#bd0026', '#800026'],
        PuRd: ['#f7f4f9', '#e7e1ef', '#d4b9da', '#c994c7', '#df65b0', '#e7298a', '#ce1256', '#980043', '#67001f'],
        Blues: ['#f7fbff', '#deebf7', '#c6dbef', '#9ecae1', '#6baed6', '#4292c6', '#2171b5', '#08519c', '#08306b'],
        PuBuGn: ['#fff7fb', '#ece2f0', '#d0d1e6', '#a6bddb', '#67a9cf', '#3690c0', '#02818a', '#016c59', '#014636'],
        Viridis: ['#440154', '#482777', '#3f4a8a', '#31678e', '#26838f', '#1f9d8a', '#6cce5a', '#b6de2b', '#fee825'],

        // diverging

        Spectral: ['#9e0142', '#d53e4f', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#e6f598', '#abdda4', '#66c2a5', '#3288bd', '#5e4fa2'],
        RdYlGn: ['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850', '#006837'],
        RdBu: ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#f7f7f7', '#d1e5f0', '#92c5de', '#4393c3', '#2166ac', '#053061'],
        PiYG: ['#8e0152', '#c51b7d', '#de77ae', '#f1b6da', '#fde0ef', '#f7f7f7', '#e6f5d0', '#b8e186', '#7fbc41', '#4d9221', '#276419'],
        PRGn: ['#40004b', '#762a83', '#9970ab', '#c2a5cf', '#e7d4e8', '#f7f7f7', '#d9f0d3', '#a6dba0', '#5aae61', '#1b7837', '#00441b'],
        RdYlBu: ['#a50026', '#d73027', '#f46d43', '#fdae61', '#fee090', '#ffffbf', '#e0f3f8', '#abd9e9', '#74add1', '#4575b4', '#313695'],
        BrBG: ['#543005', '#8c510a', '#bf812d', '#dfc27d', '#f6e8c3', '#f5f5f5', '#c7eae5', '#80cdc1', '#35978f', '#01665e', '#003c30'],
        RdGy: ['#67001f', '#b2182b', '#d6604d', '#f4a582', '#fddbc7', '#ffffff', '#e0e0e0', '#bababa', '#878787', '#4d4d4d', '#1a1a1a'],
        PuOr: ['#7f3b08', '#b35806', '#e08214', '#fdb863', '#fee0b6', '#f7f7f7', '#d8daeb', '#b2abd2', '#8073ac', '#542788', '#2d004b'],

        // qualitative

        Set2: ['#66c2a5', '#fc8d62', '#8da0cb', '#e78ac3', '#a6d854', '#ffd92f', '#e5c494', '#b3b3b3'],
        Accent: ['#7fc97f', '#beaed4', '#fdc086', '#ffff99', '#386cb0', '#f0027f', '#bf5b17', '#666666'],
        Set1: ['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00', '#ffff33', '#a65628', '#f781bf', '#999999'],
        Set3: ['#8dd3c7', '#ffffb3', '#bebada', '#fb8072', '#80b1d3', '#fdb462', '#b3de69', '#fccde5', '#d9d9d9', '#bc80bd', '#ccebc5', '#ffed6f'],
        Dark2: ['#1b9e77', '#d95f02', '#7570b3', '#e7298a', '#66a61e', '#e6ab02', '#a6761d', '#666666'],
        Paired: ['#a6cee3', '#1f78b4', '#b2df8a', '#33a02c', '#fb9a99', '#e31a1c', '#fdbf6f', '#ff7f00', '#cab2d6', '#6a3d9a', '#ffff99', '#b15928'],
        Pastel2: ['#b3e2cd', '#fdcdac', '#cbd5e8', '#f4cae4', '#e6f5c9', '#fff2ae', '#f1e2cc', '#cccccc'],
        Pastel1: ['#fbb4ae', '#b3cde3', '#ccebc5', '#decbe4', '#fed9a6', '#ffffcc', '#e5d8bd', '#fddaec', '#f2f2f2'],
    };

    // add lowercase aliases for case-insensitive matches
    for (var i = 0, list = Object.keys(colorbrewer); i < list.length; i += 1) {
        var key = list[i];

        colorbrewer[key.toLowerCase()] = colorbrewer[key];
    }

    var colorbrewer_1 = colorbrewer;

    var chroma = chroma_1;

    // feel free to comment out anything to rollup
    // a smaller chroma.js built

    // io --> convert colors

















    // operators --> modify existing Colors










    // interpolators












    // generators -- > create new colors
    chroma.average = average;
    chroma.bezier = bezier_1;
    chroma.blend = blend_1;
    chroma.cubehelix = cubehelix;
    chroma.mix = chroma.interpolate = mix$1;
    chroma.random = random_1;
    chroma.scale = scale$2;

    // other utility methods
    chroma.analyze = analyze_1.analyze;
    chroma.contrast = contrast;
    chroma.deltaE = deltaE;
    chroma.distance = distance;
    chroma.limits = analyze_1.limits;
    chroma.valid = valid;

    // scale
    chroma.scales = scales;

    // colors
    chroma.colors = w3cx11_1;
    chroma.brewer = colorbrewer_1;

    var chroma_js = chroma;

    return chroma_js;

}));

},{}],66:[function(require,module,exports){
"use strict"

var createThunk = require("./lib/thunk.js")

function Procedure() {
  this.argTypes = []
  this.shimArgs = []
  this.arrayArgs = []
  this.arrayBlockIndices = []
  this.scalarArgs = []
  this.offsetArgs = []
  this.offsetArgIndex = []
  this.indexArgs = []
  this.shapeArgs = []
  this.funcName = ""
  this.pre = null
  this.body = null
  this.post = null
  this.debug = false
}

function compileCwise(user_args) {
  //Create procedure
  var proc = new Procedure()
  
  //Parse blocks
  proc.pre    = user_args.pre
  proc.body   = user_args.body
  proc.post   = user_args.post

  //Parse arguments
  var proc_args = user_args.args.slice(0)
  proc.argTypes = proc_args
  for(var i=0; i<proc_args.length; ++i) {
    var arg_type = proc_args[i]
    if(arg_type === "array" || (typeof arg_type === "object" && arg_type.blockIndices)) {
      proc.argTypes[i] = "array"
      proc.arrayArgs.push(i)
      proc.arrayBlockIndices.push(arg_type.blockIndices ? arg_type.blockIndices : 0)
      proc.shimArgs.push("array" + i)
      if(i < proc.pre.args.length && proc.pre.args[i].count>0) {
        throw new Error("cwise: pre() block may not reference array args")
      }
      if(i < proc.post.args.length && proc.post.args[i].count>0) {
        throw new Error("cwise: post() block may not reference array args")
      }
    } else if(arg_type === "scalar") {
      proc.scalarArgs.push(i)
      proc.shimArgs.push("scalar" + i)
    } else if(arg_type === "index") {
      proc.indexArgs.push(i)
      if(i < proc.pre.args.length && proc.pre.args[i].count > 0) {
        throw new Error("cwise: pre() block may not reference array index")
      }
      if(i < proc.body.args.length && proc.body.args[i].lvalue) {
        throw new Error("cwise: body() block may not write to array index")
      }
      if(i < proc.post.args.length && proc.post.args[i].count > 0) {
        throw new Error("cwise: post() block may not reference array index")
      }
    } else if(arg_type === "shape") {
      proc.shapeArgs.push(i)
      if(i < proc.pre.args.length && proc.pre.args[i].lvalue) {
        throw new Error("cwise: pre() block may not write to array shape")
      }
      if(i < proc.body.args.length && proc.body.args[i].lvalue) {
        throw new Error("cwise: body() block may not write to array shape")
      }
      if(i < proc.post.args.length && proc.post.args[i].lvalue) {
        throw new Error("cwise: post() block may not write to array shape")
      }
    } else if(typeof arg_type === "object" && arg_type.offset) {
      proc.argTypes[i] = "offset"
      proc.offsetArgs.push({ array: arg_type.array, offset:arg_type.offset })
      proc.offsetArgIndex.push(i)
    } else {
      throw new Error("cwise: Unknown argument type " + proc_args[i])
    }
  }
  
  //Make sure at least one array argument was specified
  if(proc.arrayArgs.length <= 0) {
    throw new Error("cwise: No array arguments specified")
  }
  
  //Make sure arguments are correct
  if(proc.pre.args.length > proc_args.length) {
    throw new Error("cwise: Too many arguments in pre() block")
  }
  if(proc.body.args.length > proc_args.length) {
    throw new Error("cwise: Too many arguments in body() block")
  }
  if(proc.post.args.length > proc_args.length) {
    throw new Error("cwise: Too many arguments in post() block")
  }

  //Check debug flag
  proc.debug = !!user_args.printCode || !!user_args.debug
  
  //Retrieve name
  proc.funcName = user_args.funcName || "cwise"
  
  //Read in block size
  proc.blockSize = user_args.blockSize || 64

  return createThunk(proc)
}

module.exports = compileCwise

},{"./lib/thunk.js":68}],67:[function(require,module,exports){
"use strict"

var uniq = require("uniq")

// This function generates very simple loops analogous to how you typically traverse arrays (the outermost loop corresponds to the slowest changing index, the innermost loop to the fastest changing index)
// TODO: If two arrays have the same strides (and offsets) there is potential for decreasing the number of "pointers" and related variables. The drawback is that the type signature would become more specific and that there would thus be less potential for caching, but it might still be worth it, especially when dealing with large numbers of arguments.
function innerFill(order, proc, body) {
  var dimension = order.length
    , nargs = proc.arrayArgs.length
    , has_index = proc.indexArgs.length>0
    , code = []
    , vars = []
    , idx=0, pidx=0, i, j
  for(i=0; i<dimension; ++i) { // Iteration variables
    vars.push(["i",i,"=0"].join(""))
  }
  //Compute scan deltas
  for(j=0; j<nargs; ++j) {
    for(i=0; i<dimension; ++i) {
      pidx = idx
      idx = order[i]
      if(i === 0) { // The innermost/fastest dimension's delta is simply its stride
        vars.push(["d",j,"s",i,"=t",j,"p",idx].join(""))
      } else { // For other dimensions the delta is basically the stride minus something which essentially "rewinds" the previous (more inner) dimension
        vars.push(["d",j,"s",i,"=(t",j,"p",idx,"-s",pidx,"*t",j,"p",pidx,")"].join(""))
      }
    }
  }
  if (vars.length > 0) {
    code.push("var " + vars.join(","))
  }  
  //Scan loop
  for(i=dimension-1; i>=0; --i) { // Start at largest stride and work your way inwards
    idx = order[i]
    code.push(["for(i",i,"=0;i",i,"<s",idx,";++i",i,"){"].join(""))
  }
  //Push body of inner loop
  code.push(body)
  //Advance scan pointers
  for(i=0; i<dimension; ++i) {
    pidx = idx
    idx = order[i]
    for(j=0; j<nargs; ++j) {
      code.push(["p",j,"+=d",j,"s",i].join(""))
    }
    if(has_index) {
      if(i > 0) {
        code.push(["index[",pidx,"]-=s",pidx].join(""))
      }
      code.push(["++index[",idx,"]"].join(""))
    }
    code.push("}")
  }
  return code.join("\n")
}

// Generate "outer" loops that loop over blocks of data, applying "inner" loops to the blocks by manipulating the local variables in such a way that the inner loop only "sees" the current block.
// TODO: If this is used, then the previous declaration (done by generateCwiseOp) of s* is essentially unnecessary.
//       I believe the s* are not used elsewhere (in particular, I don't think they're used in the pre/post parts and "shape" is defined independently), so it would be possible to make defining the s* dependent on what loop method is being used.
function outerFill(matched, order, proc, body) {
  var dimension = order.length
    , nargs = proc.arrayArgs.length
    , blockSize = proc.blockSize
    , has_index = proc.indexArgs.length > 0
    , code = []
  for(var i=0; i<nargs; ++i) {
    code.push(["var offset",i,"=p",i].join(""))
  }
  //Generate loops for unmatched dimensions
  // The order in which these dimensions are traversed is fairly arbitrary (from small stride to large stride, for the first argument)
  // TODO: It would be nice if the order in which these loops are placed would also be somehow "optimal" (at the very least we should check that it really doesn't hurt us if they're not).
  for(var i=matched; i<dimension; ++i) {
    code.push(["for(var j"+i+"=SS[", order[i], "]|0;j", i, ">0;){"].join("")) // Iterate back to front
    code.push(["if(j",i,"<",blockSize,"){"].join("")) // Either decrease j by blockSize (s = blockSize), or set it to zero (after setting s = j).
    code.push(["s",order[i],"=j",i].join(""))
    code.push(["j",i,"=0"].join(""))
    code.push(["}else{s",order[i],"=",blockSize].join(""))
    code.push(["j",i,"-=",blockSize,"}"].join(""))
    if(has_index) {
      code.push(["index[",order[i],"]=j",i].join(""))
    }
  }
  for(var i=0; i<nargs; ++i) {
    var indexStr = ["offset"+i]
    for(var j=matched; j<dimension; ++j) {
      indexStr.push(["j",j,"*t",i,"p",order[j]].join(""))
    }
    code.push(["p",i,"=(",indexStr.join("+"),")"].join(""))
  }
  code.push(innerFill(order, proc, body))
  for(var i=matched; i<dimension; ++i) {
    code.push("}")
  }
  return code.join("\n")
}

//Count the number of compatible inner orders
// This is the length of the longest common prefix of the arrays in orders.
// Each array in orders lists the dimensions of the correspond ndarray in order of increasing stride.
// This is thus the maximum number of dimensions that can be efficiently traversed by simple nested loops for all arrays.
function countMatches(orders) {
  var matched = 0, dimension = orders[0].length
  while(matched < dimension) {
    for(var j=1; j<orders.length; ++j) {
      if(orders[j][matched] !== orders[0][matched]) {
        return matched
      }
    }
    ++matched
  }
  return matched
}

//Processes a block according to the given data types
// Replaces variable names by different ones, either "local" ones (that are then ferried in and out of the given array) or ones matching the arguments that the function performing the ultimate loop will accept.
function processBlock(block, proc, dtypes) {
  var code = block.body
  var pre = []
  var post = []
  for(var i=0; i<block.args.length; ++i) {
    var carg = block.args[i]
    if(carg.count <= 0) {
      continue
    }
    var re = new RegExp(carg.name, "g")
    var ptrStr = ""
    var arrNum = proc.arrayArgs.indexOf(i)
    switch(proc.argTypes[i]) {
      case "offset":
        var offArgIndex = proc.offsetArgIndex.indexOf(i)
        var offArg = proc.offsetArgs[offArgIndex]
        arrNum = offArg.array
        ptrStr = "+q" + offArgIndex // Adds offset to the "pointer" in the array
      case "array":
        ptrStr = "p" + arrNum + ptrStr
        var localStr = "l" + i
        var arrStr = "a" + arrNum
        if (proc.arrayBlockIndices[arrNum] === 0) { // Argument to body is just a single value from this array
          if(carg.count === 1) { // Argument/array used only once(?)
            if(dtypes[arrNum] === "generic") {
              if(carg.lvalue) {
                pre.push(["var ", localStr, "=", arrStr, ".get(", ptrStr, ")"].join("")) // Is this necessary if the argument is ONLY used as an lvalue? (keep in mind that we can have a += something, so we would actually need to check carg.rvalue)
                code = code.replace(re, localStr)
                post.push([arrStr, ".set(", ptrStr, ",", localStr,")"].join(""))
              } else {
                code = code.replace(re, [arrStr, ".get(", ptrStr, ")"].join(""))
              }
            } else {
              code = code.replace(re, [arrStr, "[", ptrStr, "]"].join(""))
            }
          } else if(dtypes[arrNum] === "generic") {
            pre.push(["var ", localStr, "=", arrStr, ".get(", ptrStr, ")"].join("")) // TODO: Could we optimize by checking for carg.rvalue?
            code = code.replace(re, localStr)
            if(carg.lvalue) {
              post.push([arrStr, ".set(", ptrStr, ",", localStr,")"].join(""))
            }
          } else {
            pre.push(["var ", localStr, "=", arrStr, "[", ptrStr, "]"].join("")) // TODO: Could we optimize by checking for carg.rvalue?
            code = code.replace(re, localStr)
            if(carg.lvalue) {
              post.push([arrStr, "[", ptrStr, "]=", localStr].join(""))
            }
          }
        } else { // Argument to body is a "block"
          var reStrArr = [carg.name], ptrStrArr = [ptrStr]
          for(var j=0; j<Math.abs(proc.arrayBlockIndices[arrNum]); j++) {
            reStrArr.push("\\s*\\[([^\\]]+)\\]")
            ptrStrArr.push("$" + (j+1) + "*t" + arrNum + "b" + j) // Matched index times stride
          }
          re = new RegExp(reStrArr.join(""), "g")
          ptrStr = ptrStrArr.join("+")
          if(dtypes[arrNum] === "generic") {
            /*if(carg.lvalue) {
              pre.push(["var ", localStr, "=", arrStr, ".get(", ptrStr, ")"].join("")) // Is this necessary if the argument is ONLY used as an lvalue? (keep in mind that we can have a += something, so we would actually need to check carg.rvalue)
              code = code.replace(re, localStr)
              post.push([arrStr, ".set(", ptrStr, ",", localStr,")"].join(""))
            } else {
              code = code.replace(re, [arrStr, ".get(", ptrStr, ")"].join(""))
            }*/
            throw new Error("cwise: Generic arrays not supported in combination with blocks!")
          } else {
            // This does not produce any local variables, even if variables are used multiple times. It would be possible to do so, but it would complicate things quite a bit.
            code = code.replace(re, [arrStr, "[", ptrStr, "]"].join(""))
          }
        }
      break
      case "scalar":
        code = code.replace(re, "Y" + proc.scalarArgs.indexOf(i))
      break
      case "index":
        code = code.replace(re, "index")
      break
      case "shape":
        code = code.replace(re, "shape")
      break
    }
  }
  return [pre.join("\n"), code, post.join("\n")].join("\n").trim()
}

function typeSummary(dtypes) {
  var summary = new Array(dtypes.length)
  var allEqual = true
  for(var i=0; i<dtypes.length; ++i) {
    var t = dtypes[i]
    var digits = t.match(/\d+/)
    if(!digits) {
      digits = ""
    } else {
      digits = digits[0]
    }
    if(t.charAt(0) === 0) {
      summary[i] = "u" + t.charAt(1) + digits
    } else {
      summary[i] = t.charAt(0) + digits
    }
    if(i > 0) {
      allEqual = allEqual && summary[i] === summary[i-1]
    }
  }
  if(allEqual) {
    return summary[0]
  }
  return summary.join("")
}

//Generates a cwise operator
function generateCWiseOp(proc, typesig) {

  //Compute dimension
  // Arrays get put first in typesig, and there are two entries per array (dtype and order), so this gets the number of dimensions in the first array arg.
  var dimension = (typesig[1].length - Math.abs(proc.arrayBlockIndices[0]))|0
  var orders = new Array(proc.arrayArgs.length)
  var dtypes = new Array(proc.arrayArgs.length)
  for(var i=0; i<proc.arrayArgs.length; ++i) {
    dtypes[i] = typesig[2*i]
    orders[i] = typesig[2*i+1]
  }
  
  //Determine where block and loop indices start and end
  var blockBegin = [], blockEnd = [] // These indices are exposed as blocks
  var loopBegin = [], loopEnd = [] // These indices are iterated over
  var loopOrders = [] // orders restricted to the loop indices
  for(var i=0; i<proc.arrayArgs.length; ++i) {
    if (proc.arrayBlockIndices[i]<0) {
      loopBegin.push(0)
      loopEnd.push(dimension)
      blockBegin.push(dimension)
      blockEnd.push(dimension+proc.arrayBlockIndices[i])
    } else {
      loopBegin.push(proc.arrayBlockIndices[i]) // Non-negative
      loopEnd.push(proc.arrayBlockIndices[i]+dimension)
      blockBegin.push(0)
      blockEnd.push(proc.arrayBlockIndices[i])
    }
    var newOrder = []
    for(var j=0; j<orders[i].length; j++) {
      if (loopBegin[i]<=orders[i][j] && orders[i][j]<loopEnd[i]) {
        newOrder.push(orders[i][j]-loopBegin[i]) // If this is a loop index, put it in newOrder, subtracting loopBegin, to make sure that all loopOrders are using a common set of indices.
      }
    }
    loopOrders.push(newOrder)
  }

  //First create arguments for procedure
  var arglist = ["SS"] // SS is the overall shape over which we iterate
  var code = ["'use strict'"]
  var vars = []
  
  for(var j=0; j<dimension; ++j) {
    vars.push(["s", j, "=SS[", j, "]"].join("")) // The limits for each dimension.
  }
  for(var i=0; i<proc.arrayArgs.length; ++i) {
    arglist.push("a"+i) // Actual data array
    arglist.push("t"+i) // Strides
    arglist.push("p"+i) // Offset in the array at which the data starts (also used for iterating over the data)
    
    for(var j=0; j<dimension; ++j) { // Unpack the strides into vars for looping
      vars.push(["t",i,"p",j,"=t",i,"[",loopBegin[i]+j,"]"].join(""))
    }
    
    for(var j=0; j<Math.abs(proc.arrayBlockIndices[i]); ++j) { // Unpack the strides into vars for block iteration
      vars.push(["t",i,"b",j,"=t",i,"[",blockBegin[i]+j,"]"].join(""))
    }
  }
  for(var i=0; i<proc.scalarArgs.length; ++i) {
    arglist.push("Y" + i)
  }
  if(proc.shapeArgs.length > 0) {
    vars.push("shape=SS.slice(0)") // Makes the shape over which we iterate available to the user defined functions (so you can use width/height for example)
  }
  if(proc.indexArgs.length > 0) {
    // Prepare an array to keep track of the (logical) indices, initialized to dimension zeroes.
    var zeros = new Array(dimension)
    for(var i=0; i<dimension; ++i) {
      zeros[i] = "0"
    }
    vars.push(["index=[", zeros.join(","), "]"].join(""))
  }
  for(var i=0; i<proc.offsetArgs.length; ++i) { // Offset arguments used for stencil operations
    var off_arg = proc.offsetArgs[i]
    var init_string = []
    for(var j=0; j<off_arg.offset.length; ++j) {
      if(off_arg.offset[j] === 0) {
        continue
      } else if(off_arg.offset[j] === 1) {
        init_string.push(["t", off_arg.array, "p", j].join(""))      
      } else {
        init_string.push([off_arg.offset[j], "*t", off_arg.array, "p", j].join(""))
      }
    }
    if(init_string.length === 0) {
      vars.push("q" + i + "=0")
    } else {
      vars.push(["q", i, "=", init_string.join("+")].join(""))
    }
  }

  //Prepare this variables
  var thisVars = uniq([].concat(proc.pre.thisVars)
                      .concat(proc.body.thisVars)
                      .concat(proc.post.thisVars))
  vars = vars.concat(thisVars)
  if (vars.length > 0) {
    code.push("var " + vars.join(","))
  }
  for(var i=0; i<proc.arrayArgs.length; ++i) {
    code.push("p"+i+"|=0")
  }
  
  //Inline prelude
  if(proc.pre.body.length > 3) {
    code.push(processBlock(proc.pre, proc, dtypes))
  }

  //Process body
  var body = processBlock(proc.body, proc, dtypes)
  var matched = countMatches(loopOrders)
  if(matched < dimension) {
    code.push(outerFill(matched, loopOrders[0], proc, body)) // TODO: Rather than passing loopOrders[0], it might be interesting to look at passing an order that represents the majority of the arguments for example.
  } else {
    code.push(innerFill(loopOrders[0], proc, body))
  }

  //Inline epilog
  if(proc.post.body.length > 3) {
    code.push(processBlock(proc.post, proc, dtypes))
  }
  
  if(proc.debug) {
    console.log("-----Generated cwise routine for ", typesig, ":\n" + code.join("\n") + "\n----------")
  }
  
  var loopName = [(proc.funcName||"unnamed"), "_cwise_loop_", orders[0].join("s"),"m",matched,typeSummary(dtypes)].join("")
  var f = new Function(["function ",loopName,"(", arglist.join(","),"){", code.join("\n"),"} return ", loopName].join(""))
  return f()
}
module.exports = generateCWiseOp

},{"uniq":147}],68:[function(require,module,exports){
"use strict"

// The function below is called when constructing a cwise function object, and does the following:
// A function object is constructed which accepts as argument a compilation function and returns another function.
// It is this other function that is eventually returned by createThunk, and this function is the one that actually
// checks whether a certain pattern of arguments has already been used before and compiles new loops as needed.
// The compilation passed to the first function object is used for compiling new functions.
// Once this function object is created, it is called with compile as argument, where the first argument of compile
// is bound to "proc" (essentially containing a preprocessed version of the user arguments to cwise).
// So createThunk roughly works like this:
// function createThunk(proc) {
//   var thunk = function(compileBound) {
//     var CACHED = {}
//     return function(arrays and scalars) {
//       if (dtype and order of arrays in CACHED) {
//         var func = CACHED[dtype and order of arrays]
//       } else {
//         var func = CACHED[dtype and order of arrays] = compileBound(dtype and order of arrays)
//       }
//       return func(arrays and scalars)
//     }
//   }
//   return thunk(compile.bind1(proc))
// }

var compile = require("./compile.js")

function createThunk(proc) {
  var code = ["'use strict'", "var CACHED={}"]
  var vars = []
  var thunkName = proc.funcName + "_cwise_thunk"
  
  //Build thunk
  code.push(["return function ", thunkName, "(", proc.shimArgs.join(","), "){"].join(""))
  var typesig = []
  var string_typesig = []
  var proc_args = [["array",proc.arrayArgs[0],".shape.slice(", // Slice shape so that we only retain the shape over which we iterate (which gets passed to the cwise operator as SS).
                    Math.max(0,proc.arrayBlockIndices[0]),proc.arrayBlockIndices[0]<0?(","+proc.arrayBlockIndices[0]+")"):")"].join("")]
  var shapeLengthConditions = [], shapeConditions = []
  // Process array arguments
  for(var i=0; i<proc.arrayArgs.length; ++i) {
    var j = proc.arrayArgs[i]
    vars.push(["t", j, "=array", j, ".dtype,",
               "r", j, "=array", j, ".order"].join(""))
    typesig.push("t" + j)
    typesig.push("r" + j)
    string_typesig.push("t"+j)
    string_typesig.push("r"+j+".join()")
    proc_args.push("array" + j + ".data")
    proc_args.push("array" + j + ".stride")
    proc_args.push("array" + j + ".offset|0")
    if (i>0) { // Gather conditions to check for shape equality (ignoring block indices)
      shapeLengthConditions.push("array" + proc.arrayArgs[0] + ".shape.length===array" + j + ".shape.length+" + (Math.abs(proc.arrayBlockIndices[0])-Math.abs(proc.arrayBlockIndices[i])))
      shapeConditions.push("array" + proc.arrayArgs[0] + ".shape[shapeIndex+" + Math.max(0,proc.arrayBlockIndices[0]) + "]===array" + j + ".shape[shapeIndex+" + Math.max(0,proc.arrayBlockIndices[i]) + "]")
    }
  }
  // Check for shape equality
  if (proc.arrayArgs.length > 1) {
    code.push("if (!(" + shapeLengthConditions.join(" && ") + ")) throw new Error('cwise: Arrays do not all have the same dimensionality!')")
    code.push("for(var shapeIndex=array" + proc.arrayArgs[0] + ".shape.length-" + Math.abs(proc.arrayBlockIndices[0]) + "; shapeIndex-->0;) {")
    code.push("if (!(" + shapeConditions.join(" && ") + ")) throw new Error('cwise: Arrays do not all have the same shape!')")
    code.push("}")
  }
  // Process scalar arguments
  for(var i=0; i<proc.scalarArgs.length; ++i) {
    proc_args.push("scalar" + proc.scalarArgs[i])
  }
  // Check for cached function (and if not present, generate it)
  vars.push(["type=[", string_typesig.join(","), "].join()"].join(""))
  vars.push("proc=CACHED[type]")
  code.push("var " + vars.join(","))
  
  code.push(["if(!proc){",
             "CACHED[type]=proc=compile([", typesig.join(","), "])}",
             "return proc(", proc_args.join(","), ")}"].join(""))

  if(proc.debug) {
    console.log("-----Generated thunk:\n" + code.join("\n") + "\n----------")
  }
  
  //Compile thunk
  var thunk = new Function("compile", code.join("\n"))
  return thunk(compile.bind(undefined, proc))
}

module.exports = createThunk

},{"./compile.js":67}],69:[function(require,module,exports){
"use strict"

function iota(n) {
  var result = new Array(n)
  for(var i=0; i<n; ++i) {
    result[i] = i
  }
  return result
}

module.exports = iota
},{}],70:[function(require,module,exports){
/*!
 * Determine if an object is a Buffer
 *
 * @author   Feross Aboukhadijeh <https://feross.org>
 * @license  MIT
 */

// The _isBuffer check is for Safari 5-7 support, because it's missing
// Object.prototype.constructor. Remove this eventually
module.exports = function (obj) {
  return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)
}

function isBuffer (obj) {
  return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
}

// For Node v0.10 support. Remove this eventually.
function isSlowBuffer (obj) {
  return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))
}

},{}],71:[function(require,module,exports){
var encode = require('./lib/encoder'),
    decode = require('./lib/decoder');

module.exports = {
  encode: encode,
  decode: decode
};

},{"./lib/decoder":72,"./lib/encoder":73}],72:[function(require,module,exports){
(function (Buffer){(function (){
/* -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
   Copyright 2011 notmasteryet

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

// - The JPEG specification can be found in the ITU CCITT Recommendation T.81
//   (www.w3.org/Graphics/JPEG/itu-t81.pdf)
// - The JFIF specification can be found in the JPEG File Interchange Format
//   (www.w3.org/Graphics/JPEG/jfif3.pdf)
// - The Adobe Application-Specific JPEG markers in the Supporting the DCT Filters
//   in PostScript Level 2, Technical Note #5116
//   (partners.adobe.com/public/developer/en/ps/sdk/5116.DCT_Filter.pdf)

var JpegImage = (function jpegImage() {
  "use strict";
  var dctZigZag = new Int32Array([
     0,
     1,  8,
    16,  9,  2,
     3, 10, 17, 24,
    32, 25, 18, 11, 4,
     5, 12, 19, 26, 33, 40,
    48, 41, 34, 27, 20, 13,  6,
     7, 14, 21, 28, 35, 42, 49, 56,
    57, 50, 43, 36, 29, 22, 15,
    23, 30, 37, 44, 51, 58,
    59, 52, 45, 38, 31,
    39, 46, 53, 60,
    61, 54, 47,
    55, 62,
    63
  ]);

  var dctCos1  =  4017   // cos(pi/16)
  var dctSin1  =   799   // sin(pi/16)
  var dctCos3  =  3406   // cos(3*pi/16)
  var dctSin3  =  2276   // sin(3*pi/16)
  var dctCos6  =  1567   // cos(6*pi/16)
  var dctSin6  =  3784   // sin(6*pi/16)
  var dctSqrt2 =  5793   // sqrt(2)
  var dctSqrt1d2 = 2896  // sqrt(2) / 2

  function constructor() {
  }

  function buildHuffmanTable(codeLengths, values) {
    var k = 0, code = [], i, j, length = 16;
    while (length > 0 && !codeLengths[length - 1])
      length--;
    code.push({children: [], index: 0});
    var p = code[0], q;
    for (i = 0; i < length; i++) {
      for (j = 0; j < codeLengths[i]; j++) {
        p = code.pop();
        p.children[p.index] = values[k];
        while (p.index > 0) {
          if (code.length === 0)
            throw new Error('Could not recreate Huffman Table');
          p = code.pop();
        }
        p.index++;
        code.push(p);
        while (code.length <= i) {
          code.push(q = {children: [], index: 0});
          p.children[p.index] = q.children;
          p = q;
        }
        k++;
      }
      if (i + 1 < length) {
        // p here points to last code
        code.push(q = {children: [], index: 0});
        p.children[p.index] = q.children;
        p = q;
      }
    }
    return code[0].children;
  }

  function decodeScan(data, offset,
                      frame, components, resetInterval,
                      spectralStart, spectralEnd,
                      successivePrev, successive, opts) {
    var precision = frame.precision;
    var samplesPerLine = frame.samplesPerLine;
    var scanLines = frame.scanLines;
    var mcusPerLine = frame.mcusPerLine;
    var progressive = frame.progressive;
    var maxH = frame.maxH, maxV = frame.maxV;

    var startOffset = offset, bitsData = 0, bitsCount = 0;
    function readBit() {
      if (bitsCount > 0) {
        bitsCount--;
        return (bitsData >> bitsCount) & 1;
      }
      bitsData = data[offset++];
      if (bitsData == 0xFF) {
        var nextByte = data[offset++];
        if (nextByte) {
          throw new Error("unexpected marker: " + ((bitsData << 8) | nextByte).toString(16));
        }
        // unstuff 0
      }
      bitsCount = 7;
      return bitsData >>> 7;
    }
    function decodeHuffman(tree) {
      var node = tree, bit;
      while ((bit = readBit()) !== null) {
        node = node[bit];
        if (typeof node === 'number')
          return node;
        if (typeof node !== 'object')
          throw new Error("invalid huffman sequence");
      }
      return null;
    }
    function receive(length) {
      var n = 0;
      while (length > 0) {
        var bit = readBit();
        if (bit === null) return;
        n = (n << 1) | bit;
        length--;
      }
      return n;
    }
    function receiveAndExtend(length) {
      var n = receive(length);
      if (n >= 1 << (length - 1))
        return n;
      return n + (-1 << length) + 1;
    }
    function decodeBaseline(component, zz) {
      var t = decodeHuffman(component.huffmanTableDC);
      var diff = t === 0 ? 0 : receiveAndExtend(t);
      zz[0]= (component.pred += diff);
      var k = 1;
      while (k < 64) {
        var rs = decodeHuffman(component.huffmanTableAC);
        var s = rs & 15, r = rs >> 4;
        if (s === 0) {
          if (r < 15)
            break;
          k += 16;
          continue;
        }
        k += r;
        var z = dctZigZag[k];
        zz[z] = receiveAndExtend(s);
        k++;
      }
    }
    function decodeDCFirst(component, zz) {
      var t = decodeHuffman(component.huffmanTableDC);
      var diff = t === 0 ? 0 : (receiveAndExtend(t) << successive);
      zz[0] = (component.pred += diff);
    }
    function decodeDCSuccessive(component, zz) {
      zz[0] |= readBit() << successive;
    }
    var eobrun = 0;
    function decodeACFirst(component, zz) {
      if (eobrun > 0) {
        eobrun--;
        return;
      }
      var k = spectralStart, e = spectralEnd;
      while (k <= e) {
        var rs = decodeHuffman(component.huffmanTableAC);
        var s = rs & 15, r = rs >> 4;
        if (s === 0) {
          if (r < 15) {
            eobrun = receive(r) + (1 << r) - 1;
            break;
          }
          k += 16;
          continue;
        }
        k += r;
        var z = dctZigZag[k];
        zz[z] = receiveAndExtend(s) * (1 << successive);
        k++;
      }
    }
    var successiveACState = 0, successiveACNextValue;
    function decodeACSuccessive(component, zz) {
      var k = spectralStart, e = spectralEnd, r = 0;
      while (k <= e) {
        var z = dctZigZag[k];
        var direction = zz[z] < 0 ? -1 : 1;
        switch (successiveACState) {
        case 0: // initial state
          var rs = decodeHuffman(component.huffmanTableAC);
          var s = rs & 15, r = rs >> 4;
          if (s === 0) {
            if (r < 15) {
              eobrun = receive(r) + (1 << r);
              successiveACState = 4;
            } else {
              r = 16;
              successiveACState = 1;
            }
          } else {
            if (s !== 1)
              throw new Error("invalid ACn encoding");
            successiveACNextValue = receiveAndExtend(s);
            successiveACState = r ? 2 : 3;
          }
          continue;
        case 1: // skipping r zero items
        case 2:
          if (zz[z])
            zz[z] += (readBit() << successive) * direction;
          else {
            r--;
            if (r === 0)
              successiveACState = successiveACState == 2 ? 3 : 0;
          }
          break;
        case 3: // set value for a zero item
          if (zz[z])
            zz[z] += (readBit() << successive) * direction;
          else {
            zz[z] = successiveACNextValue << successive;
            successiveACState = 0;
          }
          break;
        case 4: // eob
          if (zz[z])
            zz[z] += (readBit() << successive) * direction;
          break;
        }
        k++;
      }
      if (successiveACState === 4) {
        eobrun--;
        if (eobrun === 0)
          successiveACState = 0;
      }
    }
    function decodeMcu(component, decode, mcu, row, col) {
      var mcuRow = (mcu / mcusPerLine) | 0;
      var mcuCol = mcu % mcusPerLine;
      var blockRow = mcuRow * component.v + row;
      var blockCol = mcuCol * component.h + col;
      // If the block is missing and we're in tolerant mode, just skip it.
      if (component.blocks[blockRow] === undefined && opts.tolerantDecoding)
        return;
      decode(component, component.blocks[blockRow][blockCol]);
    }
    function decodeBlock(component, decode, mcu) {
      var blockRow = (mcu / component.blocksPerLine) | 0;
      var blockCol = mcu % component.blocksPerLine;
      // If the block is missing and we're in tolerant mode, just skip it.
      if (component.blocks[blockRow] === undefined && opts.tolerantDecoding)
        return;
      decode(component, component.blocks[blockRow][blockCol]);
    }

    var componentsLength = components.length;
    var component, i, j, k, n;
    var decodeFn;
    if (progressive) {
      if (spectralStart === 0)
        decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;
      else
        decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
    } else {
      decodeFn = decodeBaseline;
    }

    var mcu = 0, marker;
    var mcuExpected;
    if (componentsLength == 1) {
      mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn;
    } else {
      mcuExpected = mcusPerLine * frame.mcusPerColumn;
    }
    if (!resetInterval) resetInterval = mcuExpected;

    var h, v;
    while (mcu < mcuExpected) {
      // reset interval stuff
      for (i = 0; i < componentsLength; i++)
        components[i].pred = 0;
      eobrun = 0;

      if (componentsLength == 1) {
        component = components[0];
        for (n = 0; n < resetInterval; n++) {
          decodeBlock(component, decodeFn, mcu);
          mcu++;
        }
      } else {
        for (n = 0; n < resetInterval; n++) {
          for (i = 0; i < componentsLength; i++) {
            component = components[i];
            h = component.h;
            v = component.v;
            for (j = 0; j < v; j++) {
              for (k = 0; k < h; k++) {
                decodeMcu(component, decodeFn, mcu, j, k);
              }
            }
          }
          mcu++;

          // If we've reached our expected MCU's, stop decoding
          if (mcu === mcuExpected) break;
        }
      }

      if (mcu === mcuExpected) {
        // Skip trailing bytes at the end of the scan - until we reach the next marker
        do {
          if (data[offset] === 0xFF) {
            if (data[offset + 1] !== 0x00) {
              break;
            }
          }
          offset += 1;
        } while (offset < data.length - 2);
      }

      // find marker
      bitsCount = 0;
      marker = (data[offset] << 8) | data[offset + 1];
      if (marker < 0xFF00) {
        throw new Error("marker was not found");
      }

      if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx
        offset += 2;
      }
      else
        break;
    }

    return offset - startOffset;
  }

  function buildComponentData(frame, component) {
    var lines = [];
    var blocksPerLine = component.blocksPerLine;
    var blocksPerColumn = component.blocksPerColumn;
    var samplesPerLine = blocksPerLine << 3;
    // Only 1 used per invocation of this function and garbage collected after invocation, so no need to account for its memory footprint.
    var R = new Int32Array(64), r = new Uint8Array(64);

    // A port of poppler's IDCT method which in turn is taken from:
    //   Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
    //   "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
    //   IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
    //   988-991.
    function quantizeAndInverse(zz, dataOut, dataIn) {
      var qt = component.quantizationTable;
      var v0, v1, v2, v3, v4, v5, v6, v7, t;
      var p = dataIn;
      var i;

      // dequant
      for (i = 0; i < 64; i++)
        p[i] = zz[i] * qt[i];

      // inverse DCT on rows
      for (i = 0; i < 8; ++i) {
        var row = 8 * i;

        // check for all-zero AC coefficients
        if (p[1 + row] == 0 && p[2 + row] == 0 && p[3 + row] == 0 &&
            p[4 + row] == 0 && p[5 + row] == 0 && p[6 + row] == 0 &&
            p[7 + row] == 0) {
          t = (dctSqrt2 * p[0 + row] + 512) >> 10;
          p[0 + row] = t;
          p[1 + row] = t;
          p[2 + row] = t;
          p[3 + row] = t;
          p[4 + row] = t;
          p[5 + row] = t;
          p[6 + row] = t;
          p[7 + row] = t;
          continue;
        }

        // stage 4
        v0 = (dctSqrt2 * p[0 + row] + 128) >> 8;
        v1 = (dctSqrt2 * p[4 + row] + 128) >> 8;
        v2 = p[2 + row];
        v3 = p[6 + row];
        v4 = (dctSqrt1d2 * (p[1 + row] - p[7 + row]) + 128) >> 8;
        v7 = (dctSqrt1d2 * (p[1 + row] + p[7 + row]) + 128) >> 8;
        v5 = p[3 + row] << 4;
        v6 = p[5 + row] << 4;

        // stage 3
        t = (v0 - v1+ 1) >> 1;
        v0 = (v0 + v1 + 1) >> 1;
        v1 = t;
        t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8;
        v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8;
        v3 = t;
        t = (v4 - v6 + 1) >> 1;
        v4 = (v4 + v6 + 1) >> 1;
        v6 = t;
        t = (v7 + v5 + 1) >> 1;
        v5 = (v7 - v5 + 1) >> 1;
        v7 = t;

        // stage 2
        t = (v0 - v3 + 1) >> 1;
        v0 = (v0 + v3 + 1) >> 1;
        v3 = t;
        t = (v1 - v2 + 1) >> 1;
        v1 = (v1 + v2 + 1) >> 1;
        v2 = t;
        t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
        v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
        v7 = t;
        t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
        v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
        v6 = t;

        // stage 1
        p[0 + row] = v0 + v7;
        p[7 + row] = v0 - v7;
        p[1 + row] = v1 + v6;
        p[6 + row] = v1 - v6;
        p[2 + row] = v2 + v5;
        p[5 + row] = v2 - v5;
        p[3 + row] = v3 + v4;
        p[4 + row] = v3 - v4;
      }

      // inverse DCT on columns
      for (i = 0; i < 8; ++i) {
        var col = i;

        // check for all-zero AC coefficients
        if (p[1*8 + col] == 0 && p[2*8 + col] == 0 && p[3*8 + col] == 0 &&
            p[4*8 + col] == 0 && p[5*8 + col] == 0 && p[6*8 + col] == 0 &&
            p[7*8 + col] == 0) {
          t = (dctSqrt2 * dataIn[i+0] + 8192) >> 14;
          p[0*8 + col] = t;
          p[1*8 + col] = t;
          p[2*8 + col] = t;
          p[3*8 + col] = t;
          p[4*8 + col] = t;
          p[5*8 + col] = t;
          p[6*8 + col] = t;
          p[7*8 + col] = t;
          continue;
        }

        // stage 4
        v0 = (dctSqrt2 * p[0*8 + col] + 2048) >> 12;
        v1 = (dctSqrt2 * p[4*8 + col] + 2048) >> 12;
        v2 = p[2*8 + col];
        v3 = p[6*8 + col];
        v4 = (dctSqrt1d2 * (p[1*8 + col] - p[7*8 + col]) + 2048) >> 12;
        v7 = (dctSqrt1d2 * (p[1*8 + col] + p[7*8 + col]) + 2048) >> 12;
        v5 = p[3*8 + col];
        v6 = p[5*8 + col];

        // stage 3
        t = (v0 - v1 + 1) >> 1;
        v0 = (v0 + v1 + 1) >> 1;
        v1 = t;
        t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12;
        v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12;
        v3 = t;
        t = (v4 - v6 + 1) >> 1;
        v4 = (v4 + v6 + 1) >> 1;
        v6 = t;
        t = (v7 + v5 + 1) >> 1;
        v5 = (v7 - v5 + 1) >> 1;
        v7 = t;

        // stage 2
        t = (v0 - v3 + 1) >> 1;
        v0 = (v0 + v3 + 1) >> 1;
        v3 = t;
        t = (v1 - v2 + 1) >> 1;
        v1 = (v1 + v2 + 1) >> 1;
        v2 = t;
        t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
        v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
        v7 = t;
        t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
        v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
        v6 = t;

        // stage 1
        p[0*8 + col] = v0 + v7;
        p[7*8 + col] = v0 - v7;
        p[1*8 + col] = v1 + v6;
        p[6*8 + col] = v1 - v6;
        p[2*8 + col] = v2 + v5;
        p[5*8 + col] = v2 - v5;
        p[3*8 + col] = v3 + v4;
        p[4*8 + col] = v3 - v4;
      }

      // convert to 8-bit integers
      for (i = 0; i < 64; ++i) {
        var sample = 128 + ((p[i] + 8) >> 4);
        dataOut[i] = sample < 0 ? 0 : sample > 0xFF ? 0xFF : sample;
      }
    }

    requestMemoryAllocation(samplesPerLine * blocksPerColumn * 8);

    var i, j;
    for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
      var scanLine = blockRow << 3;
      for (i = 0; i < 8; i++)
        lines.push(new Uint8Array(samplesPerLine));
      for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) {
        quantizeAndInverse(component.blocks[blockRow][blockCol], r, R);

        var offset = 0, sample = blockCol << 3;
        for (j = 0; j < 8; j++) {
          var line = lines[scanLine + j];
          for (i = 0; i < 8; i++)
            line[sample + i] = r[offset++];
        }
      }
    }
    return lines;
  }

  function clampTo8bit(a) {
    return a < 0 ? 0 : a > 255 ? 255 : a;
  }

  constructor.prototype = {
    load: function load(path) {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", path, true);
      xhr.responseType = "arraybuffer";
      xhr.onload = (function() {
        // TODO catch parse error
        var data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer);
        this.parse(data);
        if (this.onload)
          this.onload();
      }).bind(this);
      xhr.send(null);
    },
    parse: function parse(data) {
      var maxResolutionInPixels = this.opts.maxResolutionInMP * 1000 * 1000;
      var offset = 0, length = data.length;
      function readUint16() {
        var value = (data[offset] << 8) | data[offset + 1];
        offset += 2;
        return value;
      }
      function readDataBlock() {
        var length = readUint16();
        var array = data.subarray(offset, offset + length - 2);
        offset += array.length;
        return array;
      }
      function prepareComponents(frame) {
        // According to the JPEG standard, the sampling factor must be between 1 and 4
        // See https://github.com/libjpeg-turbo/libjpeg-turbo/blob/9abeff46d87bd201a952e276f3e4339556a403a3/libjpeg.txt#L1138-L1146
        var maxH = 1, maxV = 1;
        var component, componentId;
        for (componentId in frame.components) {
          if (frame.components.hasOwnProperty(componentId)) {
            component = frame.components[componentId];
            if (maxH < component.h) maxH = component.h;
            if (maxV < component.v) maxV = component.v;
          }
        }
        var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / maxH);
        var mcusPerColumn = Math.ceil(frame.scanLines / 8 / maxV);
        for (componentId in frame.components) {
          if (frame.components.hasOwnProperty(componentId)) {
            component = frame.components[componentId];
            var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / maxH);
            var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines  / 8) * component.v / maxV);
            var blocksPerLineForMcu = mcusPerLine * component.h;
            var blocksPerColumnForMcu = mcusPerColumn * component.v;
            var blocksToAllocate = blocksPerColumnForMcu * blocksPerLineForMcu;
            var blocks = [];

            // Each block is a Int32Array of length 64 (4 x 64 = 256 bytes)
            requestMemoryAllocation(blocksToAllocate * 256);

            for (var i = 0; i < blocksPerColumnForMcu; i++) {
              var row = [];
              for (var j = 0; j < blocksPerLineForMcu; j++)
                row.push(new Int32Array(64));
              blocks.push(row);
            }
            component.blocksPerLine = blocksPerLine;
            component.blocksPerColumn = blocksPerColumn;
            component.blocks = blocks;
          }
        }
        frame.maxH = maxH;
        frame.maxV = maxV;
        frame.mcusPerLine = mcusPerLine;
        frame.mcusPerColumn = mcusPerColumn;
      }
      var jfif = null;
      var adobe = null;
      var pixels = null;
      var frame, resetInterval;
      var quantizationTables = [], frames = [];
      var huffmanTablesAC = [], huffmanTablesDC = [];
      var fileMarker = readUint16();
      var malformedDataOffset = -1;
      this.comments = [];
      if (fileMarker != 0xFFD8) { // SOI (Start of Image)
        throw new Error("SOI not found");
      }

      fileMarker = readUint16();
      while (fileMarker != 0xFFD9) { // EOI (End of image)
        var i, j, l;
        switch(fileMarker) {
          case 0xFF00: break;
          case 0xFFE0: // APP0 (Application Specific)
          case 0xFFE1: // APP1
          case 0xFFE2: // APP2
          case 0xFFE3: // APP3
          case 0xFFE4: // APP4
          case 0xFFE5: // APP5
          case 0xFFE6: // APP6
          case 0xFFE7: // APP7
          case 0xFFE8: // APP8
          case 0xFFE9: // APP9
          case 0xFFEA: // APP10
          case 0xFFEB: // APP11
          case 0xFFEC: // APP12
          case 0xFFED: // APP13
          case 0xFFEE: // APP14
          case 0xFFEF: // APP15
          case 0xFFFE: // COM (Comment)
            var appData = readDataBlock();

            if (fileMarker === 0xFFFE) {
              var comment = String.fromCharCode.apply(null, appData);
              this.comments.push(comment);
            }

            if (fileMarker === 0xFFE0) {
              if (appData[0] === 0x4A && appData[1] === 0x46 && appData[2] === 0x49 &&
                appData[3] === 0x46 && appData[4] === 0) { // 'JFIF\x00'
                jfif = {
                  version: { major: appData[5], minor: appData[6] },
                  densityUnits: appData[7],
                  xDensity: (appData[8] << 8) | appData[9],
                  yDensity: (appData[10] << 8) | appData[11],
                  thumbWidth: appData[12],
                  thumbHeight: appData[13],
                  thumbData: appData.subarray(14, 14 + 3 * appData[12] * appData[13])
                };
              }
            }
            // TODO APP1 - Exif
            if (fileMarker === 0xFFE1) {
              if (appData[0] === 0x45 &&
                appData[1] === 0x78 &&
                appData[2] === 0x69 &&
                appData[3] === 0x66 &&
                appData[4] === 0) { // 'EXIF\x00'
                this.exifBuffer = appData.subarray(5, appData.length);
              }
            }

            if (fileMarker === 0xFFEE) {
              if (appData[0] === 0x41 && appData[1] === 0x64 && appData[2] === 0x6F &&
                appData[3] === 0x62 && appData[4] === 0x65 && appData[5] === 0) { // 'Adobe\x00'
                adobe = {
                  version: appData[6],
                  flags0: (appData[7] << 8) | appData[8],
                  flags1: (appData[9] << 8) | appData[10],
                  transformCode: appData[11]
                };
              }
            }
            break;

          case 0xFFDB: // DQT (Define Quantization Tables)
            var quantizationTablesLength = readUint16();
            var quantizationTablesEnd = quantizationTablesLength + offset - 2;
            while (offset < quantizationTablesEnd) {
              var quantizationTableSpec = data[offset++];
              requestMemoryAllocation(64 * 4);
              var tableData = new Int32Array(64);
              if ((quantizationTableSpec >> 4) === 0) { // 8 bit values
                for (j = 0; j < 64; j++) {
                  var z = dctZigZag[j];
                  tableData[z] = data[offset++];
                }
              } else if ((quantizationTableSpec >> 4) === 1) { //16 bit
                for (j = 0; j < 64; j++) {
                  var z = dctZigZag[j];
                  tableData[z] = readUint16();
                }
              } else
                throw new Error("DQT: invalid table spec");
              quantizationTables[quantizationTableSpec & 15] = tableData;
            }
            break;

          case 0xFFC0: // SOF0 (Start of Frame, Baseline DCT)
          case 0xFFC1: // SOF1 (Start of Frame, Extended DCT)
          case 0xFFC2: // SOF2 (Start of Frame, Progressive DCT)
            readUint16(); // skip data length
            frame = {};
            frame.extended = (fileMarker === 0xFFC1);
            frame.progressive = (fileMarker === 0xFFC2);
            frame.precision = data[offset++];
            frame.scanLines = readUint16();
            frame.samplesPerLine = readUint16();
            frame.components = {};
            frame.componentsOrder = [];

            var pixelsInFrame = frame.scanLines * frame.samplesPerLine;
            if (pixelsInFrame > maxResolutionInPixels) {
              var exceededAmount = Math.ceil((pixelsInFrame - maxResolutionInPixels) / 1e6);
              throw new Error(`maxResolutionInMP limit exceeded by ${exceededAmount}MP`);
            }

            var componentsCount = data[offset++], componentId;
            var maxH = 0, maxV = 0;
            for (i = 0; i < componentsCount; i++) {
              componentId = data[offset];
              var h = data[offset + 1] >> 4;
              var v = data[offset + 1] & 15;
              var qId = data[offset + 2];

              if ( h <= 0 || v <= 0 ) {
                throw new Error('Invalid sampling factor, expected values above 0');
              }

              frame.componentsOrder.push(componentId);
              frame.components[componentId] = {
                h: h,
                v: v,
                quantizationIdx: qId
              };
              offset += 3;
            }
            prepareComponents(frame);
            frames.push(frame);
            break;

          case 0xFFC4: // DHT (Define Huffman Tables)
            var huffmanLength = readUint16();
            for (i = 2; i < huffmanLength;) {
              var huffmanTableSpec = data[offset++];
              var codeLengths = new Uint8Array(16);
              var codeLengthSum = 0;
              for (j = 0; j < 16; j++, offset++) {
                codeLengthSum += (codeLengths[j] = data[offset]);
              }
              requestMemoryAllocation(16 + codeLengthSum);
              var huffmanValues = new Uint8Array(codeLengthSum);
              for (j = 0; j < codeLengthSum; j++, offset++)
                huffmanValues[j] = data[offset];
              i += 17 + codeLengthSum;

              ((huffmanTableSpec >> 4) === 0 ?
                huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] =
                buildHuffmanTable(codeLengths, huffmanValues);
            }
            break;

          case 0xFFDD: // DRI (Define Restart Interval)
            readUint16(); // skip data length
            resetInterval = readUint16();
            break;

          case 0xFFDC: // Number of Lines marker
            readUint16() // skip data length
            readUint16() // Ignore this data since it represents the image height
            break;
            
          case 0xFFDA: // SOS (Start of Scan)
            var scanLength = readUint16();
            var selectorsCount = data[offset++];
            var components = [], component;
            for (i = 0; i < selectorsCount; i++) {
              component = frame.components[data[offset++]];
              var tableSpec = data[offset++];
              component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
              component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
              components.push(component);
            }
            var spectralStart = data[offset++];
            var spectralEnd = data[offset++];
            var successiveApproximation = data[offset++];
            var processed = decodeScan(data, offset,
              frame, components, resetInterval,
              spectralStart, spectralEnd,
              successiveApproximation >> 4, successiveApproximation & 15, this.opts);
            offset += processed;
            break;

          case 0xFFFF: // Fill bytes
            if (data[offset] !== 0xFF) { // Avoid skipping a valid marker.
              offset--;
            }
            break;
          default:
            if (data[offset - 3] == 0xFF &&
                data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) {
              // could be incorrect encoding -- last 0xFF byte of the previous
              // block was eaten by the encoder
              offset -= 3;
              break;
            }
            else if (fileMarker === 0xE0 || fileMarker == 0xE1) {
              // Recover from malformed APP1 markers popular in some phone models.
              // See https://github.com/eugeneware/jpeg-js/issues/82
              if (malformedDataOffset !== -1) {
                throw new Error(`first unknown JPEG marker at offset ${malformedDataOffset.toString(16)}, second unknown JPEG marker ${fileMarker.toString(16)} at offset ${(offset - 1).toString(16)}`);
              }
              malformedDataOffset = offset - 1;
              const nextOffset = readUint16();
              if (data[offset + nextOffset - 2] === 0xFF) {
                offset += nextOffset - 2;
                break;
              }
            }
            throw new Error("unknown JPEG marker " + fileMarker.toString(16));
        }
        fileMarker = readUint16();
      }
      if (frames.length != 1)
        throw new Error("only single frame JPEGs supported");

      // set each frame's components quantization table
      for (var i = 0; i < frames.length; i++) {
        var cp = frames[i].components;
        for (var j in cp) {
          cp[j].quantizationTable = quantizationTables[cp[j].quantizationIdx];
          delete cp[j].quantizationIdx;
        }
      }

      this.width = frame.samplesPerLine;
      this.height = frame.scanLines;
      this.jfif = jfif;
      this.adobe = adobe;
      this.components = [];
      for (var i = 0; i < frame.componentsOrder.length; i++) {
        var component = frame.components[frame.componentsOrder[i]];
        this.components.push({
          lines: buildComponentData(frame, component),
          scaleX: component.h / frame.maxH,
          scaleY: component.v / frame.maxV
        });
      }
    },
    getData: function getData(width, height) {
      var scaleX = this.width / width, scaleY = this.height / height;

      var component1, component2, component3, component4;
      var component1Line, component2Line, component3Line, component4Line;
      var x, y;
      var offset = 0;
      var Y, Cb, Cr, K, C, M, Ye, R, G, B;
      var colorTransform;
      var dataLength = width * height * this.components.length;
      requestMemoryAllocation(dataLength);
      var data = new Uint8Array(dataLength);
      switch (this.components.length) {
        case 1:
          component1 = this.components[0];
          for (y = 0; y < height; y++) {
            component1Line = component1.lines[0 | (y * component1.scaleY * scaleY)];
            for (x = 0; x < width; x++) {
              Y = component1Line[0 | (x * component1.scaleX * scaleX)];

              data[offset++] = Y;
            }
          }
          break;
        case 2:
          // PDF might compress two component data in custom colorspace
          component1 = this.components[0];
          component2 = this.components[1];
          for (y = 0; y < height; y++) {
            component1Line = component1.lines[0 | (y * component1.scaleY * scaleY)];
            component2Line = component2.lines[0 | (y * component2.scaleY * scaleY)];
            for (x = 0; x < width; x++) {
              Y = component1Line[0 | (x * component1.scaleX * scaleX)];
              data[offset++] = Y;
              Y = component2Line[0 | (x * component2.scaleX * scaleX)];
              data[offset++] = Y;
            }
          }
          break;
        case 3:
          // The default transform for three components is true
          colorTransform = true;
          // The adobe transform marker overrides any previous setting
          if (this.adobe && this.adobe.transformCode)
            colorTransform = true;
          else if (typeof this.opts.colorTransform !== 'undefined')
            colorTransform = !!this.opts.colorTransform;

          component1 = this.components[0];
          component2 = this.components[1];
          component3 = this.components[2];
          for (y = 0; y < height; y++) {
            component1Line = component1.lines[0 | (y * component1.scaleY * scaleY)];
            component2Line = component2.lines[0 | (y * component2.scaleY * scaleY)];
            component3Line = component3.lines[0 | (y * component3.scaleY * scaleY)];
            for (x = 0; x < width; x++) {
              if (!colorTransform) {
                R = component1Line[0 | (x * component1.scaleX * scaleX)];
                G = component2Line[0 | (x * component2.scaleX * scaleX)];
                B = component3Line[0 | (x * component3.scaleX * scaleX)];
              } else {
                Y = component1Line[0 | (x * component1.scaleX * scaleX)];
                Cb = component2Line[0 | (x * component2.scaleX * scaleX)];
                Cr = component3Line[0 | (x * component3.scaleX * scaleX)];

                R = clampTo8bit(Y + 1.402 * (Cr - 128));
                G = clampTo8bit(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128));
                B = clampTo8bit(Y + 1.772 * (Cb - 128));
              }

              data[offset++] = R;
              data[offset++] = G;
              data[offset++] = B;
            }
          }
          break;
        case 4:
          if (!this.adobe)
            throw new Error('Unsupported color mode (4 components)');
          // The default transform for four components is false
          colorTransform = false;
          // The adobe transform marker overrides any previous setting
          if (this.adobe && this.adobe.transformCode)
            colorTransform = true;
          else if (typeof this.opts.colorTransform !== 'undefined')
            colorTransform = !!this.opts.colorTransform;

          component1 = this.components[0];
          component2 = this.components[1];
          component3 = this.components[2];
          component4 = this.components[3];
          for (y = 0; y < height; y++) {
            component1Line = component1.lines[0 | (y * component1.scaleY * scaleY)];
            component2Line = component2.lines[0 | (y * component2.scaleY * scaleY)];
            component3Line = component3.lines[0 | (y * component3.scaleY * scaleY)];
            component4Line = component4.lines[0 | (y * component4.scaleY * scaleY)];
            for (x = 0; x < width; x++) {
              if (!colorTransform) {
                C = component1Line[0 | (x * component1.scaleX * scaleX)];
                M = component2Line[0 | (x * component2.scaleX * scaleX)];
                Ye = component3Line[0 | (x * component3.scaleX * scaleX)];
                K = component4Line[0 | (x * component4.scaleX * scaleX)];
              } else {
                Y = component1Line[0 | (x * component1.scaleX * scaleX)];
                Cb = component2Line[0 | (x * component2.scaleX * scaleX)];
                Cr = component3Line[0 | (x * component3.scaleX * scaleX)];
                K = component4Line[0 | (x * component4.scaleX * scaleX)];

                C = 255 - clampTo8bit(Y + 1.402 * (Cr - 128));
                M = 255 - clampTo8bit(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128));
                Ye = 255 - clampTo8bit(Y + 1.772 * (Cb - 128));
              }
              data[offset++] = 255-C;
              data[offset++] = 255-M;
              data[offset++] = 255-Ye;
              data[offset++] = 255-K;
            }
          }
          break;
        default:
          throw new Error('Unsupported color mode');
      }
      return data;
    },
    copyToImageData: function copyToImageData(imageData, formatAsRGBA) {
      var width = imageData.width, height = imageData.height;
      var imageDataArray = imageData.data;
      var data = this.getData(width, height);
      var i = 0, j = 0, x, y;
      var Y, K, C, M, R, G, B;
      switch (this.components.length) {
        case 1:
          for (y = 0; y < height; y++) {
            for (x = 0; x < width; x++) {
              Y = data[i++];

              imageDataArray[j++] = Y;
              imageDataArray[j++] = Y;
              imageDataArray[j++] = Y;
              if (formatAsRGBA) {
                imageDataArray[j++] = 255;
              }
            }
          }
          break;
        case 3:
          for (y = 0; y < height; y++) {
            for (x = 0; x < width; x++) {
              R = data[i++];
              G = data[i++];
              B = data[i++];

              imageDataArray[j++] = R;
              imageDataArray[j++] = G;
              imageDataArray[j++] = B;
              if (formatAsRGBA) {
                imageDataArray[j++] = 255;
              }
            }
          }
          break;
        case 4:
          for (y = 0; y < height; y++) {
            for (x = 0; x < width; x++) {
              C = data[i++];
              M = data[i++];
              Y = data[i++];
              K = data[i++];

              R = 255 - clampTo8bit(C * (1 - K / 255) + K);
              G = 255 - clampTo8bit(M * (1 - K / 255) + K);
              B = 255 - clampTo8bit(Y * (1 - K / 255) + K);

              imageDataArray[j++] = R;
              imageDataArray[j++] = G;
              imageDataArray[j++] = B;
              if (formatAsRGBA) {
                imageDataArray[j++] = 255;
              }
            }
          }
          break;
        default:
          throw new Error('Unsupported color mode');
      }
    }
  };


  // We cap the amount of memory used by jpeg-js to avoid unexpected OOMs from untrusted content.
  var totalBytesAllocated = 0;
  var maxMemoryUsageBytes = 0;
  function requestMemoryAllocation(increaseAmount = 0) {
    var totalMemoryImpactBytes = totalBytesAllocated + increaseAmount;
    if (totalMemoryImpactBytes > maxMemoryUsageBytes) {
      var exceededAmount = Math.ceil((totalMemoryImpactBytes - maxMemoryUsageBytes) / 1024 / 1024);
      throw new Error(`maxMemoryUsageInMB limit exceeded by at least ${exceededAmount}MB`);
    }

    totalBytesAllocated = totalMemoryImpactBytes;
  }

  constructor.resetMaxMemoryUsage = function (maxMemoryUsageBytes_) {
    totalBytesAllocated = 0;
    maxMemoryUsageBytes = maxMemoryUsageBytes_;
  };

  constructor.getBytesAllocated = function () {
    return totalBytesAllocated;
  };

  constructor.requestMemoryAllocation = requestMemoryAllocation;

  return constructor;
})();

if (typeof module !== 'undefined') {
	module.exports = decode;
} else if (typeof window !== 'undefined') {
	window['jpeg-js'] = window['jpeg-js'] || {};
	window['jpeg-js'].decode = decode;
}

function decode(jpegData, userOpts = {}) {
  var defaultOpts = {
    // "undefined" means "Choose whether to transform colors based on the image’s color model."
    colorTransform: undefined,
    useTArray: false,
    formatAsRGBA: true,
    tolerantDecoding: true,
    maxResolutionInMP: 100, // Don't decode more than 100 megapixels
    maxMemoryUsageInMB: 512, // Don't decode if memory footprint is more than 512MB
  };

  var opts = {...defaultOpts, ...userOpts};
  var arr = new Uint8Array(jpegData);
  var decoder = new JpegImage();
  decoder.opts = opts;
  // If this constructor ever supports async decoding this will need to be done differently.
  // Until then, treating as singleton limit is fine.
  JpegImage.resetMaxMemoryUsage(opts.maxMemoryUsageInMB * 1024 * 1024);
  decoder.parse(arr);

  var channels = (opts.formatAsRGBA) ? 4 : 3;
  var bytesNeeded = decoder.width * decoder.height * channels;
  try {
    JpegImage.requestMemoryAllocation(bytesNeeded);
    var image = {
      width: decoder.width,
      height: decoder.height,
      exifBuffer: decoder.exifBuffer,
      data: opts.useTArray ?
        new Uint8Array(bytesNeeded) :
        Buffer.alloc(bytesNeeded)
    };
    if(decoder.comments.length > 0) {
      image["comments"] = decoder.comments;
    }
  } catch (err) {
    if (err instanceof RangeError) {
      throw new Error("Could not allocate enough memory for the image. " +
                      "Required: " + bytesNeeded);
    } 
    
    if (err instanceof ReferenceError) {
      if (err.message === "Buffer is not defined") {
        throw new Error("Buffer is not globally defined in this environment. " +
                        "Consider setting useTArray to true");
      }
    }
    throw err;
  }

  decoder.copyToImageData(image, opts.formatAsRGBA);

  return image;
}

}).call(this)}).call(this,require("buffer").Buffer)
},{"buffer":159}],73:[function(require,module,exports){
(function (Buffer){(function (){
/*
  Copyright (c) 2008, Adobe Systems Incorporated
  All rights reserved.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions are
  met:

  * Redistributions of source code must retain the above copyright notice, 
    this list of conditions and the following disclaimer.
  
  * Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the 
    documentation and/or other materials provided with the distribution.
  
  * Neither the name of Adobe Systems Incorporated nor the names of its 
    contributors may be used to endorse or promote products derived from 
    this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009

Basic GUI blocking jpeg encoder
*/

var btoa = btoa || function(buf) {
  return Buffer.from(buf).toString('base64');
};

function JPEGEncoder(quality) {
  var self = this;
	var fround = Math.round;
	var ffloor = Math.floor;
	var YTable = new Array(64);
	var UVTable = new Array(64);
	var fdtbl_Y = new Array(64);
	var fdtbl_UV = new Array(64);
	var YDC_HT;
	var UVDC_HT;
	var YAC_HT;
	var UVAC_HT;
	
	var bitcode = new Array(65535);
	var category = new Array(65535);
	var outputfDCTQuant = new Array(64);
	var DU = new Array(64);
	var byteout = [];
	var bytenew = 0;
	var bytepos = 7;
	
	var YDU = new Array(64);
	var UDU = new Array(64);
	var VDU = new Array(64);
	var clt = new Array(256);
	var RGB_YUV_TABLE = new Array(2048);
	var currentQuality;
	
	var ZigZag = [
			 0, 1, 5, 6,14,15,27,28,
			 2, 4, 7,13,16,26,29,42,
			 3, 8,12,17,25,30,41,43,
			 9,11,18,24,31,40,44,53,
			10,19,23,32,39,45,52,54,
			20,22,33,38,46,51,55,60,
			21,34,37,47,50,56,59,61,
			35,36,48,49,57,58,62,63
		];
	
	var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];
	var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11];
	var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];
	var std_ac_luminance_values = [
			0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,
			0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,
			0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
			0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
			0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,
			0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
			0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,
			0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
			0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
			0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
			0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,
			0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
			0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,
			0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,
			0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
			0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
			0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,
			0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
			0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,
			0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
			0xf9,0xfa
		];
	
	var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];
	var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11];
	var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];
	var std_ac_chrominance_values = [
			0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,
			0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,
			0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
			0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
			0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,
			0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
			0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,
			0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
			0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
			0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,
			0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,
			0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
			0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,
			0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,
			0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
			0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
			0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,
			0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
			0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,
			0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
			0xf9,0xfa
		];
	
	function initQuantTables(sf){
			var YQT = [
				16, 11, 10, 16, 24, 40, 51, 61,
				12, 12, 14, 19, 26, 58, 60, 55,
				14, 13, 16, 24, 40, 57, 69, 56,
				14, 17, 22, 29, 51, 87, 80, 62,
				18, 22, 37, 56, 68,109,103, 77,
				24, 35, 55, 64, 81,104,113, 92,
				49, 64, 78, 87,103,121,120,101,
				72, 92, 95, 98,112,100,103, 99
			];
			
			for (var i = 0; i < 64; i++) {
				var t = ffloor((YQT[i]*sf+50)/100);
				if (t < 1) {
					t = 1;
				} else if (t > 255) {
					t = 255;
				}
				YTable[ZigZag[i]] = t;
			}
			var UVQT = [
				17, 18, 24, 47, 99, 99, 99, 99,
				18, 21, 26, 66, 99, 99, 99, 99,
				24, 26, 56, 99, 99, 99, 99, 99,
				47, 66, 99, 99, 99, 99, 99, 99,
				99, 99, 99, 99, 99, 99, 99, 99,
				99, 99, 99, 99, 99, 99, 99, 99,
				99, 99, 99, 99, 99, 99, 99, 99,
				99, 99, 99, 99, 99, 99, 99, 99
			];
			for (var j = 0; j < 64; j++) {
				var u = ffloor((UVQT[j]*sf+50)/100);
				if (u < 1) {
					u = 1;
				} else if (u > 255) {
					u = 255;
				}
				UVTable[ZigZag[j]] = u;
			}
			var aasf = [
				1.0, 1.387039845, 1.306562965, 1.175875602,
				1.0, 0.785694958, 0.541196100, 0.275899379
			];
			var k = 0;
			for (var row = 0; row < 8; row++)
			{
				for (var col = 0; col < 8; col++)
				{
					fdtbl_Y[k]  = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
					fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
					k++;
				}
			}
		}
		
		function computeHuffmanTbl(nrcodes, std_table){
			var codevalue = 0;
			var pos_in_table = 0;
			var HT = new Array();
			for (var k = 1; k <= 16; k++) {
				for (var j = 1; j <= nrcodes[k]; j++) {
					HT[std_table[pos_in_table]] = [];
					HT[std_table[pos_in_table]][0] = codevalue;
					HT[std_table[pos_in_table]][1] = k;
					pos_in_table++;
					codevalue++;
				}
				codevalue*=2;
			}
			return HT;
		}
		
		function initHuffmanTbl()
		{
			YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);
			UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);
			YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);
			UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);
		}
	
		function initCategoryNumber()
		{
			var nrlower = 1;
			var nrupper = 2;
			for (var cat = 1; cat <= 15; cat++) {
				//Positive numbers
				for (var nr = nrlower; nr<nrupper; nr++) {
					category[32767+nr] = cat;
					bitcode[32767+nr] = [];
					bitcode[32767+nr][1] = cat;
					bitcode[32767+nr][0] = nr;
				}
				//Negative numbers
				for (var nrneg =-(nrupper-1); nrneg<=-nrlower; nrneg++) {
					category[32767+nrneg] = cat;
					bitcode[32767+nrneg] = [];
					bitcode[32767+nrneg][1] = cat;
					bitcode[32767+nrneg][0] = nrupper-1+nrneg;
				}
				nrlower <<= 1;
				nrupper <<= 1;
			}
		}
		
		function initRGBYUVTable() {
			for(var i = 0; i < 256;i++) {
				RGB_YUV_TABLE[i]      		=  19595 * i;
				RGB_YUV_TABLE[(i+ 256)>>0] 	=  38470 * i;
				RGB_YUV_TABLE[(i+ 512)>>0] 	=   7471 * i + 0x8000;
				RGB_YUV_TABLE[(i+ 768)>>0] 	= -11059 * i;
				RGB_YUV_TABLE[(i+1024)>>0] 	= -21709 * i;
				RGB_YUV_TABLE[(i+1280)>>0] 	=  32768 * i + 0x807FFF;
				RGB_YUV_TABLE[(i+1536)>>0] 	= -27439 * i;
				RGB_YUV_TABLE[(i+1792)>>0] 	= - 5329 * i;
			}
		}
		
		// IO functions
		function writeBits(bs)
		{
			var value = bs[0];
			var posval = bs[1]-1;
			while ( posval >= 0 ) {
				if (value & (1 << posval) ) {
					bytenew |= (1 << bytepos);
				}
				posval--;
				bytepos--;
				if (bytepos < 0) {
					if (bytenew == 0xFF) {
						writeByte(0xFF);
						writeByte(0);
					}
					else {
						writeByte(bytenew);
					}
					bytepos=7;
					bytenew=0;
				}
			}
		}
	
		function writeByte(value)
		{
			//byteout.push(clt[value]); // write char directly instead of converting later
      byteout.push(value);
		}
	
		function writeWord(value)
		{
			writeByte((value>>8)&0xFF);
			writeByte((value   )&0xFF);
		}
		
		// DCT & quantization core
		function fDCTQuant(data, fdtbl)
		{
			var d0, d1, d2, d3, d4, d5, d6, d7;
			/* Pass 1: process rows. */
			var dataOff=0;
			var i;
			var I8 = 8;
			var I64 = 64;
			for (i=0; i<I8; ++i)
			{
				d0 = data[dataOff];
				d1 = data[dataOff+1];
				d2 = data[dataOff+2];
				d3 = data[dataOff+3];
				d4 = data[dataOff+4];
				d5 = data[dataOff+5];
				d6 = data[dataOff+6];
				d7 = data[dataOff+7];
				
				var tmp0 = d0 + d7;
				var tmp7 = d0 - d7;
				var tmp1 = d1 + d6;
				var tmp6 = d1 - d6;
				var tmp2 = d2 + d5;
				var tmp5 = d2 - d5;
				var tmp3 = d3 + d4;
				var tmp4 = d3 - d4;
	
				/* Even part */
				var tmp10 = tmp0 + tmp3;	/* phase 2 */
				var tmp13 = tmp0 - tmp3;
				var tmp11 = tmp1 + tmp2;
				var tmp12 = tmp1 - tmp2;
	
				data[dataOff] = tmp10 + tmp11; /* phase 3 */
				data[dataOff+4] = tmp10 - tmp11;
	
				var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */
				data[dataOff+2] = tmp13 + z1; /* phase 5 */
				data[dataOff+6] = tmp13 - z1;
	
				/* Odd part */
				tmp10 = tmp4 + tmp5; /* phase 2 */
				tmp11 = tmp5 + tmp6;
				tmp12 = tmp6 + tmp7;
	
				/* The rotator is modified from fig 4-8 to avoid extra negations. */
				var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */
				var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */
				var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */
				var z3 = tmp11 * 0.707106781; /* c4 */
	
				var z11 = tmp7 + z3;	/* phase 5 */
				var z13 = tmp7 - z3;
	
				data[dataOff+5] = z13 + z2;	/* phase 6 */
				data[dataOff+3] = z13 - z2;
				data[dataOff+1] = z11 + z4;
				data[dataOff+7] = z11 - z4;
	
				dataOff += 8; /* advance pointer to next row */
			}
	
			/* Pass 2: process columns. */
			dataOff = 0;
			for (i=0; i<I8; ++i)
			{
				d0 = data[dataOff];
				d1 = data[dataOff + 8];
				d2 = data[dataOff + 16];
				d3 = data[dataOff + 24];
				d4 = data[dataOff + 32];
				d5 = data[dataOff + 40];
				d6 = data[dataOff + 48];
				d7 = data[dataOff + 56];
				
				var tmp0p2 = d0 + d7;
				var tmp7p2 = d0 - d7;
				var tmp1p2 = d1 + d6;
				var tmp6p2 = d1 - d6;
				var tmp2p2 = d2 + d5;
				var tmp5p2 = d2 - d5;
				var tmp3p2 = d3 + d4;
				var tmp4p2 = d3 - d4;
	
				/* Even part */
				var tmp10p2 = tmp0p2 + tmp3p2;	/* phase 2 */
				var tmp13p2 = tmp0p2 - tmp3p2;
				var tmp11p2 = tmp1p2 + tmp2p2;
				var tmp12p2 = tmp1p2 - tmp2p2;
	
				data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */
				data[dataOff+32] = tmp10p2 - tmp11p2;
	
				var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */
				data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */
				data[dataOff+48] = tmp13p2 - z1p2;
	
				/* Odd part */
				tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */
				tmp11p2 = tmp5p2 + tmp6p2;
				tmp12p2 = tmp6p2 + tmp7p2;
	
				/* The rotator is modified from fig 4-8 to avoid extra negations. */
				var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */
				var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */
				var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */
				var z3p2 = tmp11p2 * 0.707106781; /* c4 */
	
				var z11p2 = tmp7p2 + z3p2;	/* phase 5 */
				var z13p2 = tmp7p2 - z3p2;
	
				data[dataOff+40] = z13p2 + z2p2; /* phase 6 */
				data[dataOff+24] = z13p2 - z2p2;
				data[dataOff+ 8] = z11p2 + z4p2;
				data[dataOff+56] = z11p2 - z4p2;
	
				dataOff++; /* advance pointer to next column */
			}
	
			// Quantize/descale the coefficients
			var fDCTQuant;
			for (i=0; i<I64; ++i)
			{
				// Apply the quantization and scaling factor & Round to nearest integer
				fDCTQuant = data[i]*fdtbl[i];
				outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0);
				//outputfDCTQuant[i] = fround(fDCTQuant);

			}
			return outputfDCTQuant;
		}
		
		function writeAPP0()
		{
			writeWord(0xFFE0); // marker
			writeWord(16); // length
			writeByte(0x4A); // J
			writeByte(0x46); // F
			writeByte(0x49); // I
			writeByte(0x46); // F
			writeByte(0); // = "JFIF",'\0'
			writeByte(1); // versionhi
			writeByte(1); // versionlo
			writeByte(0); // xyunits
			writeWord(1); // xdensity
			writeWord(1); // ydensity
			writeByte(0); // thumbnwidth
			writeByte(0); // thumbnheight
		}

		function writeAPP1(exifBuffer) {
			if (!exifBuffer) return;

			writeWord(0xFFE1); // APP1 marker

			if (exifBuffer[0] === 0x45 &&
					exifBuffer[1] === 0x78 &&
					exifBuffer[2] === 0x69 &&
					exifBuffer[3] === 0x66) {
				// Buffer already starts with EXIF, just use it directly
				writeWord(exifBuffer.length + 2); // length is buffer + length itself!
			} else {
				// Buffer doesn't start with EXIF, write it for them
				writeWord(exifBuffer.length + 5 + 2); // length is buffer + EXIF\0 + length itself!
				writeByte(0x45); // E
				writeByte(0x78); // X
				writeByte(0x69); // I
				writeByte(0x66); // F
				writeByte(0); // = "EXIF",'\0'
			}

			for (var i = 0; i < exifBuffer.length; i++) {
				writeByte(exifBuffer[i]);
			}
		}

		function writeSOF0(width, height)
		{
			writeWord(0xFFC0); // marker
			writeWord(17);   // length, truecolor YUV JPG
			writeByte(8);    // precision
			writeWord(height);
			writeWord(width);
			writeByte(3);    // nrofcomponents
			writeByte(1);    // IdY
			writeByte(0x11); // HVY
			writeByte(0);    // QTY
			writeByte(2);    // IdU
			writeByte(0x11); // HVU
			writeByte(1);    // QTU
			writeByte(3);    // IdV
			writeByte(0x11); // HVV
			writeByte(1);    // QTV
		}
	
		function writeDQT()
		{
			writeWord(0xFFDB); // marker
			writeWord(132);	   // length
			writeByte(0);
			for (var i=0; i<64; i++) {
				writeByte(YTable[i]);
			}
			writeByte(1);
			for (var j=0; j<64; j++) {
				writeByte(UVTable[j]);
			}
		}
	
		function writeDHT()
		{
			writeWord(0xFFC4); // marker
			writeWord(0x01A2); // length
	
			writeByte(0); // HTYDCinfo
			for (var i=0; i<16; i++) {
				writeByte(std_dc_luminance_nrcodes[i+1]);
			}
			for (var j=0; j<=11; j++) {
				writeByte(std_dc_luminance_values[j]);
			}
	
			writeByte(0x10); // HTYACinfo
			for (var k=0; k<16; k++) {
				writeByte(std_ac_luminance_nrcodes[k+1]);
			}
			for (var l=0; l<=161; l++) {
				writeByte(std_ac_luminance_values[l]);
			}
	
			writeByte(1); // HTUDCinfo
			for (var m=0; m<16; m++) {
				writeByte(std_dc_chrominance_nrcodes[m+1]);
			}
			for (var n=0; n<=11; n++) {
				writeByte(std_dc_chrominance_values[n]);
			}
	
			writeByte(0x11); // HTUACinfo
			for (var o=0; o<16; o++) {
				writeByte(std_ac_chrominance_nrcodes[o+1]);
			}
			for (var p=0; p<=161; p++) {
				writeByte(std_ac_chrominance_values[p]);
			}
		}
		
		function writeCOM(comments)
		{
			if (typeof comments === "undefined" || comments.constructor !== Array) return;
			comments.forEach(e => {
				if (typeof e !== "string") return;
				writeWord(0xFFFE); // marker
				var l = e.length;
				writeWord(l + 2); // length itself as well
				var i;
				for (i = 0; i < l; i++)
					writeByte(e.charCodeAt(i));
			});
		}
	
		function writeSOS()
		{
			writeWord(0xFFDA); // marker
			writeWord(12); // length
			writeByte(3); // nrofcomponents
			writeByte(1); // IdY
			writeByte(0); // HTY
			writeByte(2); // IdU
			writeByte(0x11); // HTU
			writeByte(3); // IdV
			writeByte(0x11); // HTV
			writeByte(0); // Ss
			writeByte(0x3f); // Se
			writeByte(0); // Bf
		}
		
		function processDU(CDU, fdtbl, DC, HTDC, HTAC){
			var EOB = HTAC[0x00];
			var M16zeroes = HTAC[0xF0];
			var pos;
			var I16 = 16;
			var I63 = 63;
			var I64 = 64;
			var DU_DCT = fDCTQuant(CDU, fdtbl);
			//ZigZag reorder
			for (var j=0;j<I64;++j) {
				DU[ZigZag[j]]=DU_DCT[j];
			}
			var Diff = DU[0] - DC; DC = DU[0];
			//Encode DC
			if (Diff==0) {
				writeBits(HTDC[0]); // Diff might be 0
			} else {
				pos = 32767+Diff;
				writeBits(HTDC[category[pos]]);
				writeBits(bitcode[pos]);
			}
			//Encode ACs
			var end0pos = 63; // was const... which is crazy
			for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {};
			//end0pos = first element in reverse order !=0
			if ( end0pos == 0) {
				writeBits(EOB);
				return DC;
			}
			var i = 1;
			var lng;
			while ( i <= end0pos ) {
				var startpos = i;
				for (; (DU[i]==0) && (i<=end0pos); ++i) {}
				var nrzeroes = i-startpos;
				if ( nrzeroes >= I16 ) {
					lng = nrzeroes>>4;
					for (var nrmarker=1; nrmarker <= lng; ++nrmarker)
						writeBits(M16zeroes);
					nrzeroes = nrzeroes&0xF;
				}
				pos = 32767+DU[i];
				writeBits(HTAC[(nrzeroes<<4)+category[pos]]);
				writeBits(bitcode[pos]);
				i++;
			}
			if ( end0pos != I63 ) {
				writeBits(EOB);
			}
			return DC;
		}

		function initCharLookupTable(){
			var sfcc = String.fromCharCode;
			for(var i=0; i < 256; i++){ ///// ACHTUNG // 255
				clt[i] = sfcc(i);
			}
		}
		
		this.encode = function(image,quality) // image data object
		{
			var time_start = new Date().getTime();
			
			if(quality) setQuality(quality);
			
			// Initialize bit writer
			byteout = new Array();
			bytenew=0;
			bytepos=7;
	
			// Add JPEG headers
			writeWord(0xFFD8); // SOI
			writeAPP0();
			writeCOM(image.comments);
			writeAPP1(image.exifBuffer);
			writeDQT();
			writeSOF0(image.width,image.height);
			writeDHT();
			writeSOS();

	
			// Encode 8x8 macroblocks
			var DCY=0;
			var DCU=0;
			var DCV=0;
			
			bytenew=0;
			bytepos=7;
			
			
			this.encode.displayName = "_encode_";

			var imageData = image.data;
			var width = image.width;
			var height = image.height;

			var quadWidth = width*4;
			var tripleWidth = width*3;
			
			var x, y = 0;
			var r, g, b;
			var start,p, col,row,pos;
			while(y < height){
				x = 0;
				while(x < quadWidth){
				start = quadWidth * y + x;
				p = start;
				col = -1;
				row = 0;
				
				for(pos=0; pos < 64; pos++){
					row = pos >> 3;// /8
					col = ( pos & 7 ) * 4; // %8
					p = start + ( row * quadWidth ) + col;		
					
					if(y+row >= height){ // padding bottom
						p-= (quadWidth*(y+1+row-height));
					}

					if(x+col >= quadWidth){ // padding right	
						p-= ((x+col) - quadWidth +4)
					}
					
					r = imageData[ p++ ];
					g = imageData[ p++ ];
					b = imageData[ p++ ];
					
					
					/* // calculate YUV values dynamically
					YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80
					UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b));
					VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b));
					*/
					
					// use lookup table (slightly faster)
					YDU[pos] = ((RGB_YUV_TABLE[r]             + RGB_YUV_TABLE[(g +  256)>>0] + RGB_YUV_TABLE[(b +  512)>>0]) >> 16)-128;
					UDU[pos] = ((RGB_YUV_TABLE[(r +  768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128;
					VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128;

				}
				
				DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);
				DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);
				DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);
				x+=32;
				}
				y+=8;
			}
			
			
			////////////////////////////////////////////////////////////////
	
			// Do the bit alignment of the EOI marker
			if ( bytepos >= 0 ) {
				var fillbits = [];
				fillbits[1] = bytepos+1;
				fillbits[0] = (1<<(bytepos+1))-1;
				writeBits(fillbits);
			}
	
			writeWord(0xFFD9); //EOI

			if (typeof module === 'undefined') return new Uint8Array(byteout);
      return Buffer.from(byteout);

			var jpegDataUri = 'data:image/jpeg;base64,' + btoa(byteout.join(''));
			
			byteout = [];
			
			// benchmarking
			var duration = new Date().getTime() - time_start;
    		//console.log('Encoding time: '+ duration + 'ms');
    		//
			
			return jpegDataUri			
	}
	
	function setQuality(quality){
		if (quality <= 0) {
			quality = 1;
		}
		if (quality > 100) {
			quality = 100;
		}
		
		if(currentQuality == quality) return // don't recalc if unchanged
		
		var sf = 0;
		if (quality < 50) {
			sf = Math.floor(5000 / quality);
		} else {
			sf = Math.floor(200 - quality*2);
		}
		
		initQuantTables(sf);
		currentQuality = quality;
		//console.log('Quality set to: '+quality +'%');
	}
	
	function init(){
		var time_start = new Date().getTime();
		if(!quality) quality = 50;
		// Create tables
		initCharLookupTable()
		initHuffmanTbl();
		initCategoryNumber();
		initRGBYUVTable();
		
		setQuality(quality);
		var duration = new Date().getTime() - time_start;
    	//console.log('Initialization '+ duration + 'ms');
	}
	
	init();
	
};

if (typeof module !== 'undefined') {
	module.exports = encode;
} else if (typeof window !== 'undefined') {
	window['jpeg-js'] = window['jpeg-js'] || {};
	window['jpeg-js'].encode = encode;
}

function encode(imgData, qu) {
  if (typeof qu === 'undefined') qu = 50;
  var encoder = new JPEGEncoder(qu);
	var data = encoder.encode(imgData, qu);
  return {
    data: data,
    width: imgData.width,
    height: imgData.height,
  };
}

// helper function to get the imageData of an existing image on the current page.
function getImageDataFromImage(idOrElement){
	var theImg = (typeof(idOrElement)=='string')? document.getElementById(idOrElement):idOrElement;
	var cvs = document.createElement('canvas');
	cvs.width = theImg.width;
	cvs.height = theImg.height;
	var ctx = cvs.getContext("2d");
	ctx.drawImage(theImg,0,0);
	
	return (ctx.getImageData(0, 0, cvs.width, cvs.height));
}

}).call(this)}).call(this,require("buffer").Buffer)
},{"buffer":159}],74:[function(require,module,exports){
/*
	Lazyness

	Copyright (c) 2018 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



function Lazyness( require_ ) {
	if ( ! this || ! ( this instanceof Lazyness ) ) { return new Lazyness( require_ ) ; }
	this.require = require_ ;
}

module.exports = Lazyness ;



// Define a lazy property
Lazyness.property = ( object , property , fn , enumerable ) => {
	Object.defineProperty( object , property , {
		configurable: true ,
		enumerable: !! enumerable ,

		// Must be a function, not =>
		get: function() {
			var value = fn( object , property ) ;

			Object.defineProperty( object , property , {
				configurable: true ,
				enumerable: !! enumerable ,
				writable: false ,
				value: value
			} ) ;

			return value ;
		}
	} ) ;

	return object ;
} ;

Lazyness.prototype.property = Lazyness.property ;



// Multiple properties at once in a property object
Lazyness.properties = ( object , properties , enumerable ) => {
	Object.keys( properties ).forEach( property => {
		Lazyness.property( object , property , properties[ property ] , enumerable ) ;
	} ) ;

	return object ;
} ;

Lazyness.prototype.properties = Lazyness.properties ;



// Define a lazy property for instances (call it on a prototype)
// Almost the same than .property() except than it use 'this' in the getter instead of 'object'
Lazyness.instanceProperty = ( object , property , fn , enumerable ) => {
	Object.defineProperty( object , property , {
		configurable: true ,
		enumerable: !! enumerable ,

		// Must be a function, not =>
		get: function() {
			var value = fn( this , property ) ;

			Object.defineProperty( this , property , {
				configurable: true ,
				enumerable: !! enumerable ,
				writable: false ,
				value: value
			} ) ;

			return value ;
		}
	} ) ;

	return object ;
} ;

Lazyness.prototype.instanceProperty = Lazyness.instanceProperty ;



// Multiple properties for instances (call it on a prototype)
Lazyness.instanceProperties = ( object , properties , enumerable ) => {
	Object.keys( properties ).forEach( property => {
		Lazyness.property( object , property , properties[ property ] , enumerable ) ;
	} ) ;

	return object ;
} ;

Lazyness.prototype.instanceProperties = Lazyness.instanceProperties ;



// Lazy value
Lazyness.value = fn => {
	var firstTime = true , value ;

	// Must be a function, not =>
	return function() {
		if ( firstTime ) {
			value = fn() ;
			firstTime = false ;
		}

		return value ;
	} ;
} ;

Lazyness.prototype.value = Lazyness.value ;



// Lazy require, return a Proxy
Lazyness.require = function( require_ , moduleId ) {
	var firstTime = true , module_ ;

	return new Proxy( ( () => {} ) , {
		construct: ( target , args ) => {
			if ( firstTime ) {
				module_ = require_( moduleId ) ;
				firstTime = false ;
			}

			return Reflect.construct( module_ , args ) ;
		} ,
		apply: ( target , thisArg , args ) => {
			if ( firstTime ) {
				module_ = require_( moduleId ) ;
				firstTime = false ;
			}

			return Reflect.apply( module_ , thisArg , args ) ;
		} ,
		get: ( target , property ) => {
			if ( firstTime ) {
				module_ = require_( moduleId ) ;
				firstTime = false ;
			}

			return Reflect.get( module_ , property ) ;
		}
	} ) ;
} ;

Lazyness.prototype.require = function( moduleId ) {
	return Lazyness.require( this.require , moduleId ) ;
} ;



Lazyness.requireProperty = function( require_ , object , property , moduleId , enumerable ) {
	Object.defineProperty( object , property , {
		configurable: true ,
		enumerable: !! enumerable ,

		// Must be a function, not =>
		get: function() {
			var module_ = require_( moduleId ) ;

			Object.defineProperty( object , property , {
				configurable: true ,
				enumerable: !! enumerable ,
				writable: false ,
				value: module_
			} ) ;

			return module_ ;
		}
	} ) ;

	return object ;
} ;

Lazyness.prototype.requireProperty = function( object , property , moduleId , enumerable ) {
	return Lazyness.requireProperty( this.require , object , property , moduleId , enumerable ) ;
} ;



// Multiple properties at once in a property object
Lazyness.requireProperties = function( require_ , object , properties , enumerable ) {
	Object.keys( properties ).forEach( property => {
		Lazyness.requireProperty( require_ , object , property , properties[ property ] , enumerable ) ;
	} ) ;

	return object ;
} ;

Lazyness.prototype.requireProperties = function( object , properties , enumerable ) {
	return Lazyness.requireProperties( this.require , object , properties , enumerable ) ;
} ;


},{}],75:[function(require,module,exports){
"use strict"

var ndarray = require("ndarray")
var do_convert = require("./doConvert.js")

module.exports = function convert(arr, result) {
  var shape = [], c = arr, sz = 1
  while(Array.isArray(c)) {
    shape.push(c.length)
    sz *= c.length
    c = c[0]
  }
  if(shape.length === 0) {
    return ndarray()
  }
  if(!result) {
    result = ndarray(new Float64Array(sz), shape)
  }
  do_convert(result, arr)
  return result
}

},{"./doConvert.js":76,"ndarray":77}],76:[function(require,module,exports){
module.exports=require('cwise-compiler')({"args":["array","scalar","index"],"pre":{"body":"{}","args":[],"thisVars":[],"localVars":[]},"body":{"body":"{\nvar _inline_1_v=_inline_1_arg1_,_inline_1_i\nfor(_inline_1_i=0;_inline_1_i<_inline_1_arg2_.length-1;++_inline_1_i) {\n_inline_1_v=_inline_1_v[_inline_1_arg2_[_inline_1_i]]\n}\n_inline_1_arg0_=_inline_1_v[_inline_1_arg2_[_inline_1_arg2_.length-1]]\n}","args":[{"name":"_inline_1_arg0_","lvalue":true,"rvalue":false,"count":1},{"name":"_inline_1_arg1_","lvalue":false,"rvalue":true,"count":1},{"name":"_inline_1_arg2_","lvalue":false,"rvalue":true,"count":4}],"thisVars":[],"localVars":["_inline_1_i","_inline_1_v"]},"post":{"body":"{}","args":[],"thisVars":[],"localVars":[]},"funcName":"convert","blockSize":64})

},{"cwise-compiler":66}],77:[function(require,module,exports){
var iota = require("iota-array")
var isBuffer = require("is-buffer")

var hasTypedArrays  = ((typeof Float64Array) !== "undefined")

function compare1st(a, b) {
  return a[0] - b[0]
}

function order() {
  var stride = this.stride
  var terms = new Array(stride.length)
  var i
  for(i=0; i<terms.length; ++i) {
    terms[i] = [Math.abs(stride[i]), i]
  }
  terms.sort(compare1st)
  var result = new Array(terms.length)
  for(i=0; i<result.length; ++i) {
    result[i] = terms[i][1]
  }
  return result
}

function compileConstructor(dtype, dimension) {
  var className = ["View", dimension, "d", dtype].join("")
  if(dimension < 0) {
    className = "View_Nil" + dtype
  }
  var useGetters = (dtype === "generic")

  if(dimension === -1) {
    //Special case for trivial arrays
    var code =
      "function "+className+"(a){this.data=a;};\
var proto="+className+".prototype;\
proto.dtype='"+dtype+"';\
proto.index=function(){return -1};\
proto.size=0;\
proto.dimension=-1;\
proto.shape=proto.stride=proto.order=[];\
proto.lo=proto.hi=proto.transpose=proto.step=\
function(){return new "+className+"(this.data);};\
proto.get=proto.set=function(){};\
proto.pick=function(){return null};\
return function construct_"+className+"(a){return new "+className+"(a);}"
    var procedure = new Function(code)
    return procedure()
  } else if(dimension === 0) {
    //Special case for 0d arrays
    var code =
      "function "+className+"(a,d) {\
this.data = a;\
this.offset = d\
};\
var proto="+className+".prototype;\
proto.dtype='"+dtype+"';\
proto.index=function(){return this.offset};\
proto.dimension=0;\
proto.size=1;\
proto.shape=\
proto.stride=\
proto.order=[];\
proto.lo=\
proto.hi=\
proto.transpose=\
proto.step=function "+className+"_copy() {\
return new "+className+"(this.data,this.offset)\
};\
proto.pick=function "+className+"_pick(){\
return TrivialArray(this.data);\
};\
proto.valueOf=proto.get=function "+className+"_get(){\
return "+(useGetters ? "this.data.get(this.offset)" : "this.data[this.offset]")+
"};\
proto.set=function "+className+"_set(v){\
return "+(useGetters ? "this.data.set(this.offset,v)" : "this.data[this.offset]=v")+"\
};\
return function construct_"+className+"(a,b,c,d){return new "+className+"(a,d)}"
    var procedure = new Function("TrivialArray", code)
    return procedure(CACHED_CONSTRUCTORS[dtype][0])
  }

  var code = ["'use strict'"]

  //Create constructor for view
  var indices = iota(dimension)
  var args = indices.map(function(i) { return "i"+i })
  var index_str = "this.offset+" + indices.map(function(i) {
        return "this.stride[" + i + "]*i" + i
      }).join("+")
  var shapeArg = indices.map(function(i) {
      return "b"+i
    }).join(",")
  var strideArg = indices.map(function(i) {
      return "c"+i
    }).join(",")
  code.push(
    "function "+className+"(a," + shapeArg + "," + strideArg + ",d){this.data=a",
      "this.shape=[" + shapeArg + "]",
      "this.stride=[" + strideArg + "]",
      "this.offset=d|0}",
    "var proto="+className+".prototype",
    "proto.dtype='"+dtype+"'",
    "proto.dimension="+dimension)

  //view.size:
  code.push("Object.defineProperty(proto,'size',{get:function "+className+"_size(){\
return "+indices.map(function(i) { return "this.shape["+i+"]" }).join("*"),
"}})")

  //view.order:
  if(dimension === 1) {
    code.push("proto.order=[0]")
  } else {
    code.push("Object.defineProperty(proto,'order',{get:")
    if(dimension < 4) {
      code.push("function "+className+"_order(){")
      if(dimension === 2) {
        code.push("return (Math.abs(this.stride[0])>Math.abs(this.stride[1]))?[1,0]:[0,1]}})")
      } else if(dimension === 3) {
        code.push(
"var s0=Math.abs(this.stride[0]),s1=Math.abs(this.stride[1]),s2=Math.abs(this.stride[2]);\
if(s0>s1){\
if(s1>s2){\
return [2,1,0];\
}else if(s0>s2){\
return [1,2,0];\
}else{\
return [1,0,2];\
}\
}else if(s0>s2){\
return [2,0,1];\
}else if(s2>s1){\
return [0,1,2];\
}else{\
return [0,2,1];\
}}})")
      }
    } else {
      code.push("ORDER})")
    }
  }

  //view.set(i0, ..., v):
  code.push(
"proto.set=function "+className+"_set("+args.join(",")+",v){")
  if(useGetters) {
    code.push("return this.data.set("+index_str+",v)}")
  } else {
    code.push("return this.data["+index_str+"]=v}")
  }

  //view.get(i0, ...):
  code.push("proto.get=function "+className+"_get("+args.join(",")+"){")
  if(useGetters) {
    code.push("return this.data.get("+index_str+")}")
  } else {
    code.push("return this.data["+index_str+"]}")
  }

  //view.index:
  code.push(
    "proto.index=function "+className+"_index(", args.join(), "){return "+index_str+"}")

  //view.hi():
  code.push("proto.hi=function "+className+"_hi("+args.join(",")+"){return new "+className+"(this.data,"+
    indices.map(function(i) {
      return ["(typeof i",i,"!=='number'||i",i,"<0)?this.shape[", i, "]:i", i,"|0"].join("")
    }).join(",")+","+
    indices.map(function(i) {
      return "this.stride["+i + "]"
    }).join(",")+",this.offset)}")

  //view.lo():
  var a_vars = indices.map(function(i) { return "a"+i+"=this.shape["+i+"]" })
  var c_vars = indices.map(function(i) { return "c"+i+"=this.stride["+i+"]" })
  code.push("proto.lo=function "+className+"_lo("+args.join(",")+"){var b=this.offset,d=0,"+a_vars.join(",")+","+c_vars.join(","))
  for(var i=0; i<dimension; ++i) {
    code.push(
"if(typeof i"+i+"==='number'&&i"+i+">=0){\
d=i"+i+"|0;\
b+=c"+i+"*d;\
a"+i+"-=d}")
  }
  code.push("return new "+className+"(this.data,"+
    indices.map(function(i) {
      return "a"+i
    }).join(",")+","+
    indices.map(function(i) {
      return "c"+i
    }).join(",")+",b)}")

  //view.step():
  code.push("proto.step=function "+className+"_step("+args.join(",")+"){var "+
    indices.map(function(i) {
      return "a"+i+"=this.shape["+i+"]"
    }).join(",")+","+
    indices.map(function(i) {
      return "b"+i+"=this.stride["+i+"]"
    }).join(",")+",c=this.offset,d=0,ceil=Math.ceil")
  for(var i=0; i<dimension; ++i) {
    code.push(
"if(typeof i"+i+"==='number'){\
d=i"+i+"|0;\
if(d<0){\
c+=b"+i+"*(a"+i+"-1);\
a"+i+"=ceil(-a"+i+"/d)\
}else{\
a"+i+"=ceil(a"+i+"/d)\
}\
b"+i+"*=d\
}")
  }
  code.push("return new "+className+"(this.data,"+
    indices.map(function(i) {
      return "a" + i
    }).join(",")+","+
    indices.map(function(i) {
      return "b" + i
    }).join(",")+",c)}")

  //view.transpose():
  var tShape = new Array(dimension)
  var tStride = new Array(dimension)
  for(var i=0; i<dimension; ++i) {
    tShape[i] = "a[i"+i+"]"
    tStride[i] = "b[i"+i+"]"
  }
  code.push("proto.transpose=function "+className+"_transpose("+args+"){"+
    args.map(function(n,idx) { return n + "=(" + n + "===undefined?" + idx + ":" + n + "|0)"}).join(";"),
    "var a=this.shape,b=this.stride;return new "+className+"(this.data,"+tShape.join(",")+","+tStride.join(",")+",this.offset)}")

  //view.pick():
  code.push("proto.pick=function "+className+"_pick("+args+"){var a=[],b=[],c=this.offset")
  for(var i=0; i<dimension; ++i) {
    code.push("if(typeof i"+i+"==='number'&&i"+i+">=0){c=(c+this.stride["+i+"]*i"+i+")|0}else{a.push(this.shape["+i+"]);b.push(this.stride["+i+"])}")
  }
  code.push("var ctor=CTOR_LIST[a.length+1];return ctor(this.data,a,b,c)}")

  //Add return statement
  code.push("return function construct_"+className+"(data,shape,stride,offset){return new "+className+"(data,"+
    indices.map(function(i) {
      return "shape["+i+"]"
    }).join(",")+","+
    indices.map(function(i) {
      return "stride["+i+"]"
    }).join(",")+",offset)}")

  //Compile procedure
  var procedure = new Function("CTOR_LIST", "ORDER", code.join("\n"))
  return procedure(CACHED_CONSTRUCTORS[dtype], order)
}

function arrayDType(data) {
  if(isBuffer(data)) {
    return "buffer"
  }
  if(hasTypedArrays) {
    switch(Object.prototype.toString.call(data)) {
      case "[object Float64Array]":
        return "float64"
      case "[object Float32Array]":
        return "float32"
      case "[object Int8Array]":
        return "int8"
      case "[object Int16Array]":
        return "int16"
      case "[object Int32Array]":
        return "int32"
      case "[object Uint8Array]":
        return "uint8"
      case "[object Uint16Array]":
        return "uint16"
      case "[object Uint32Array]":
        return "uint32"
      case "[object Uint8ClampedArray]":
        return "uint8_clamped"
      case "[object BigInt64Array]":
        return "bigint64"
      case "[object BigUint64Array]":
        return "biguint64"
    }
  }
  if(Array.isArray(data)) {
    return "array"
  }
  return "generic"
}

var CACHED_CONSTRUCTORS = {
  "float32":[],
  "float64":[],
  "int8":[],
  "int16":[],
  "int32":[],
  "uint8":[],
  "uint16":[],
  "uint32":[],
  "array":[],
  "uint8_clamped":[],
  "bigint64": [],
  "biguint64": [],
  "buffer":[],
  "generic":[]
}

;(function() {
  for(var id in CACHED_CONSTRUCTORS) {
    CACHED_CONSTRUCTORS[id].push(compileConstructor(id, -1))
  }
});

function wrappedNDArrayCtor(data, shape, stride, offset) {
  if(data === undefined) {
    var ctor = CACHED_CONSTRUCTORS.array[0]
    return ctor([])
  } else if(typeof data === "number") {
    data = [data]
  }
  if(shape === undefined) {
    shape = [ data.length ]
  }
  var d = shape.length
  if(stride === undefined) {
    stride = new Array(d)
    for(var i=d-1, sz=1; i>=0; --i) {
      stride[i] = sz
      sz *= shape[i]
    }
  }
  if(offset === undefined) {
    offset = 0
    for(var i=0; i<d; ++i) {
      if(stride[i] < 0) {
        offset -= (shape[i]-1)*stride[i]
      }
    }
  }
  var dtype = arrayDType(data)
  var ctor_list = CACHED_CONSTRUCTORS[dtype]
  while(ctor_list.length <= d+1) {
    ctor_list.push(compileConstructor(dtype, ctor_list.length-1))
  }
  var ctor = ctor_list[d+1]
  return ctor(data, shape, stride, offset)
}

module.exports = wrappedNDArrayCtor

},{"iota-array":69,"is-buffer":70}],78:[function(require,module,exports){
(function (process,global,setImmediate){(function (){
/*
	Next-Gen Events

	Copyright (c) 2015 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



// Some features needs a portable nextTick
const nextTick = process.browser ? window.setImmediate : process.nextTick ;



if ( ! global.__NEXTGEN_EVENTS__ ) {
	global.__NEXTGEN_EVENTS__ = {
		recursions: 0
	} ;
}

var globalData = global.__NEXTGEN_EVENTS__ ;



function NextGenEvents() {}
module.exports = NextGenEvents ;
NextGenEvents.prototype.__prototypeUID__ = 'nextgen-events/NextGenEvents' ;
NextGenEvents.prototype.__prototypeVersion__ = require( '../package.json' ).version ;



/* Basic features, more or less compatible with Node.js */



NextGenEvents.SYNC = -Infinity ;
NextGenEvents.DESYNC = -1 ;

NextGenEvents.defaultMaxListeners = Infinity ;



// Not part of the prototype, because it should not pollute userland's prototype.
// It has an eventEmitter as 'this' anyway (always called using call()).
NextGenEvents.init = function() {
	Object.defineProperty( this , '__ngev' , {
		configurable: true ,
		value: new NextGenEvents.Internal()
	} ) ;
} ;



NextGenEvents.Internal = function( from ) {
	this.nice = NextGenEvents.SYNC ;
	this.interruptible = false ;
	this.contexts = {} ;
	this.desync = setImmediate ;
	this.depth = 0 ;

	// States by events
	this.states = {} ;

	// State groups by events
	this.stateGroups = {} ;

	// Listeners by events
	this.listeners = {
		// Special events
		error: [] ,
		interrupt: [] ,
		newListener: [] ,
		removeListener: []
	} ;

	this.hasListenerPriority = false ;
	this.maxListeners = NextGenEvents.defaultMaxListeners ;

	if ( from ) {
		this.nice = from.nice ;
		this.interruptible = from.interruptible ;
		Object.assign( this.states , from.states ) ,
		Object.assign( this.stateGroups , from.stateGroups ) ,

		Object.keys( from.listeners ).forEach( eventName => {
			this.listeners[ eventName ] = from.listeners[ eventName ].slice() ;
		} ) ;

		// Copy all contexts
		Object.keys( from.contexts ).forEach( contextName => {
			var context = from.contexts[ contextName ] ;
			this.contexts[ contextName ] = {
				nice: context.nice ,
				ready: true ,
				status: context.status ,
				serial: context.serial ,
				scopes: {}
			} ;
		} ) ;
	}
} ;



NextGenEvents.initFrom = function( from ) {
	if ( ! from.__ngev ) { NextGenEvents.init.call( from ) ; }

	Object.defineProperty( this , '__ngev' , {
		configurable: true ,
		value: new NextGenEvents.Internal( from.__ngev )
	} ) ;
} ;



/*
	Merge listeners of duplicated event bus:
		* listeners that are present locally but not in all foreigner are removed (one of the foreigner has removed it)
		* listeners that are not present locally but present in at least one foreigner are copied

	Not sure if it will ever go public, it was a very specific use-case (Spellcast).
*/
NextGenEvents.mergeListeners = function( foreigners ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }

	// Backup the current listeners...
	var oldListeners = this.__ngev.listeners ;


	// Reset listeners...
	this.__ngev.listeners = {} ;

	Object.keys( oldListeners ).forEach( eventName => {
		this.__ngev.listeners[ eventName ] = [] ;
	} ) ;

	foreigners.forEach( foreigner => {
		if ( ! foreigner.__ngev ) { NextGenEvents.init.call( foreigner ) ; }

		Object.keys( foreigner.__ngev.listeners ).forEach( eventName => {
			if ( ! this.__ngev.listeners[ eventName ] ) { this.__ngev.listeners[ eventName ] = [] ; }
		} ) ;
	} ) ;


	// Now we can scan by eventName first
	Object.keys( this.__ngev.listeners ).forEach( eventName => {
		var i , iMax , blacklist = [] ;

		// First pass: find all removed listeners and add them to the blacklist
		if ( oldListeners[ eventName ] ) {
			oldListeners[ eventName ].forEach( listener => {
				for ( i = 0 , iMax = foreigners.length ; i < iMax ; i ++ ) {
					if (
						! foreigners[ i ].__ngev.listeners[ eventName ] ||
						foreigners[ i ].__ngev.listeners[ eventName ].indexOf( listener ) === -1
					) {
						blacklist.push( listener ) ;
						break ;
					}
				}
			} ) ;
		}

		// Second pass: add all listeners still not present and that are not blacklisted
		foreigners.forEach( foreigner => {

			foreigner.__ngev.listeners[ eventName ].forEach( listener => {
				if ( this.__ngev.listeners[ eventName ].indexOf( listener ) === -1 && blacklist.indexOf( listener ) === -1 ) {
					this.__ngev.listeners[ eventName ].push( listener ) ;
				}
			} ) ;
		} ) ;
	} ) ;
} ;



// Use it with .bind()
NextGenEvents.filterOutCallback = function( what , currentElement ) { return what !== currentElement ; } ;



// .addListener( eventName , [fn] , [options] )
NextGenEvents.prototype.addListener = function( eventName , fn , options ) {
	var listener , newListenerListeners ;

	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	if ( ! this.__ngev.listeners[ eventName ] ) { this.__ngev.listeners[ eventName ] = [] ; }

	// Argument management
	if ( ! eventName || typeof eventName !== 'string' ) {
		throw new TypeError( ".addListener(): argument #0 should be a non-empty string" ) ;
	}

	if ( typeof fn === 'function' ) {
		listener = {} ;

		if ( ! options || typeof options !== 'object' ) { options = {} ; }
	}
	else if ( options === true && fn && typeof fn === 'object' ) {
		// We want to use the current object as the listener object (used by Spellcast's serializer)
		options = listener = fn ;
		fn = undefined ;
	}
	else {
		options = fn ;

		if ( ! options || typeof options !== 'object' ) {
			throw new TypeError( ".addListener(): a function or an object with a 'fn' property which value is a function should be provided" ) ;
		}

		fn = undefined ;
		listener = {} ;
	}


	listener.fn = fn || options.fn ;
	listener.id = options.id !== undefined ? options.id : listener.fn ;

	if ( options.unique ) {
		if ( this.__ngev.listeners[ eventName ].find( e => e.id === listener.id ) ) {
			// Not unique! Return now!
			return ;
		}
	}

	listener.once = !! options.once ;
	listener.async = !! options.async ;
	listener.eventObject = !! options.eventObject ;
	listener.nice = options.nice !== undefined ? Math.floor( options.nice ) : NextGenEvents.SYNC ;
	listener.priority = + options.priority || 0 ;
	listener.context = options.context && ( typeof options.context === 'string' || typeof options.context === 'object' ) ? options.context : null ;

	if ( typeof listener.fn !== 'function' ) {
		throw new TypeError( ".addListener(): a function or an object with a 'fn' property which value is a function should be provided" ) ;
	}

	// Implicit context creation
	if ( typeof listener.context === 'string' ) {
		listener.context = this.__ngev.contexts[ listener.context ] || this.addListenerContext( listener.context ) ;
	}

	// Note: 'newListener' and 'removeListener' event return an array of listener, but not the event name.
	// So the event's name can be retrieved in the listener itself.
	listener.event = eventName ;

	if ( this.__ngev.listeners.newListener.length ) {
		// Extra care should be taken with the 'newListener' event, we should avoid recursion
		// in the case that eventName === 'newListener', but inside a 'newListener' listener,
		// .listenerCount() should report correctly
		newListenerListeners = this.__ngev.listeners.newListener.slice() ;

		this.__ngev.listeners[ eventName ].push( listener ) ;

		// Return an array, because one day, .addListener() may support multiple event addition at once,
		// e.g.: .addListener( { request: onRequest, close: onClose, error: onError } ) ;
		NextGenEvents.emitEvent( {
			emitter: this ,
			name: 'newListener' ,
			args: [ [ listener ] ] ,
			listeners: newListenerListeners
		} ) ;

		if ( this.__ngev.states[ eventName ] ) { NextGenEvents.emitToOneListener( this.__ngev.states[ eventName ] , listener ) ; }

		return this ;
	}

	this.__ngev.listeners[ eventName ].push( listener ) ;

	if ( this.__ngev.hasListenerPriority ) {
		// order higher priority first
		this.__ngev.listeners[ eventName ].sort( ( a , b ) => b.priority - a.priority ) ;
	}

	if ( this.__ngev.listeners[ eventName ].length === this.__ngev.maxListeners + 1 ) {
		process.emitWarning(
			"Possible NextGenEvents memory leak detected. " + this.__ngev.listeners[ eventName ].length + ' ' +
			eventName + " listeners added. Use emitter.setMaxListeners() to increase limit" ,
			{ type: "MaxListenersExceededWarning" }
		) ;
	}

	if ( this.__ngev.states[ eventName ] ) { NextGenEvents.emitToOneListener( this.__ngev.states[ eventName ] , listener ) ; }

	return this ;
} ;

NextGenEvents.prototype.on = NextGenEvents.prototype.addListener ;



// Short-hand
// .once( eventName , [fn] , [options] )
NextGenEvents.prototype.once = function( eventName , fn , options ) {
	if ( fn && typeof fn === 'object' ) { fn.once = true ; }
	else if ( options && typeof options === 'object' ) { options.once = true ; }
	else { options = { once: true } ; }

	return this.addListener( eventName , fn , options ) ;
} ;



// .waitFor( eventName )
// A Promise-returning .once() variant, only the first arg is returned
NextGenEvents.prototype.waitFor = function( eventName ) {
	return new Promise( resolve => {
		this.addListener( eventName , ( firstArg ) => resolve( firstArg ) , { once: true } ) ;
	} ) ;
} ;



// .waitForAll( eventName )
// A Promise-returning .once() variant, all args are returned as an array
NextGenEvents.prototype.waitForAll = function( eventName ) {
	return new Promise( resolve => {
		this.addListener( eventName , ( ... args ) => resolve( args ) , { once: true } ) ;
	} ) ;
} ;



NextGenEvents.prototype.removeListener = function( eventName , id ) {
	if ( ! eventName || typeof eventName !== 'string' ) { throw new TypeError( ".removeListener(): argument #0 should be a non-empty string" ) ; }

	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }

	var listeners = this.__ngev.listeners[ eventName ] ;
	if ( ! listeners || ! listeners.length ) { return this ; }

	var i , removedListeners , removeCount = 0 ,
		length = listeners.length ,
		hasRemoveListener = this.__ngev.listeners.removeListener.length ;

	if ( hasRemoveListener ) { removedListeners = [] ; }

	// In-place remove (from the listener array)
	for ( i = 0 ; i < length ; i ++ ) {
		if ( listeners[ i ].id === id ) {
			removeCount ++ ;
			if ( hasRemoveListener ) { removedListeners.push( listeners[ i ] ) ; }
		}
		else if ( removeCount ) {
			listeners[ i - removeCount ] = listeners[ i ] ;
		}
	}

	// Adjust the length
	if ( removeCount ) { listeners.length -= removeCount ; }

	if ( hasRemoveListener && removedListeners.length ) {
		this.emit( 'removeListener' , removedListeners ) ;
	}

	return this ;
} ;

NextGenEvents.prototype.off = NextGenEvents.prototype.removeListener ;



NextGenEvents.prototype.removeAllListeners = function( eventName ) {
	var removedListeners ;

	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }

	if ( eventName ) {
		// Remove all listeners for a particular event

		if ( ! eventName || typeof eventName !== 'string' ) { throw new TypeError( ".removeAllListeners(): argument #0 should be undefined or a non-empty string" ) ; }

		if ( ! this.__ngev.listeners[ eventName ] ) { this.__ngev.listeners[ eventName ] = [] ; }

		removedListeners = this.__ngev.listeners[ eventName ] ;
		this.__ngev.listeners[ eventName ] = [] ;

		if ( removedListeners.length && this.__ngev.listeners.removeListener.length ) {
			this.emit( 'removeListener' , removedListeners ) ;
		}
	}
	else {
		// Remove all listeners for any events
		// 'removeListener' listeners cannot be triggered: they are already deleted
		this.__ngev.listeners = {
			// Special events
			error: [] ,
			interrupt: [] ,
			newListener: [] ,
			removeListener: []
		} ;
	}

	return this ;
} ;



NextGenEvents.listenerWrapper = function( listener , event , contextScope , serial , nice ) {
	var returnValue , listenerCallback ,
		eventMaster = event.master || event ,
		interruptible = !! event.master || event.emitter.__ngev.interruptible ;

	if ( eventMaster.interrupt ) { return ; }

	if ( listener.async ) {
		if ( contextScope ) {
			contextScope.ready = ! serial ;
		}

		if ( nice < 0 ) {
			if ( globalData.recursions >= -nice ) {
				event.emitter.__ngev.desync( NextGenEvents.listenerWrapper.bind( undefined , listener , event , contextScope , serial , NextGenEvents.SYNC ) ) ;
				return ;
			}
		}
		else {
			setTimeout( NextGenEvents.listenerWrapper.bind( undefined , listener , event , contextScope , serial , NextGenEvents.SYNC ) , nice ) ;
			return ;
		}

		listenerCallback = ( arg ) => {

			eventMaster.listenersDone ++ ;

			// Async interrupt
			if ( arg && interruptible && ! eventMaster.interrupt && event.name !== 'interrupt' ) {
				eventMaster.interrupt = arg ;

				if ( eventMaster.callback ) { NextGenEvents.emitCallback( event ) ; }

				event.emitter.emit( 'interrupt' , eventMaster.interrupt ) ;
			}
			else if ( eventMaster.listenersDone >= eventMaster.listeners.length && eventMaster.callback ) {
				NextGenEvents.emitCallback( event ) ;
			}

			// Process the queue if serialized
			if ( serial ) { NextGenEvents.processScopeQueue( contextScope , true , true ) ; }
		} ;

		if ( listener.eventObject ) { listener.fn( event , listenerCallback ) ; }
		else { returnValue = listener.fn( ... event.args , listenerCallback ) ; }
	}
	else {
		if ( nice < 0 ) {
			if ( globalData.recursions >= -nice ) {
				event.emitter.__ngev.desync( NextGenEvents.listenerWrapper.bind( undefined , listener , event , contextScope , serial , NextGenEvents.SYNC ) ) ;
				return ;
			}
		}
		else {
			setTimeout( NextGenEvents.listenerWrapper.bind( undefined , listener , event , contextScope , serial , NextGenEvents.SYNC ) , nice ) ;
			return ;
		}

		if ( listener.eventObject ) { listener.fn( event ) ; }
		else { returnValue = listener.fn( ... event.args ) ; }

		eventMaster.listenersDone ++ ;
	}

	// Interrupt if non-falsy return value, if the emitter is interruptible, not already interrupted (emit once),
	// and not within an 'interrupt' event.
	if ( returnValue && interruptible && ! eventMaster.interrupt && event.name !== 'interrupt' ) {
		eventMaster.interrupt = returnValue ;

		if ( eventMaster.callback ) { NextGenEvents.emitCallback( event ) ; }

		event.emitter.emit( 'interrupt' , eventMaster.interrupt ) ;
	}
	else if ( eventMaster.listenersDone >= eventMaster.listeners.length && eventMaster.callback ) {
		NextGenEvents.emitCallback( event ) ;
	}
} ;



// A unique event ID
var nextEventId = 0 ;



/*
	emit( [nice] , eventName , [arg1] , [arg2] , [...] , [emitCallback] )
*/
NextGenEvents.prototype.emit = function( ... args ) {
	var event = NextGenEvents.createEvent( this , ... args ) ;
	return NextGenEvents.emitEvent( event ) ;
} ;



// For performance, do not emit if there is no listener for that event,
// do not even return an Event object, do not throw if the event name is error,
// or whatever the .emit() process could do when there is no listener.
NextGenEvents.prototype.emitIfListener = function( ... args ) {
	var eventName = typeof args[ 0 ] === 'number' ? args[ 1 ] : args[ 0 ] ;
	if ( ! this.__ngev || ! this.__ngev.listeners[ eventName ] || ! this.__ngev.listeners[ eventName ].length ) { return null ; }
	var event = NextGenEvents.createEvent( this , ... args ) ;
	return NextGenEvents.emitEvent( event ) ;
} ;



NextGenEvents.prototype.waitForEmit = function( ... args ) {
	return new Promise( resolve => {
		this.emit( ... args , ( interrupt ) => resolve( interrupt ) ) ;
	} ) ;
} ;



// Create an event object
NextGenEvents.createEvent = function( emitter , ... args ) {
	var event = {
		emitter: emitter ,
		interrupt: null ,
		master: null ,	// For grouped-correlated events
		sync: true
	} ;

	// Arguments handling
	if ( typeof args[ 0 ] === 'number' ) {
		event.nice = Math.floor( args[ 0 ] ) ;
		event.name = args[ 1 ] ;

		if ( ! event.name || typeof event.name !== 'string' ) {
			throw new TypeError( ".emit(): when argument #0 is a number, argument #1 should be a non-empty string" ) ;
		}

		if ( typeof args[ args.length - 1 ] === 'function' ) {
			event.callback = args[ args.length - 1 ] ;
			event.args = args.slice( 2 , -1 ) ;
		}
		else {
			event.args = args.slice( 2 ) ;
		}
	}
	else {
		//event.nice = emitter.__ngev.nice ;
		event.name = args[ 0 ] ;

		if ( ! event.name || typeof event.name !== 'string' ) {
			throw new TypeError( ".emit(): argument #0 should be an number or a non-empty string" ) ;
		}

		if ( typeof args[ args.length - 1 ] === 'function' ) {
			event.callback = args[ args.length - 1 ] ;
			event.args = args.slice( 1 , -1 ) ;
		}
		else {
			event.args = args.slice( 1 ) ;
		}
	}

	return event ;
} ;



/*
	At this stage, 'event' should be an object having those properties:
		* emitter: the event emitter
		* name: the event name
		* args: array, the arguments of the event
		* nice: (optional) nice value
		* callback: (optional) a callback for emit
		* listeners: (optional) override the listeners array stored in __ngev
*/
NextGenEvents.emitEvent = function( event ) {
	// /!\ Any change here *MUST* be reflected to NextGenEvents.emitIntricatedEvents() /!\
	var self = event.emitter ,
		i , iMax , count = 0 , state , removedListeners ;

	if ( ! self.__ngev ) { NextGenEvents.init.call( self ) ; }

	state = self.__ngev.states[ event.name ] ;

	// This is a state event, register it now!
	if ( state !== undefined ) {
		if ( state && event.args.length === state.args.length &&
			event.args.every( ( arg , index ) => arg === state.args[ index ] ) ) {
			// The emitter is already in this exact state, skip it now!
			return ;
		}

		// Unset all states of that group
		self.__ngev.stateGroups[ event.name ].forEach( ( eventName ) => {
			self.__ngev.states[ eventName ] = null ;
		} ) ;

		self.__ngev.states[ event.name ] = event ;
	}

	if ( ! self.__ngev.listeners[ event.name ] ) { self.__ngev.listeners[ event.name ] = [] ; }

	event.id = nextEventId ++ ;
	event.listenersDone = 0 ;

	if ( event.nice === undefined || event.nice === null ) { event.nice = self.__ngev.nice ; }

	// Trouble arise when a listener is removed from another listener, while we are still in the loop.
	// So we have to COPY the listener array right now!
	if ( ! event.listeners ) { event.listeners = self.__ngev.listeners[ event.name ].slice() ; }

	// Increment globalData.recursions
	globalData.recursions ++ ;
	event.depth = self.__ngev.depth ++ ;
	removedListeners = [] ;

	try {
		// Emit the event to all listeners!
		for ( i = 0 , iMax = event.listeners.length ; i < iMax ; i ++ ) {
			count ++ ;
			NextGenEvents.emitToOneListener( event , event.listeners[ i ] , removedListeners ) ;
		}
	}
	catch ( error ) {
		// Catch error, just to decrement globalData.recursions, re-throw after that...
		globalData.recursions -- ;
		self.__ngev.depth -- ;
		throw error ;
	}

	// Decrement globalData.recursions
	globalData.recursions -- ;
	if ( ! event.callback ) { self.__ngev.depth -- ; }

	// Emit 'removeListener' after calling listeners
	if ( removedListeners.length && self.__ngev.listeners.removeListener.length ) {
		self.emit( 'removeListener' , removedListeners ) ;
	}


	// 'error' event is a special case: it should be listened for, or it will throw an error
	if ( ! count ) {
		if ( event.name === 'error' ) {
			if ( event.args[ 0 ] ) { throw event.args[ 0 ] ; }
			else { throw Error( "Uncaught, unspecified 'error' event." ) ; }
		}

		if ( event.callback ) { NextGenEvents.emitCallback( event ) ; }
	}

	// Leaving sync mode
	event.sync = false ;

	return event ;
} ;



/*
	Spellcast-specific:
	Send interruptible events with listener-priority across multiple emitters.
	If an event is interrupted, all event are interrupted too.
	It has limited feature-support: no state-event, no builtin-event (not even 'error').
*/
NextGenEvents.emitIntricatedEvents = function( array , callback ) {
	var i , iMax , count = 0 , removedListeners ;

	if ( ! Array.isArray( array ) ) {
		throw new TypeError( '.emitCorrelatedEvents() argument should be an array' ) ;
	}

	var listenerEventRows = [] ,
		context = {
			nice: NextGenEvents.DESYNC ,
			ready: true ,
			status: NextGenEvents.CONTEXT_ENABLED ,
			serial: true ,
			scopes: {}
		} ,
		master = {
			sync: false ,
			nice: NextGenEvents.DESYNC ,
			context ,
			interrupt: null ,
			listeners: listenerEventRows ,	// because we need eventMaster.listeners.length
			listenersDone: 0 ,
			depth: 0 ,
			callback
		} ;

	array.forEach( eventParams => {
		var event = NextGenEvents.createEvent( ... eventParams ) ;
		event.master = master ;

		if ( ! event.emitter.__ngev ) { NextGenEvents.init.call( event.emitter ) ; }

		if ( ! event.emitter.__ngev.listeners[ event.name ] ) { event.emitter.__ngev.listeners[ event.name ] = [] ; }
		event.listeners = event.emitter.__ngev.listeners[ event.name ].slice() ;

		event.id = nextEventId ++ ;
		//event.listenersDone = 0 ;
		//event.nice = master.nice ;

		event.listeners.forEach( listener => listenerEventRows.push( { event , listener } ) ) ;
	} ) ;


	// Sort listeners
	listenerEventRows.sort( ( a , b ) => b.listener.priority - a.listener.priority ) ;

	// Increment globalData.recursions
	globalData.recursions ++ ;

	removedListeners = [] ;

	try {
		// Emit the event to all listeners!
		for ( i = 0 , iMax = listenerEventRows.length ; i < iMax ; i ++ ) {
			count ++ ;
			NextGenEvents.emitToOneListener( listenerEventRows[ i ].event , listenerEventRows[ i ].listener , removedListeners ) ;
		}
	}
	catch ( error ) {
		// Catch error, just to decrement globalData.recursions, re-throw after that...
		globalData.recursions -- ;
		throw error ;
	}

	// Decrement globalData.recursions
	globalData.recursions -- ;

	if ( ! count && master.callback ) { NextGenEvents.emitCallback( event ) ; }

	// Leaving sync mode
	master.sync = false ;
} ;



// If removedListeners is not given, then one-time listener emit the 'removeListener' event,
// if given: that's the caller business to do it
NextGenEvents.emitToOneListener = function( event , listener , removedListeners ) {
	var self = event.emitter ,
		eventMaster = event.master || event ,
		context = event.master ? event.master.context : listener.context ,
		contextScope , serial , currentNice , emitRemoveListener = false ;

	if ( context ) {
		// If the listener context is disabled...
		if ( context.status === NextGenEvents.CONTEXT_DISABLED ) { return ; }

		// The nice value for this listener...
		currentNice = Math.max( eventMaster.nice , listener.nice , context.nice ) ;
		serial = context.serial ;
		contextScope = NextGenEvents.getContextScope( context , eventMaster.depth ) ;
	}
	else {
		currentNice = Math.max( eventMaster.nice , listener.nice ) ;
	}


	if ( listener.once && self.__ngev.listeners[ event.name ] ) {
		// We should remove the current listener RIGHT NOW because of recursive .emit() issues:
		// one listener may eventually fire this very same event synchronously during the current loop.
		// ALSO another listener may have called .removeAllListeners(), so we first check that there are still
		// a listener array for this event.
		self.__ngev.listeners[ event.name ] = self.__ngev.listeners[ event.name ].filter(
			NextGenEvents.filterOutCallback.bind( undefined , listener )
		) ;

		if ( removedListeners ) { removedListeners.push( listener ) ; }
		else { emitRemoveListener = true ; }
	}

	if ( context && ( context.status === NextGenEvents.CONTEXT_QUEUED || ! contextScope.ready ) ) {
		// Almost all works should be done by .emit(), and little few should be done by .processScopeQueue()
		contextScope.queue.push( { event: event , listener: listener , nice: currentNice } ) ;
	}
	else {
		NextGenEvents.listenerWrapper( listener , event , contextScope , serial , currentNice ) ;
	}

	// Emit 'removeListener' after calling the listener
	if ( emitRemoveListener && self.__ngev.listeners.removeListener.length ) {
		self.emit( 'removeListener' , [ listener ] ) ;
	}
} ;



NextGenEvents.emitCallback = function( event ) {
	var callback ;

	if ( event.master ) {
		callback = event.master.callback ;
		delete event.master.callback ;

		if ( event.master.sync ) {
			nextTick( () => callback( event.master.interrupt , event ) ) ;
		}
		else {
			callback( event.master.interrupt , event ) ;
		}

		return ;
	}

	callback = event.callback ;
	delete event.callback ;

	if ( event.sync && event.emitter.__ngev.nice !== NextGenEvents.SYNC ) {
		// Force desync if global nice value is not SYNC
		event.emitter.__ngev.desync( () => {
			event.emitter.__ngev.depth -- ;
			callback( event.interrupt , event ) ;
		} ) ;
	}
	else {
		event.emitter.__ngev.depth -- ;
		callback( event.interrupt , event ) ;
	}
} ;



NextGenEvents.prototype.listeners = function( eventName ) {
	if ( ! eventName || typeof eventName !== 'string' ) { throw new TypeError( ".listeners(): argument #0 should be a non-empty string" ) ; }

	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	if ( ! this.__ngev.listeners[ eventName ] ) { this.__ngev.listeners[ eventName ] = [] ; }

	// Do not return the array, shallow copy it
	return this.__ngev.listeners[ eventName ].slice() ;
} ;



NextGenEvents.listenerCount = function( emitter , eventName ) {
	if ( ! emitter || ! ( emitter instanceof NextGenEvents ) ) { throw new TypeError( ".listenerCount(): argument #0 should be an instance of NextGenEvents" ) ; }
	return emitter.listenerCount( eventName ) ;
} ;



NextGenEvents.prototype.listenerCount = function( eventName ) {
	if ( ! eventName || typeof eventName !== 'string' ) { throw new TypeError( ".listenerCount(): argument #1 should be a non-empty string" ) ; }
	if ( ! this.__ngev || ! this.__ngev.listeners[ eventName ] ) { return 0 ; }
	return this.__ngev.listeners[ eventName ].length ;
} ;



NextGenEvents.prototype.setNice = function( nice ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	this.__ngev.nice = Math.floor( + nice || 0 ) ;
} ;



NextGenEvents.prototype.desyncUseNextTick = function( useNextTick ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	this.__ngev.desync = useNextTick ? nextTick : setImmediate ;
} ;



NextGenEvents.prototype.setInterruptible = function( isInterruptible ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	this.__ngev.interruptible = !! isInterruptible ;
} ;



NextGenEvents.prototype.setListenerPriority = function( hasListenerPriority ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	this.__ngev.hasListenerPriority = !! hasListenerPriority ;
} ;



// Make two objects share the same event bus
NextGenEvents.share = function( source , target ) {
	if ( ! ( source instanceof NextGenEvents ) || ! ( target instanceof NextGenEvents ) ) {
		throw new TypeError( 'NextGenEvents.share() arguments should be instances of NextGenEvents' ) ;
	}

	if ( ! source.__ngev ) { NextGenEvents.init.call( source ) ; }

	Object.defineProperty( target , '__ngev' , {
		configurable: true ,
		value: source.__ngev
	} ) ;
} ;



NextGenEvents.reset = function( emitter ) {
	Object.defineProperty( emitter , '__ngev' , {
		configurable: true ,
		value: null
	} ) ;
} ;



NextGenEvents.prototype.getMaxListeners = function() {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	return this.__ngev.maxListeners ;
} ;



NextGenEvents.prototype.setMaxListeners = function( n ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	this.__ngev.maxListeners = typeof n === 'number' && ! Number.isNaN( n ) ? Math.floor( n ) : NextGenEvents.defaultMaxListeners ;
	return this ;
} ;



// Sometime useful as a no-op callback...
NextGenEvents.noop = () => undefined ;





/* Next Gen feature: states! */



// .defineStates( exclusiveState1 , [exclusiveState2] , [exclusiveState3] , ... )
NextGenEvents.prototype.defineStates = function( ... states ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }

	states.forEach( ( state ) => {
		this.__ngev.states[ state ] = null ;
		this.__ngev.stateGroups[ state ] = states ;
	} ) ;
} ;



NextGenEvents.prototype.hasState = function( state ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	return !! this.__ngev.states[ state ] ;
} ;



NextGenEvents.prototype.getAllStates = function() {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	return Object.keys( this.__ngev.states ).filter( e => this.__ngev.states[ e ] ) ;
} ;





/* Next Gen feature: groups! */



NextGenEvents.groupAddListener = function( emitters , eventName , fn , options ) {
	// Manage arguments
	if ( typeof fn !== 'function' ) { options = fn ; fn = undefined ; }
	if ( ! options || typeof options !== 'object' ) { options = {} ; }

	fn = fn || options.fn ;
	delete options.fn ;

	// Preserve the listener ID, so groupRemoveListener() will work as expected
	options.id = options.id || fn ;

	emitters.forEach( ( emitter ) => {
		emitter.addListener( eventName , fn.bind( undefined , emitter ) , options ) ;
	} ) ;
} ;

NextGenEvents.groupOn = NextGenEvents.groupAddListener ;



// Once per emitter
NextGenEvents.groupOnce = function( emitters , eventName , fn , options ) {
	if ( fn && typeof fn === 'object' ) { fn.once = true ; }
	else if ( options && typeof options === 'object' ) { options.once = true ; }
	else { options = { once: true } ; }

	return this.groupAddListener( emitters , eventName , fn , options ) ;
} ;



// A Promise-returning .groupOnce() variant, it returns an array with only the first arg for each emitter's event
NextGenEvents.groupWaitFor = function( emitters , eventName ) {
	return Promise.all( emitters.map( emitter => emitter.waitFor( eventName ) ) ) ;
} ;



// A Promise-returning .groupOnce() variant, it returns an array of array for each emitter's event
NextGenEvents.groupWaitForAll = function( emitters , eventName ) {
	return Promise.all( emitters.map( emitter => emitter.waitForAll( eventName ) ) ) ;
} ;



// Globally once, only one event could be emitted, by the first emitter to emit
NextGenEvents.groupOnceFirst = function( emitters , eventName , fn , options ) {
	var fnWrapper , triggered = false ;

	// Manage arguments
	if ( typeof fn !== 'function' ) { options = fn ; fn = undefined ; }
	if ( ! options || typeof options !== 'object' ) { options = {} ; }

	fn = fn || options.fn ;
	delete options.fn ;

	// Preserve the listener ID, so groupRemoveListener() will work as expected
	options.id = options.id || fn ;

	fnWrapper = ( ... args ) => {
		if ( triggered ) { return ; }
		triggered = true ;
		NextGenEvents.groupRemoveListener( emitters , eventName , options.id ) ;
		fn( ... args ) ;
	} ;

	emitters.forEach( ( emitter ) => {
		emitter.once( eventName , fnWrapper.bind( undefined , emitter ) , options ) ;
	} ) ;
} ;



// A Promise-returning .groupOnceFirst() variant, only the first arg is returned
NextGenEvents.groupWaitForFirst = function( emitters , eventName ) {
	return new Promise( resolve => {
		NextGenEvents.groupOnceFirst( emitters , eventName , ( firstArg ) => resolve( firstArg ) ) ;
	} ) ;
} ;



// A Promise-returning .groupOnceFirst() variant, all args are returned as an array
NextGenEvents.groupWaitForFirstAll = function( emitters , eventName ) {
	return new Promise( resolve => {
		NextGenEvents.groupOnceFirst( emitters , eventName , ( ... args ) => resolve( args ) ) ;
	} ) ;
} ;



// Globally once, only one event could be emitted, by the last emitter to emit
NextGenEvents.groupOnceLast = function( emitters , eventName , fn , options ) {
	var fnWrapper , triggered = false , count = emitters.length ;

	// Manage arguments
	if ( typeof fn !== 'function' ) { options = fn ; fn = undefined ; }
	if ( ! options || typeof options !== 'object' ) { options = {} ; }

	fn = fn || options.fn ;
	delete options.fn ;

	// Preserve the listener ID, so groupRemoveListener() will work as expected
	options.id = options.id || fn ;

	fnWrapper = ( ... args ) => {
		if ( triggered ) { return ; }
		if ( -- count ) { return ; }

		// So this is the last emitter...

		triggered = true ;
		// No need to remove listeners: there are already removed anyway
		//NextGenEvents.groupRemoveListener( emitters , eventName , options.id ) ;
		fn( ... args ) ;
	} ;

	emitters.forEach( ( emitter ) => {
		emitter.once( eventName , fnWrapper.bind( undefined , emitter ) , options ) ;
	} ) ;
} ;



// A Promise-returning .groupGlobalWaitFor() variant, only the first arg is returned
NextGenEvents.groupWaitForLast = function( emitters , eventName ) {
	return new Promise( resolve => {
		NextGenEvents.groupOnceLast( emitters , eventName , ( firstArg ) => resolve( firstArg ) ) ;
	} ) ;
} ;



// A Promise-returning .groupGlobalWaitFor() variant, all args are returned as an array
NextGenEvents.groupWaitForLastAll = function( emitters , eventName ) {
	return new Promise( resolve => {
		NextGenEvents.groupOnceLast( emitters , eventName , ( ... args ) => resolve( args ) ) ;
	} ) ;
} ;



NextGenEvents.groupRemoveListener = function( emitters , eventName , id ) {
	emitters.forEach( ( emitter ) => {
		emitter.removeListener( eventName , id ) ;
	} ) ;
} ;

NextGenEvents.groupOff = NextGenEvents.groupRemoveListener ;



NextGenEvents.groupRemoveAllListeners = function( emitters , eventName ) {
	emitters.forEach( ( emitter ) => {
		emitter.removeAllListeners( eventName ) ;
	} ) ;
} ;



NextGenEvents.groupEmit = function( emitters , ... args ) {
	var eventName , nice , argStart = 1 , argEnd , count = emitters.length ,
		callback , callbackWrapper , callbackTriggered = false ;

	if ( typeof args[ args.length - 1 ] === 'function' ) {
		argEnd = -1 ;
		callback = args[ args.length - 1 ] ;

		callbackWrapper = ( interruption ) => {
			if ( callbackTriggered ) { return ; }

			if ( interruption ) {
				callbackTriggered = true ;
				callback( interruption ) ;
			}
			else if ( ! -- count ) {
				callbackTriggered = true ;
				callback() ;
			}
		} ;
	}

	if ( typeof args[ 0 ] === 'number' ) {
		argStart = 2 ;
		nice = typeof args[ 0 ] ;
	}

	eventName = args[ argStart - 1 ] ;
	args = args.slice( argStart , argEnd ) ;

	emitters.forEach( ( emitter ) => {
		NextGenEvents.emitEvent( {
			emitter: emitter ,
			name: eventName ,
			args: args ,
			nice: nice ,
			callback: callbackWrapper
		} ) ;
	} ) ;
} ;



NextGenEvents.groupWaitForEmit = function( emitters , ... args ) {
	return new Promise( resolve => {
		NextGenEvents.groupEmit( emitters , ... args , ( interrupt ) => resolve( interrupt ) ) ;
	} ) ;
} ;



NextGenEvents.groupDefineStates = function( emitters , ... args ) {
	emitters.forEach( ( emitter ) => {
		emitter.defineStates( ... args ) ;
	} ) ;
} ;



// Bad names, but since they make their way through the API documentation,
// it should be kept for backward compatibility, but they are DEPRECATED.
NextGenEvents.groupGlobalOnce = NextGenEvents.groupOnceFirst ;
NextGenEvents.groupGlobalOnceAll = NextGenEvents.groupOnceLast ;





/* Next Gen feature: contexts! */



NextGenEvents.CONTEXT_ENABLED = 0 ;
NextGenEvents.CONTEXT_DISABLED = 1 ;
NextGenEvents.CONTEXT_QUEUED = 2 ;



NextGenEvents.prototype.addListenerContext = function( contextName , options ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }

	if ( ! contextName || typeof contextName !== 'string' ) { throw new TypeError( ".addListenerContext(): argument #0 should be a non-empty string" ) ; }
	if ( ! options || typeof options !== 'object' ) { options = {} ; }

	var context = this.__ngev.contexts[ contextName ] ;

	if ( ! context ) {
		context = this.__ngev.contexts[ contextName ] = {
			nice: NextGenEvents.SYNC ,
			ready: true ,
			status: NextGenEvents.CONTEXT_ENABLED ,
			serial: false ,
			scopes: {}
		} ;
	}

	if ( options.nice !== undefined ) { context.nice = Math.floor( options.nice ) ; }
	if ( options.status !== undefined ) { context.status = options.status ; }
	if ( options.serial !== undefined ) { context.serial = !! options.serial ; }

	return context ;
} ;



NextGenEvents.prototype.getListenerContext = function( contextName ) {
	return this.__ngev.contexts[ contextName ] ;
} ;



NextGenEvents.getContextScope = function( context , scopeName ) {
	var scope = context.scopes[ scopeName ] ;

	if ( ! scope ) {
		scope = context.scopes[ scopeName ] = {
			ready: true ,
			queue: []
		} ;
	}

	return scope ;
} ;



NextGenEvents.prototype.disableListenerContext = function( contextName ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	if ( ! contextName || typeof contextName !== 'string' ) { throw new TypeError( ".disableListenerContext(): argument #0 should be a non-empty string" ) ; }
	if ( ! this.__ngev.contexts[ contextName ] ) { this.addListenerContext( contextName ) ; }

	this.__ngev.contexts[ contextName ].status = NextGenEvents.CONTEXT_DISABLED ;

	return this ;
} ;



NextGenEvents.prototype.enableListenerContext = function( contextName ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	if ( ! contextName || typeof contextName !== 'string' ) { throw new TypeError( ".enableListenerContext(): argument #0 should be a non-empty string" ) ; }
	if ( ! this.__ngev.contexts[ contextName ] ) { this.addListenerContext( contextName ) ; }

	var context = this.__ngev.contexts[ contextName ] ;

	context.status = NextGenEvents.CONTEXT_ENABLED ;

	Object.values( context.scopes ).forEach( contextScope => {
		if ( contextScope.queue.length > 0 ) { NextGenEvents.processScopeQueue( contextScope , context.serial ) ; }
	} ) ;

	return this ;
} ;



NextGenEvents.prototype.queueListenerContext = function( contextName ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	if ( ! contextName || typeof contextName !== 'string' ) { throw new TypeError( ".queueListenerContext(): argument #0 should be a non-empty string" ) ; }
	if ( ! this.__ngev.contexts[ contextName ] ) { this.addListenerContext( contextName ) ; }

	this.__ngev.contexts[ contextName ].status = NextGenEvents.CONTEXT_QUEUED ;

	return this ;
} ;



NextGenEvents.prototype.serializeListenerContext = function( contextName , value ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	if ( ! contextName || typeof contextName !== 'string' ) { throw new TypeError( ".serializeListenerContext(): argument #0 should be a non-empty string" ) ; }
	if ( ! this.__ngev.contexts[ contextName ] ) { this.addListenerContext( contextName ) ; }

	this.__ngev.contexts[ contextName ].serial = value === undefined ? true : !! value ;

	return this ;
} ;



NextGenEvents.prototype.setListenerContextNice = function( contextName , nice ) {
	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }
	if ( ! contextName || typeof contextName !== 'string' ) { throw new TypeError( ".setListenerContextNice(): argument #0 should be a non-empty string" ) ; }
	if ( ! this.__ngev.contexts[ contextName ] ) { this.addListenerContext( contextName ) ; }

	this.__ngev.contexts[ contextName ].nice = Math.floor( nice ) ;

	return this ;
} ;



NextGenEvents.prototype.destroyListenerContext = function( contextName ) {
	var i , length , context , eventName , newListeners , removedListeners = [] ;

	if ( ! contextName || typeof contextName !== 'string' ) { throw new TypeError( ".disableListenerContext(): argument #0 should be a non-empty string" ) ; }

	if ( ! this.__ngev ) { NextGenEvents.init.call( this ) ; }

	context = this.__ngev.contexts[ contextName ] ;
	if ( ! context ) { return ; }

	for ( eventName in this.__ngev.listeners ) {
		newListeners = null ;
		length = this.__ngev.listeners[ eventName ].length ;

		for ( i = 0 ; i < length ; i ++ ) {
			if ( this.__ngev.listeners[ eventName ][ i ].context === context ) {
				newListeners = [] ;
				removedListeners.push( this.__ngev.listeners[ eventName ][ i ] ) ;
			}
			else if ( newListeners ) {
				newListeners.push( this.__ngev.listeners[ eventName ][ i ] ) ;
			}
		}

		if ( newListeners ) { this.__ngev.listeners[ eventName ] = newListeners ; }
	}

	delete this.__ngev.contexts[ contextName ] ;

	if ( removedListeners.length && this.__ngev.listeners.removeListener.length ) {
		this.emit( 'removeListener' , removedListeners ) ;
	}

	return this ;
} ;



NextGenEvents.processScopeQueue = function( contextScope , serial , isCompletionCallback ) {
	var job , event , eventMaster , emitter ;

	if ( isCompletionCallback ) { contextScope.ready = true ; }

	// Increment recursion
	globalData.recursions ++ ;

	while ( contextScope.ready && contextScope.queue.length ) {
		job = contextScope.queue.shift() ;
		event = job.event ;
		eventMaster = event.master || event ;
		emitter = event.emitter ;

		// This event has been interrupted, drop it now!
		if ( eventMaster.interrupt ) { continue ; }

		NextGenEvents.listenerWrapper( job.listener , event , contextScope , serial , job.nice ) ;
	}

	// Decrement recursion
	globalData.recursions -- ;
} ;



// Backup for the AsyncTryCatch
NextGenEvents.on = NextGenEvents.prototype.on ;
NextGenEvents.once = NextGenEvents.prototype.once ;
NextGenEvents.off = NextGenEvents.prototype.off ;



if ( global.AsyncTryCatch ) {
	NextGenEvents.prototype.asyncTryCatchId = global.AsyncTryCatch.NextGenEvents.length ;
	global.AsyncTryCatch.NextGenEvents.push( NextGenEvents ) ;

	if ( global.AsyncTryCatch.substituted ) {
		global.AsyncTryCatch.substitute() ;
	}
}



// Load Proxy AT THE END (circular require)
NextGenEvents.Proxy = require( './Proxy.js' ) ;


}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate)
},{"../package.json":80,"./Proxy.js":79,"_process":191,"timers":209}],79:[function(require,module,exports){
/*
	Next-Gen Events

	Copyright (c) 2015 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



function Proxy() {
	this.localServices = {} ;
	this.remoteServices = {} ;
	this.nextAckId = 1 ;
}

module.exports = Proxy ;

var NextGenEvents = require( './NextGenEvents.js' ) ;
var MESSAGE_TYPE = 'NextGenEvents/message' ;

function noop() {}



// Backward compatibility
Proxy.create = ( ... args ) => new Proxy( ... args ) ;



// Add a local service accessible remotely
Proxy.prototype.addLocalService = function( id , emitter , options ) {
	this.localServices[ id ] = LocalService.create( this , id , emitter , options ) ;
	return this.localServices[ id ] ;
} ;



// Add a remote service accessible locally
Proxy.prototype.addRemoteService = function( id ) {
	this.remoteServices[ id ] = RemoteService.create( this , id ) ;
	return this.remoteServices[ id ] ;
} ;



// Destroy the proxy
Proxy.prototype.destroy = function() {
	Object.keys( this.localServices ).forEach( ( id ) => {
		this.localServices[ id ].destroy() ;
		delete this.localServices[ id ] ;
	} ) ;

	Object.keys( this.remoteServices ).forEach( ( id ) => {
		this.remoteServices[ id ].destroy() ;
		delete this.remoteServices[ id ] ;
	} ) ;

	this.receive = this.send = noop ;
} ;



// Push an event message.
Proxy.prototype.push = function( message ) {
	if (
		message.__type !== MESSAGE_TYPE ||
		! message.service || typeof message.service !== 'string' ||
		! message.event || typeof message.event !== 'string' ||
		! message.method
	) {
		return ;
	}

	switch ( message.method ) {
		// Those methods target a remote service
		case 'event' :
			return this.remoteServices[ message.service ] && this.remoteServices[ message.service ].receiveEvent( message ) ;
		case 'ackEmit' :
			return this.remoteServices[ message.service ] && this.remoteServices[ message.service ].receiveAckEmit( message ) ;

		// Those methods target a local service
		case 'emit' :
			return this.localServices[ message.service ] && this.localServices[ message.service ].receiveEmit( message ) ;
		case 'listen' :
			return this.localServices[ message.service ] && this.localServices[ message.service ].receiveListen( message ) ;
		case 'ignore' :
			return this.localServices[ message.service ] && this.localServices[ message.service ].receiveIgnore( message ) ;
		case 'ackEvent' :
			return this.localServices[ message.service ] && this.localServices[ message.service ].receiveAckEvent( message ) ;

		default :
			return ;
	}
} ;



// This is the method to receive and decode data from the other side of the communication channel, most of time another proxy.
// In most case, this should be overwritten.
Proxy.prototype.receive = function( raw ) {
	this.push( raw ) ;
} ;



// This is the method used to send data to the other side of the communication channel, most of time another proxy.
// This MUST be overwritten in any case.
Proxy.prototype.send = function() {
	throw new Error( 'The send() method of the Proxy MUST be extended/overwritten' ) ;
} ;



/* Local Service */



function LocalService( proxy , id , emitter , options ) { return LocalService.create( proxy , id , emitter , options ) ; }
Proxy.LocalService = LocalService ;



LocalService.create = function( proxy , id , emitter , options ) {
	var self = Object.create( LocalService.prototype , {
		proxy: { value: proxy , enumerable: true } ,
		id: { value: id , enumerable: true } ,
		emitter: { value: emitter , writable: true , enumerable: true } ,
		internalEvents: { value: Object.create( NextGenEvents.prototype ) , writable: true , enumerable: true } ,
		events: { value: {} , enumerable: true } ,
		canListen: { value: !! options.listen , writable: true , enumerable: true } ,
		canEmit: { value: !! options.emit , writable: true , enumerable: true } ,
		canAck: { value: !! options.ack , writable: true , enumerable: true } ,
		canRpc: { value: !! options.rpc , writable: true , enumerable: true } ,
		destroyed: { value: false , writable: true , enumerable: true }
	} ) ;

	return self ;
} ;



// Destroy a service
LocalService.prototype.destroy = function() {
	Object.keys( this.events ).forEach( ( eventName ) => {
		this.emitter.off( eventName , this.events[ eventName ] ) ;
		delete this.events[ eventName ] ;
	} ) ;

	this.emitter = null ;
	this.destroyed = true ;
} ;



// Remote want to emit on the local service
LocalService.prototype.receiveEmit = function( message ) {
	if ( this.destroyed || ! this.canEmit || ( message.ack && ! this.canAck ) ) { return ; }

	var event = {
		emitter: this.emitter ,
		name: message.event ,
		args: message.args || []
	} ;

	if ( message.ack ) {
		event.callback = ( interruption ) => {

			this.proxy.send( {
				__type: MESSAGE_TYPE ,
				service: this.id ,
				method: 'ackEmit' ,
				ack: message.ack ,
				event: message.event ,
				interruption: interruption
			} ) ;
		} ;
	}

	NextGenEvents.emitEvent( event ) ;
} ;



// Remote want to listen to an event of the local service
LocalService.prototype.receiveListen = function( message ) {
	if ( this.destroyed || ! this.canListen || ( message.ack && ! this.canAck ) ) { return ; }

	if ( message.ack ) {
		if ( this.events[ message.event ] ) {
			if ( this.events[ message.event ].ack ) { return ; }

			// There is already an event, but not featuring ack, remove that listener now
			this.emitter.off( message.event , this.events[ message.event ] ) ;
		}

		this.events[ message.event ] = LocalService.forwardWithAck.bind( this ) ;
		this.events[ message.event ].ack = true ;
		this.emitter.on( message.event , this.events[ message.event ] , { eventObject: true , async: true } ) ;
	}
	else {
		if ( this.events[ message.event ] ) {
			if ( ! this.events[ message.event ].ack ) { return ; }

			// Remote want to downgrade:
			// there is already an event, but featuring ack so we remove that listener now
			this.emitter.off( message.event , this.events[ message.event ] ) ;
		}

		this.events[ message.event ] = LocalService.forward.bind( this ) ;
		this.events[ message.event ].ack = false ;
		this.emitter.on( message.event , this.events[ message.event ] , { eventObject: true } ) ;
	}
} ;



// Remote do not want to listen to that event of the local service anymore
LocalService.prototype.receiveIgnore = function( message ) {
	if ( this.destroyed || ! this.canListen ) { return ; }

	if ( ! this.events[ message.event ] ) { return ; }

	this.emitter.off( message.event , this.events[ message.event ] ) ;
	this.events[ message.event ] = null ;
} ;



//
LocalService.prototype.receiveAckEvent = function( message ) {
	if (
		this.destroyed || ! this.canListen || ! this.canAck || ! message.ack ||
		! this.events[ message.event ] || ! this.events[ message.event ].ack
	) {
		return ;
	}

	this.internalEvents.emit( 'ack' , message ) ;
} ;



// Send an event from the local service to remote
LocalService.forward = function( event ) {
	if ( this.destroyed ) { return ; }

	this.proxy.send( {
		__type: MESSAGE_TYPE ,
		service: this.id ,
		method: 'event' ,
		event: event.name ,
		args: event.args
	} ) ;
} ;

LocalService.forward.ack = false ;



// Send an event from the local service to remote, with ACK
LocalService.forwardWithAck = function( event , callback ) {
	if ( this.destroyed ) { return ; }

	if ( ! event.callback ) {
		// There is no emit callback, no need to ack this one
		this.proxy.send( {
			__type: MESSAGE_TYPE ,
			service: this.id ,
			method: 'event' ,
			event: event.name ,
			args: event.args
		} ) ;

		callback() ;
		return ;
	}

	var triggered = false ;
	var ackId = this.proxy.nextAckId ++ ;

	var onAck = ( message ) => {
		if ( triggered || message.ack !== ackId ) { return ; }	// Not our ack...
		//if ( message.event !== event ) { return ; }	// Do we care?
		triggered = true ;
		this.internalEvents.off( 'ack' , onAck ) ;
		callback() ;
	} ;

	this.internalEvents.on( 'ack' , onAck ) ;

	this.proxy.send( {
		__type: MESSAGE_TYPE ,
		service: this.id ,
		method: 'event' ,
		event: event.name ,
		ack: ackId ,
		args: event.args
	} ) ;
} ;

LocalService.forwardWithAck.ack = true ;



/* Remote Service */



function RemoteService( proxy , id ) { return RemoteService.create( proxy , id ) ; }
//RemoteService.prototype = Object.create( NextGenEvents.prototype ) ;
//RemoteService.prototype.constructor = RemoteService ;
Proxy.RemoteService = RemoteService ;



var EVENT_NO_ACK = 1 ;
var EVENT_ACK = 2 ;



RemoteService.create = function( proxy , id ) {
	var self = Object.create( RemoteService.prototype , {
		proxy: { value: proxy , enumerable: true } ,
		id: { value: id , enumerable: true } ,
		// This is the emitter where everything is routed to
		emitter: { value: Object.create( NextGenEvents.prototype ) , writable: true , enumerable: true } ,
		internalEvents: { value: Object.create( NextGenEvents.prototype ) , writable: true , enumerable: true } ,
		events: { value: {} , enumerable: true } ,
		destroyed: { value: false , writable: true , enumerable: true }

		/*	Useless for instance, unless some kind of service capabilities discovery mechanism exists
		canListen: { value: !! options.listen , writable: true , enumerable: true } ,
		canEmit: { value: !! options.emit , writable: true , enumerable: true } ,
		canAck: { value: !! options.ack , writable: true , enumerable: true } ,
		canRpc: { value: !! options.rpc , writable: true , enumerable: true } ,
		*/
	} ) ;

	return self ;
} ;



// Destroy a service
RemoteService.prototype.destroy = function() {
	this.emitter.removeAllListeners() ;
	this.emitter = null ;
	Object.keys( this.events ).forEach( ( eventName ) => { delete this.events[ eventName ] ; } ) ;
	this.destroyed = true ;
} ;



// Local code want to emit to remote service
RemoteService.prototype.emit = function( eventName , ... args ) {
	if ( this.destroyed ) { return ; }

	var callback , ackId , triggered ;

	if ( typeof eventName === 'number' ) { throw new TypeError( 'Cannot emit with a nice value on a remote service' ) ; }

	if ( typeof args[ args.length - 1 ] !== 'function' ) {
		this.proxy.send( {
			__type: MESSAGE_TYPE ,
			service: this.id ,
			method: 'emit' ,
			event: eventName ,
			args: args
		} ) ;

		return ;
	}

	callback = args.pop() ;
	ackId = this.proxy.nextAckId ++ ;
	triggered = false ;

	var onAck = ( message ) => {
		if ( triggered || message.ack !== ackId ) { return ; }	// Not our ack...
		//if ( message.event !== event ) { return ; }	// Do we care?
		triggered = true ;
		this.internalEvents.off( 'ack' , onAck ) ;
		callback( message.interruption ) ;
	} ;

	this.internalEvents.on( 'ack' , onAck ) ;

	this.proxy.send( {
		__type: MESSAGE_TYPE ,
		service: this.id ,
		method: 'emit' ,
		ack: ackId ,
		event: eventName ,
		args: args
	} ) ;
} ;



// Local code want to listen to an event of remote service
RemoteService.prototype.addListener = function( eventName , fn , options ) {
	if ( this.destroyed ) { return ; }

	// Manage arguments the same way NextGenEvents#addListener() does
	if ( typeof fn !== 'function' ) { options = fn ; fn = undefined ; }
	if ( ! options || typeof options !== 'object' ) { options = {} ; }
	options.fn = fn || options.fn ;

	this.emitter.addListener( eventName , options ) ;

	// No event was added...
	if ( ! this.emitter.__ngev.listeners[ eventName ] || ! this.emitter.__ngev.listeners[ eventName ].length ) { return ; }

	// If the event is successfully listened to and was not remotely listened...
	if ( options.async && this.events[ eventName ] !== EVENT_ACK ) {
		// We need to listen to or upgrade this event
		this.events[ eventName ] = EVENT_ACK ;

		this.proxy.send( {
			__type: MESSAGE_TYPE ,
			service: this.id ,
			method: 'listen' ,
			ack: true ,
			event: eventName
		} ) ;
	}
	else if ( ! options.async && ! this.events[ eventName ] ) {
		// We need to listen to this event
		this.events[ eventName ] = EVENT_NO_ACK ;

		this.proxy.send( {
			__type: MESSAGE_TYPE ,
			service: this.id ,
			method: 'listen' ,
			event: eventName
		} ) ;
	}
} ;

RemoteService.prototype.on = RemoteService.prototype.addListener ;

// This is a shortcut to this.addListener()
RemoteService.prototype.once = NextGenEvents.prototype.once ;



// Local code want to ignore an event of remote service
RemoteService.prototype.removeListener = function( eventName , id ) {
	if ( this.destroyed ) { return ; }

	this.emitter.removeListener( eventName , id ) ;

	// If no more listener are locally tied to with event and the event was remotely listened...
	if (
		( ! this.emitter.__ngev.listeners[ eventName ] || ! this.emitter.__ngev.listeners[ eventName ].length ) &&
		this.events[ eventName ]
	) {
		this.events[ eventName ] = 0 ;

		this.proxy.send( {
			__type: MESSAGE_TYPE ,
			service: this.id ,
			method: 'ignore' ,
			event: eventName
		} ) ;
	}
} ;

RemoteService.prototype.off = RemoteService.prototype.removeListener ;



// A remote service sent an event we are listening to, emit on the service representing the remote
RemoteService.prototype.receiveEvent = function( message ) {
	if ( this.destroyed || ! this.events[ message.event ] ) { return ; }

	var event = {
		emitter: this.emitter ,
		name: message.event ,
		args: message.args || []
	} ;

	if ( message.ack ) {
		event.callback = () => {

			this.proxy.send( {
				__type: MESSAGE_TYPE ,
				service: this.id ,
				method: 'ackEvent' ,
				ack: message.ack ,
				event: message.event
			} ) ;
		} ;
	}

	NextGenEvents.emitEvent( event ) ;

	var eventName = event.name ;

	// Here we should catch if the event is still listened to ('once' type listeners)
	//if ( this.events[ eventName ]	) // not needed, already checked at the begining of the function
	if ( ! this.emitter.__ngev.listeners[ eventName ] || ! this.emitter.__ngev.listeners[ eventName ].length ) {
		this.events[ eventName ] = 0 ;

		this.proxy.send( {
			__type: MESSAGE_TYPE ,
			service: this.id ,
			method: 'ignore' ,
			event: eventName
		} ) ;
	}
} ;



//
RemoteService.prototype.receiveAckEmit = function( message ) {
	if ( this.destroyed || ! message.ack || this.events[ message.event ] !== EVENT_ACK ) {
		return ;
	}

	this.internalEvents.emit( 'ack' , message ) ;
} ;


},{"./NextGenEvents.js":78}],80:[function(require,module,exports){
module.exports={
  "name": "nextgen-events",
  "version": "1.5.3",
  "description": "The next generation of events handling for javascript! New: abstract away the network!",
  "main": "lib/NextGenEvents.js",
  "engines": {
    "node": ">=6.0.0"
  },
  "directories": {
    "test": "test",
    "bench": "bench"
  },
  "dependencies": {},
  "devDependencies": {
    "browserify": "^17.0.0",
    "uglify-js-es6": "^2.8.9",
    "ws": "^7.4.6"
  },
  "scripts": {
    "test": "tea-time -R dot"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/cronvel/nextgen-events.git"
  },
  "keywords": [
    "events",
    "async",
    "emit",
    "listener",
    "context",
    "series",
    "serialize",
    "namespace",
    "proxy",
    "network"
  ],
  "author": "Cédric Ronvel",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/cronvel/nextgen-events/issues"
  },
  "config": {
    "tea-time": {
      "coverDir": [
        "lib"
      ]
    }
  },
  "copyright": {
    "title": "Next-Gen Events",
    "years": [
      2015,
      2021
    ],
    "owner": "Cédric Ronvel"
  }
}

},{}],81:[function(require,module,exports){
module.exports = require('./lib/bitmap');

},{"./lib/bitmap":82}],82:[function(require,module,exports){
(function (Buffer){(function (){
var Bitmap = module.exports = exports = function(buffer){
  this.buffer = buffer;
  this.initialized = false;

  this.fileHeader = null;
  this.infoHeader = null;
  this.coreHeader = null;
  this.colorPalette = null;
  this.dataPos = -1;
};
Bitmap.prototype.CORE_TYPE_WINDOWS_V3 = 40;
Bitmap.prototype.CORE_TYPE_WINDOWS_V4 = 108;
Bitmap.prototype.CORE_TYPE_WINDOWS_V5 = 124;
Bitmap.prototype.CORE_TYPE_OS2_V1 = 12;
Bitmap.prototype.CORE_TYPE_OS2_V2 = 64;
Bitmap.prototype.BITMAPCOREHEADER = Bitmap.prototype.CORE_TYPE_OS2_V1;
Bitmap.prototype.BITMAPINFOHEADER = Bitmap.prototype.CORE_TYPE_WINDOWS_V3;
Bitmap.prototype.BITMAPINFOHEADER2 = Bitmap.prototype.CORE_TYPE_OS2_V2;
Bitmap.prototype.BITMAPV4HEADER = Bitmap.prototype.CORE_TYPE_WINDOWS_V4;
Bitmap.prototype.BITMAPV5HEADER = Bitmap.prototype.CORE_TYPE_WINDOWS_V5;
Bitmap.prototype.COMPRESSION_BI_RGB = 0;
Bitmap.prototype.COMPRESSION_BI_RLE8 = 1;
Bitmap.prototype.COMPRESSION_BI_RLE4 = 2;
Bitmap.prototype.COMPRESSION_BI_BITFIELDS = 3;
Bitmap.prototype.COMPRESSION_BI_JPEG = 4;
Bitmap.prototype.COMPRESSION_BI_PNG = 5;
Bitmap.prototype.BITCOUNT_2 = 1;
Bitmap.prototype.BITCOUNT_16 = 4;
Bitmap.prototype.BITCOUNT_256 = 8;
Bitmap.prototype.BITCOUNT_16bit = 16;
Bitmap.prototype.BITCOUNT_24bit = 24;
Bitmap.prototype.BITCOUNT_32bit = 32;
Bitmap.prototype.init = function(){
  this.readFileHeader();
  this.readInfoHeader();
  this.readCoreHeader();
  this.readColorPalette();

  this.initDataPos();
  this.initialized = true;
};
Bitmap.prototype.checkInit = function (){
  if(!this.initialized){
    throw new Error('not initialized');
  }
  /* nop */
};
Bitmap.prototype.isBitmap = function(){
  this.checkInit();

  if('BM' == this.fileHeader.bfType){
    return true;
  }
  return false;
};
Bitmap.prototype.getData = function (){
  this.checkInit();

  if(this.COMPRESSION_BI_RGB !== this.coreHeader.__copmression__){
    throw new Error('not supported compression: ' + this.coreHeader.__copmression__);
  }

  var bitCount = this.coreHeader.__bitCount__;
  var width = this.getWidth();
  var height = this.getHeight();

  var line = (width * bitCount) / 8;
  if(0 != (line % 4)){
    line = ((line / 4) + 1) * 4;
  }

  var rgbaData = [];
  var dataPos = this.dataPos;
  for(var i = 0; i < height; ++i) {
    var pos = dataPos + (line * (height - (i + 1)));
    var buf = this.buffer.slice(pos, pos + line);
    var color = this.mapColor(buf, bitCount);
    rgbaData.push(color);
  }
  return rgbaData;
};
Bitmap.prototype.getWidth = function (){
  this.checkInit();

  return this.coreHeader.__width__;
};
Bitmap.prototype.getHeight = function (){
  this.checkInit();

  return this.coreHeader.__height__;
};
Bitmap.prototype.read = function(buf, offset, limit){
  var read = [];
  for(var i = offset, len = offset + limit; i < len; ++i){
    read.push(buf.readInt8(i));
  }
  return new Buffer(read);
};
Bitmap.prototype.readFileHeader = function(){
  var bfType = this.read(this.buffer, 0, 2);
  var bfSize = this.read(this.buffer, 2, 4);
  var bfReserved1 = this.read(this.buffer, 6, 2);
  var bfReserved2 = this.read(this.buffer, 8, 2);
  var bfOffBits = this.read(this.buffer, 10, 4);

  this.fileHeader = {
    bfType: bfType.toString('ascii'),
    _bfType: bfType,
    bfSize: bfSize.readUInt16LE(0),
    _bfSize: bfSize,
    bfReserved1: 0,
    bfReserved2: 0,
    bfOffBits: bfOffBits.readUInt16LE(0),
    _bfOffBits: bfOffBits
  };
};
Bitmap.prototype.readInfoHeader = function (){
  this.infoHeader = this.read(this.buffer, 14, 4);
};
Bitmap.prototype.readCoreHeader = function (){
  var coreType = this.infoHeader.readUInt16LE(0);
  switch(coreType){
  case this.BITMAPCOREHEADER:
    return this.readCoreHeaderOS2_V1();
  case this.BITMAPINFOHEADER2:
    return this.readCoreHeaderOS2_V2();
  case this.BITMAPV4HEADER:
    return this.readCoreHeaderWINDOWS_V4();
  case this.BITMAPV5HEADER:
    return this.readCoreHeaderWINDOWS_V5();
  case this.BITMAPINFOHEADER:
    return this.readCoreHeaderWINDOWS_V3();
  default:
    throw new Error('unknown coreType: ' + coreType);
  }
};
Bitmap.prototype.readCoreHeaderWINDOWS_V3 = function (){
  var biWidth = this.read(this.buffer, 0x12, 4);
  var biHeight = this.read(this.buffer, 0x16, 4);
  var biPlanes = this.read(this.buffer, 0x1a, 2);
  var biBitCount = this.read(this.buffer, 0x1c, 2);
  var biCopmression = this.read(this.buffer, 0x1e, 4);
  var biSizeImage = this.read(this.buffer, 0x22, 4);
  var biXPixPerMeter = this.read(this.buffer, 0x26, 4);
  var biYPixPerMeter = this.read(this.buffer, 0x2a, 4);
  var biClrUsed = this.read(this.buffer, 0x2e, 4);
  var biCirImportant = this.read(this.buffer, 0x32, 4);

  this.coreHeader = {
    __copmression__: biCopmression.readUInt16LE(0),
    __bitCount__: biBitCount.readUInt8(0),
    __width__: biWidth.readUInt16LE(0),
    __height__: biHeight.readUInt16LE(0),
    biWidth: biWidth.readUInt16LE(0),
    _biWidth: biWidth,
    biHeight: biHeight.readUInt16LE(0),
    _biHeight: biHeight,
    biPlanes: biPlanes.readUInt8(0),
    _biPlanes: biPlanes,
    biBitCount: biBitCount.readUInt8(0),
    _biBitCount: biBitCount,
    biCopmression: biCopmression.readUInt16LE(0),
    _biCopmression: biCopmression,
    biSizeImage: biSizeImage.readUInt16LE(0),
    _biSizeImage: biSizeImage,
    biXPixPerMeter: biXPixPerMeter.readUInt16LE(0),
    _biXPixPerMeter: biXPixPerMeter,
    biYPixPerMeter: biYPixPerMeter.readUInt16LE(0),
    _biYPixPerMeter: biYPixPerMeter,
    biClrUsed: biClrUsed.readUInt16LE(0),
    _biClrUsed: biClrUsed,
    biCirImportant: biCirImportant.readUInt16LE(0),
    _biCirImportant: biCirImportant
  };
};
Bitmap.prototype.readCoreHeaderWINDOWS_V4 = function (){
  throw new Error('not yet impl');
  
  var bV4Width = this.read(this.buffer, 0x12, 4);
  var bV4Height = this.read(this.buffer, 0x16, 4);
  var bV4Planes = this.read(this.buffer, 0x1a, 2);
  var bV4BitCount = this.read(this.buffer, 0x1c, 2);
  var bV4Compression = this.read(this.buffer, 0x1e, 4);
  var bV4SizeImage = this.read(this.buffer, 0x22, 4);
  var bV4XPelsPerMeter = this.read(this.buffer, 0x26, 4);
  var bV4YPelsPerMeter = this.read(this.buffer, 0x2a, 4);
  var bV4ClrUsed = this.read(this.buffer, 0x2e, 4);
  var bV4ClrImportant = this.read(this.buffer, 0x32, 4);
  var bV4RedMask = this.read(this.buffer, 0x36, 4);
  var bV4GreenMask = this.read(this.buffer, 0x3a, 4);
  var bV4BlueMask = this.read(this.buffer, 0x3e, 4);
  var bV4AlphaMask = this.read(this.buffer, 0x42, 4);
  var bV4CSType = this.read(this.buffer, 0x46, 4);
  var bV4Endpoints = this.read(this.buffer, 0x6a, 36);
  var bV4GammaRed = this.read(this.buffer, 0x6e, 4);
  var bV4GammaGreen = this.read(this.buffer, 0x72, 4);
  var bV4GammaBlue = this.read(this.buffer, 0x76, 4);

  this.coreHeader = {
    __compression__: bV4Compression.readUInt16LE(0),
    __bitCount__: bV4BitCount.readUInt8(0),
    __width__: bV4Width.readUInt16LE(0),
    __height__: bV4Height.readUInt16LE(0),
    bV4Width: bV4Width.readUInt16LE(0),
    _bV4Width: bV4Width,
    bV4Height: bV4Height.readUInt16LE(0),
    _bV4Height: bV4Height,
    bV4Planes: bV4Planes.readUInt8(0),
    _bV4Planes: bV4Planes,
    bV4BitCount: bV4BitCount.readUInt8(0),
    _bV4BitCount: bV4BitCount,
    bV4Compression: bV4Compression.readUInt16LE(0),
    _bV4Compression: bV4Compression,
    bV4SizeImage: bV4SizeImage.readUInt16LE(0),
    _bV4SizeImage: bV4SizeImage,
    bV4XPelsPerMeter: bV4XPelsPerMeter.readUInt16LE(0),
    _bV4XPelsPerMeter: bV4XPelsPerMeter,
    bV4YPelsPerMeter: bV4YPelsPerMeter.readUInt16LE(0),
    _bV4YPelsPerMeter: bV4YPelsPerMeter,
    bV4ClrUsed: bV4ClrUsed.readUInt16LE(0),
    _bV4ClrUsed: bV4ClrUsed,
    bV4ClrImportant: bV4ClrImportant.readUInt16LE(0),
    _bV4ClrImportant: bV4ClrImportant,
    bV4RedMask: bV4RedMask.readUInt16LE(0),
    _bV4RedMask: bV4RedMask,
    bV4GreenMask: bV4GreenMask.readUInt16LE(0),
    _bV4GreenMask: bV4GreenMask,
    bV4BlueMask: bV4BlueMask.readUInt16LE(0),
    _bV4BlueMask: bV4BlueMask,
    bV4AlphaMask: bV4AlphaMask.readUInt16LE(0),
    _bV4AlphaMask: bV4AlphaMask,
    bV4CSType: bV4CSType.readUInt16LE(0),
    _bV4CSType: bV4CSType,
    bV4Endpoints: null,
    _bV4Endpoints: bV4Endpoints,
    bV4GammaRed: bV4GammaRed.readUInt16LE(0),
    _bV4GammaRed: bV4GammaRed,
    bV4GammaGreen: bV4GammaGreen.readUInt16LE(0),
    _bV4GammaGreen: bV4GammaGreen,
    bV4GammaBlue: bV4GammaBlue.readUInt16LE(0),
    _bV4GammaBlue: bV4GammaBlue
  };
};
Bitmap.prototype.readCoreHeaderWINDOWS_V5 = function (){
  throw new Error('not yet impl');

  var bV5Width = this.read(this.buffer, 0x12, 4);
  var bV5Height = this.read(this.buffer, 0x16, 4);
  var bV5Planes = this.read(this.buffer, 0x1a, 2);
  var bV5BitCount = this.read(this.buffer, 0x1c, 2);
  var bV5Compression = this.read(this.buffer, 0x1e, 4);
  var bV5SizeImage = this.read(this.buffer, 0x22, 4);
  var bV5XPelsPerMeter = this.read(this.buffer, 0x26, 4);
  var bV5YPelsPerMeter = this.read(this.buffer, 0x2a, 4);
  var bV5ClrUsed = this.read(this.buffer, 0x2e, 4);
  var bV5ClrImportant = this.read(this.buffer, 0x32, 4);
  var bV5RedMask = this.read(this.buffer, 0x36, 4);
  var bV5GreenMask = this.read(this.buffer, 0x3a, 4);
  var bV5BlueMask = this.read(this.buffer, 0x3e, 4);
  var bV5AlphaMask = this.read(this.buffer, 0x42, 4);
  var bV5CSType = this.read(this.buffer, 0x46, 4);
  var bV5Endpoints = this.read(this.buffer, 0x6a, 36);
  var bV5GammaRed = this.read(this.buffer, 0x6e, 4);
  var bV5GammaGreen = this.read(this.buffer, 0x72, 4);
  var bV5GammaBlue = this.read(this.buffer, 0x76, 4);
  var bV5Intent = this.read(this.buffer, 0x7a, 4);
  var bV5ProfileData = this.read(this.buffer, 0x7e, 4);
  var bV5ProfileSize = this.read(this.buffer, 0x82, 4);
  var bV5Reserved = this.read(this.buffer, 0x86, 4);

  this.coreHeader = {
    __compression__: bV5Compression.readUInt16LE(0),
    __bitCount__: bV5BitCount.readUInt8(0),
    __width__: bV5Width.readUInt16LE(0),
    __height__: bV5Height.readUInt16LE(0),
    bV5Width: bV5Width.readUInt16LE(0),
    _bV5Width: bV5Width,
    bV5Height: bV5Height.readUInt16LE(0),
    _bV5Height: bV5Height,
    bV5Planes: bV5Planes.readUInt8(0),
    _bV5Planes: bV5Planes,
    bV5BitCount: bV5BitCount.readUInt8(0),
    _bV5BitCount: bV5BitCount,
    bV5Compression: bV5Compression.readUInt16LE(0),
    _bV5Compression: bV5Compression,
    bV5SizeImage: bV5SizeImage.readUInt16LE(0),
    _bV5SizeImage: bV5SizeImage,
    bV5XPelsPerMeter: bV5XPelsPerMeter.readUInt16LE(0),
    _bV5XPelsPerMeter: bV5XPelsPerMeter,
    bV5YPelsPerMeter: bV5YPelsPerMeter.readUInt16LE(0),
    _bV5YPelsPerMeter: bV5YPelsPerMeter,
    bV5ClrUsed: bV5ClrUsed.readUInt16LE(0),
    _bV5ClrUsed: bV5ClrUsed,
    bV5ClrImportant: bV5ClrImportant.readUInt16LE(0),
    _bV5ClrImportant: bV5ClrImportant,
    bV5RedMask: bV5RedMask.readUInt16LE(0),
    _bV5RedMask: bV5RedMask,
    bV5GreenMask: bV5GreenMask.readUInt16LE(0),
    _bV5GreenMask: bV5GreenMask,
    bV5BlueMask: bV5BlueMask.readUInt16LE(0),
    _bV5BlueMask: bV5BlueMask,
    bV5AlphaMask: bV5AlphaMask.readUInt16LE(0),
    _bV5AlphaMask: bV5AlphaMask,
    bV5CSType: bV5CSType.readUInt16LE(0),
    _bV5CSType: bV5CSType,
    bV5Endpoints: null,
    _bV5Endpoints: bV5Endpoints,
    bV5GammaRed: bV5GammaRed.readUInt16LE(0),
    _bV5GammaRed: bV5GammaRed,
    bV5GammaGreen: bV5GammaGreen.readUInt16LE(0),
    _bV5GammaGreen: bV5GammaGreen,
    bV5GammaBlue: bV5GammaBlue.readUInt16LE(0),
    _bV5GammaBlue: bV5GammaBlue,
    bV5Intent: bV5Intent.readUInt16LE(0),
    _bV5Intent: bV5Intent,
    bV5ProfileData: bV5ProfileData.readUInt16LE(0),
    _bV5ProfileData: bV5ProfileData,
    bV5ProfileSize: bV5ProfileSize.readUInt16LE(0),
    _bV5ProfileSize: bV5ProfileSize,
    bV5Reserved: 0,
    _bV5Reserved: bV5Reserved
  };
};
Bitmap.prototype.readCoreHeaderOS2_V1 = function (){
  throw new Error('not yet impl');

  var bcWidth = this.read(this.buffer, 0x12, 2);
  var bcHeight = this.read(this.buffer, 0x14, 2);
  var bcPlanes = this.read(this.buffer, 0x16, 2);
  var bcBitCount = this.read(this.buffer, 0x18, 2);

  this.coreHeader = {
    __compression__: 0,
    __bitCount__: bcBitCount.readUInt8(0),
    __width__: bcWidth.readUInt8(0),
    __height__: bcHeight.readUInt8(0),
    bcWidth: bcWidth.readUInt8(0),
    _bcWidth: bcWidth,
    bcHeight: bcHeight.readUInt8(0),
    _bcHeight: bcHeight,
    bcPlanes: bcPlanes.readUInt8(0),
    _bcPlanes: bcPlanes,
    bcBitCount: bcBitCount.readUInt8(0),
    _bcBitCount: bcBitCount
  };
};
Bitmap.prototype.readCoreHeaderOS2_V2 = function (){
  throw new Error('not yet impl');

  var cx = this.read(this.buffer, 0x12, 4);
  var cy = this.read(this.buffer, 0x16, 4);
  var cPlanes = this.read(this.buffer, 0x1a, 2);
  var cBitCount = this.read(this.buffer, 0x1c, 2);
  var ulCompression = this.read(this.buffer, 0x1e, 4);
  var cbImage = this.read(this.buffer, 0x22, 4);
  var cxResolution = this.read(this.buffer, 0x26, 4);
  var cyResolution = this.read(this.buffer, 0x2a, 4);
  var cclrUsed = this.read(this.buffer, 0x2e, 4);
  var cclrImportant = this.read(this.buffer, 0x32, 4);
  var usUnits = this.read(this.buffer, 0x36, 2);
  var usReserved = this.read(this.buffer, 0x38, 2);
  var usRecording = this.read(this.buffer, 0x3a, 2);
  var usRendering = this.read(this.buffer, 0x3c, 2);
  var cSize1 = this.read(this.buffer, 0x3e, 4);
  var cSize2 = this.read(this.buffer, 0x42, 4);
  var ulColorEncoding = this.read(this.buffer, 0x46, 4);
  var ulIdentifier = this.read(this.buffer, 0x4a, 4);

  this.coreHeader = {
    __compression__: ulCompression.readUInt16LE(0),
    __bitCount__: cBitCount.readUInt8(0),
    __width__: cx.readUInt16LE(0),
    __height__: cy.readUInt16LE(0),
    cx: cx.readUInt16LE(0),
    _cx: cx,
    cy: cy.readUInt16LE(0),
    _cy: cy,
    cPlanes: cPlanes.readUInt8(0),
    _cPlanes: cPlanes,
    cBitCount: cBitCount.readUInt8(0),
    _cBitCount: cBitCount,
    ulCompression: ulCompression.readUInt16LE(0),
    _ulCompression: ulCompression,
    cbImage: cbImage.readUInt16LE(0),
    _cbImage: cbImage,
    cxResolution: cxResolution.readUInt16LE(0),
    _cxResolution: cxResolution,
    cyResolution: cyResolution.readUInt16LE(0),
    _cyResolution: cyResolution,
    cclrUsed: cclrUsed.readUInt16LE(0),
    _cclrUsed: cclrUsed,
    cclrImportant: cclrImportant.readUInt16LE(0),
    _cclrImportant: cclrImportant,
    usUnits: usUnits.readUInt8(0),
    _usUnits: usUnits,
    usReserved: usReserved.readUInt8(0),
    _usReserved: usReserved,
    usRecording: usRecording.readUInt8(0),
    _usRecording: usRecording,
    usRendering: usRendering.readUInt8(0),
    _usRendering: usRendering,
    cSize1: cSize1.readUInt16LE(0),
    _cSize1: cSize1,
    cSize2: cSize2.readUInt16LE(0),
    _cSize1: cSize1,
    ulColorEncoding: ulColorEncoding.readUInt16LE(0),
    _ulColorEncoding: ulColorEncoding,
    ulIdentifier: ulIdentifier.readUInt16LE(0),
    _ulIdentifier: ulIdentifier
  };
};
Bitmap.prototype.readColorPalette = function (){
  var bitCount = this.coreHeader.__bitCount__;
  if(this.BITCOUNT_16bit == bitCount){
    return /* nop */;
  }
  if(this.BITCOUNT_24bit == bitCount){
    return /* nop */;
  }
  if(this.BITCOUNT_32bit == bitCount){
    return /* nop */;
  }

  var coreType = this.infoHeader.readUInt16LE(0);
  switch(coreType){
  case this.BITMAPCOREHEADER:
    return this.readColorPalette_RGBTRIPLE(bitCount, 0x1a);
  case this.BITMAPINFOHEADER2:
    return this.readColorPalette_RGBTRIPLE(bitCount, 0x4e);
    case this.BITMAPV4HEADER:
    return this.readColorPalette_RGBQUAD(bitCount, 0x7a);
  case this.BITMAPV5HEADER:
    return this.readColorPalette_RGBQUAD(bitCount, 0x8a);
  case this.BITMAPINFOHEADER:
    return this.readColorPalette_RGBQUAD(bitCount, 0x36);
  default:
    throw new Error('unknown colorPalette: ' + coreType + ',' + bitCount);
  }
};
Bitmap.prototype.readColorPalette_RGBTRIPLE = function (bitCount, startPos){
  throw new Error('not yet impl');
};
Bitmap.prototype.readColorPalette_RGBQUAD = function (bitCount, startPos){
  if(this.BITCOUNT_2 == bitCount){
    return this.readRGBQUAD(1 << this.BITCOUNT_2, startPos);
  }
  if(this.BITCOUNT_16 == bitCount){
    return this.readRGBQUAD(1 << this.BITCOUNT_16, startPos);
  }
  if(this.BITCOUNT_256 == bitCount){
    return this.readRGBQUAD(1 << this.BITCOUNT_256, startPos);
  }
  throw new Error('unknown bitCount: ' + bitCount);
};
Bitmap.prototype.readRGBQUAD = function(count, startPos){
  var palette = [];
  for(var i = startPos, len = startPos + (4 * count); i < len; i += 4){
    palette.push({
      rgbBlue: this.read(this.buffer, i, 1).readUInt8(0),
      rgbGreen: this.read(this.buffer, i + 1, 1).readUInt8(0),
      rgbRed: this.read(this.buffer, i + 2, 1).readUInt8(0),
      rgbReserved: this.read(this.buffer, i + 3, 1).readUInt8(0)
    });
  }
  this.colorPalette = palette;
};
Bitmap.prototype.initDataPos = function(){
  var bitCount = this.coreHeader.__bitCount__;
  var hasPalette = true;
  if(this.BITCOUNT_16bit == bitCount){
    hasPalette = true;
  }
  if(this.BITCOUNT_24bit == bitCount){
    hasPalette = true;
  }
  if(this.BITCOUNT_32bit == bitCount){
    hasPalette = true;
  }

  var coreType = this.infoHeader.readUInt16LE(0);
  switch(coreType){
  case this.BITMAPCOREHEADER:
    this.dataPos = 0x1a;
    if(hasPalette){
      this.dataPos = this.dataPos + (3 * (1 << bitCount));
    }
    break;
  case this.BITMAPINFOHEADER2:
    this.dataPos = 0x4e;
    if(hasPalette){
      this.dataPos = this.dataPos + (3 * (1 << bitCount));
    }
    break;
  case this.BITMAPV4HEADER:
    this.dataPos = 0x7a;
    if(hasPalette){
      this.dataPos = this.dataPos + (4 * (1 << bitCount));
    }
    break;
  case this.BITMAPV5HEADER:
    this.dataPos = 0x8a;
    if(hasPalette){
      this.dataPos = this.dataPos + (4 * (1 << bitCount));
    }
  case this.BITMAPINFOHEADER:
    this.dataPos = 0x36;
    if(hasPalette){
      this.dataPos = this.dataPos + (4 * (1 << bitCount));
    }
    break;
  default:
    throw new Error('unknown colorPalette: ' + coreType + ',' + bitCount);
  }
};
Bitmap.prototype.mapRGBA = function(r, g, b, a){
  var hex = [];

  var padHex = function(value){
    var h = value.toString(16);
    if(value < 0x0f){
      return '0' + h;
    }
    return h;
  };

  hex.push(padHex(r));
  hex.push(padHex(g));
  hex.push(padHex(b));

  return '#' + hex.join('');
};
Bitmap.prototype.mapColor = function(bmpBuf, bitCount){
  var b, g, r, a;
  var length = bmpBuf.length;
  var colorData = [];

  if(this.BITCOUNT_2 == bitCount){
    for(var i = 0; i < length; ++i){
      var paletteValue = bmpBuf[i];
      var bin = paletteValue.toString(2);
      bin = new Array(8 - bin.length).join('0') + bin;

      for(var j = 0; j < bin.length; ++j){
        var paletteIndex = parseInt(bin.substring(j, j + 1), 10);
        var palette = this.colorPalette[paletteIndex];
        colorData.push(this.mapRGBA(palette.rgbRed, palette.rgbGreen, palette.rgbBlue, -1));
      }
    }
    return colorData;
  }
  if(this.BITCOUNT_16 == bitCount){
    for(var i = 0; i < length; i += 2){
      var paletteHigh = bmpBuf.readUInt8(i);
      var paletteLow = bmpBuf.readUInt8(i + 1);
      var indexes = [paletteHigh, paletteLow];
      indexes.forEach(function(paletteIndex){
        var palette = this.colorPalette[paletteIndex];
        colorData.push(this.mapRGBA(palette.rgbRed, palette.rgbGreen, palette.rgbBlue, -1));
      });
    }

    return colorData;
  }
  if(this.BITCOUNT_256 == bitCount){
    for(var i = 0; i < length; ++i){
      var paletteIndex = bmpBuf.readUInt16LE(i);
      var palette = this.colorPalette[paletteIndex];
      colorData.push(this.mapRGBA(palette.rgbRed, palette.rgbGreen, palette.rgbBlue, -1));
    }
    return colorData;
  }
  if(this.BITCOUNT_16bit == bitCount){
    for(var i = 0; i < length; i += 3){
      b = bmpBuf[i];
      g = bmpBuf[i + 1];
      r = bmpBuf[i + 2];
      colorData.push(this.mapRGBA(r, g, b, -1));
    }
    return colorData;
  }
  if(this.BITCOUNT_24bit == bitCount){
    for(var i = 0; i < length; i += 3){
      b = bmpBuf[i];
      g = bmpBuf[i + 1];
      r = bmpBuf[i + 2];
      colorData.push(this.mapRGBA(r, g, b, -1));
    }
    return colorData;
  }
  if(this.BITCOUNT_32bit == bitCount){
    for(var i = 0; i < length; i += 4){
      b = bmpBuf[i];
      g = bmpBuf[i + 1];
      r = bmpBuf[i + 2];
      a = bmpBuf[i + 3];
      colorData.push(this.mapRGBA(r, g, b, a));
    }
    return colorData;
  }
  throw new Error('unknown bitCount: ' + bitCount);
};

}).call(this)}).call(this,require("buffer").Buffer)
},{"buffer":159}],83:[function(require,module,exports){
// (c) Dean McNamee <dean@gmail.com>, 2013.
//
// https://github.com/deanm/omggif
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
// omggif is a JavaScript implementation of a GIF 89a encoder and decoder,
// including animation and compression.  It does not rely on any specific
// underlying system, so should run in the browser, Node, or Plask.

"use strict";

function GifWriter(buf, width, height, gopts) {
  var p = 0;

  var gopts = gopts === undefined ? { } : gopts;
  var loop_count = gopts.loop === undefined ? null : gopts.loop;
  var global_palette = gopts.palette === undefined ? null : gopts.palette;

  if (width <= 0 || height <= 0 || width > 65535 || height > 65535)
    throw new Error("Width/Height invalid.");

  function check_palette_and_num_colors(palette) {
    var num_colors = palette.length;
    if (num_colors < 2 || num_colors > 256 ||  num_colors & (num_colors-1)) {
      throw new Error(
          "Invalid code/color length, must be power of 2 and 2 .. 256.");
    }
    return num_colors;
  }

  // - Header.
  buf[p++] = 0x47; buf[p++] = 0x49; buf[p++] = 0x46;  // GIF
  buf[p++] = 0x38; buf[p++] = 0x39; buf[p++] = 0x61;  // 89a

  // Handling of Global Color Table (palette) and background index.
  var gp_num_colors_pow2 = 0;
  var background = 0;
  if (global_palette !== null) {
    var gp_num_colors = check_palette_and_num_colors(global_palette);
    while (gp_num_colors >>= 1) ++gp_num_colors_pow2;
    gp_num_colors = 1 << gp_num_colors_pow2;
    --gp_num_colors_pow2;
    if (gopts.background !== undefined) {
      background = gopts.background;
      if (background >= gp_num_colors)
        throw new Error("Background index out of range.");
      // The GIF spec states that a background index of 0 should be ignored, so
      // this is probably a mistake and you really want to set it to another
      // slot in the palette.  But actually in the end most browsers, etc end
      // up ignoring this almost completely (including for dispose background).
      if (background === 0)
        throw new Error("Background index explicitly passed as 0.");
    }
  }

  // - Logical Screen Descriptor.
  // NOTE(deanm): w/h apparently ignored by implementations, but set anyway.
  buf[p++] = width & 0xff; buf[p++] = width >> 8 & 0xff;
  buf[p++] = height & 0xff; buf[p++] = height >> 8 & 0xff;
  // NOTE: Indicates 0-bpp original color resolution (unused?).
  buf[p++] = (global_palette !== null ? 0x80 : 0) |  // Global Color Table Flag.
             gp_num_colors_pow2;  // NOTE: No sort flag (unused?).
  buf[p++] = background;  // Background Color Index.
  buf[p++] = 0;  // Pixel aspect ratio (unused?).

  // - Global Color Table
  if (global_palette !== null) {
    for (var i = 0, il = global_palette.length; i < il; ++i) {
      var rgb = global_palette[i];
      buf[p++] = rgb >> 16 & 0xff;
      buf[p++] = rgb >> 8 & 0xff;
      buf[p++] = rgb & 0xff;
    }
  }

  if (loop_count !== null) {  // Netscape block for looping.
    if (loop_count < 0 || loop_count > 65535)
      throw new Error("Loop count invalid.")
    // Extension code, label, and length.
    buf[p++] = 0x21; buf[p++] = 0xff; buf[p++] = 0x0b;
    // NETSCAPE2.0
    buf[p++] = 0x4e; buf[p++] = 0x45; buf[p++] = 0x54; buf[p++] = 0x53;
    buf[p++] = 0x43; buf[p++] = 0x41; buf[p++] = 0x50; buf[p++] = 0x45;
    buf[p++] = 0x32; buf[p++] = 0x2e; buf[p++] = 0x30;
    // Sub-block
    buf[p++] = 0x03; buf[p++] = 0x01;
    buf[p++] = loop_count & 0xff; buf[p++] = loop_count >> 8 & 0xff;
    buf[p++] = 0x00;  // Terminator.
  }


  var ended = false;

  this.addFrame = function(x, y, w, h, indexed_pixels, opts) {
    if (ended === true) { --p; ended = false; }  // Un-end.

    opts = opts === undefined ? { } : opts;

    // TODO(deanm): Bounds check x, y.  Do they need to be within the virtual
    // canvas width/height, I imagine?
    if (x < 0 || y < 0 || x > 65535 || y > 65535)
      throw new Error("x/y invalid.")

    if (w <= 0 || h <= 0 || w > 65535 || h > 65535)
      throw new Error("Width/Height invalid.")

    if (indexed_pixels.length < w * h)
      throw new Error("Not enough pixels for the frame size.");

    var using_local_palette = true;
    var palette = opts.palette;
    if (palette === undefined || palette === null) {
      using_local_palette = false;
      palette = global_palette;
    }

    if (palette === undefined || palette === null)
      throw new Error("Must supply either a local or global palette.");

    var num_colors = check_palette_and_num_colors(palette);

    // Compute the min_code_size (power of 2), destroying num_colors.
    var min_code_size = 0;
    while (num_colors >>= 1) ++min_code_size;
    num_colors = 1 << min_code_size;  // Now we can easily get it back.

    var delay = opts.delay === undefined ? 0 : opts.delay;

    // From the spec:
    //     0 -   No disposal specified. The decoder is
    //           not required to take any action.
    //     1 -   Do not dispose. The graphic is to be left
    //           in place.
    //     2 -   Restore to background color. The area used by the
    //           graphic must be restored to the background color.
    //     3 -   Restore to previous. The decoder is required to
    //           restore the area overwritten by the graphic with
    //           what was there prior to rendering the graphic.
    //  4-7 -    To be defined.
    // NOTE(deanm): Dispose background doesn't really work, apparently most
    // browsers ignore the background palette index and clear to transparency.
    var disposal = opts.disposal === undefined ? 0 : opts.disposal;
    if (disposal < 0 || disposal > 3)  // 4-7 is reserved.
      throw new Error("Disposal out of range.");

    var use_transparency = false;
    var transparent_index = 0;
    if (opts.transparent !== undefined && opts.transparent !== null) {
      use_transparency = true;
      transparent_index = opts.transparent;
      if (transparent_index < 0 || transparent_index >= num_colors)
        throw new Error("Transparent color index.");
    }

    if (disposal !== 0 || use_transparency || delay !== 0) {
      // - Graphics Control Extension
      buf[p++] = 0x21; buf[p++] = 0xf9;  // Extension / Label.
      buf[p++] = 4;  // Byte size.

      buf[p++] = disposal << 2 | (use_transparency === true ? 1 : 0);
      buf[p++] = delay & 0xff; buf[p++] = delay >> 8 & 0xff;
      buf[p++] = transparent_index;  // Transparent color index.
      buf[p++] = 0;  // Block Terminator.
    }

    // - Image Descriptor
    buf[p++] = 0x2c;  // Image Seperator.
    buf[p++] = x & 0xff; buf[p++] = x >> 8 & 0xff;  // Left.
    buf[p++] = y & 0xff; buf[p++] = y >> 8 & 0xff;  // Top.
    buf[p++] = w & 0xff; buf[p++] = w >> 8 & 0xff;
    buf[p++] = h & 0xff; buf[p++] = h >> 8 & 0xff;
    // NOTE: No sort flag (unused?).
    // TODO(deanm): Support interlace.
    buf[p++] = using_local_palette === true ? (0x80 | (min_code_size-1)) : 0;

    // - Local Color Table
    if (using_local_palette === true) {
      for (var i = 0, il = palette.length; i < il; ++i) {
        var rgb = palette[i];
        buf[p++] = rgb >> 16 & 0xff;
        buf[p++] = rgb >> 8 & 0xff;
        buf[p++] = rgb & 0xff;
      }
    }

    p = GifWriterOutputLZWCodeStream(
            buf, p, min_code_size < 2 ? 2 : min_code_size, indexed_pixels);

    return p;
  };

  this.end = function() {
    if (ended === false) {
      buf[p++] = 0x3b;  // Trailer.
      ended = true;
    }
    return p;
  };

  this.getOutputBuffer = function() { return buf; };
  this.setOutputBuffer = function(v) { buf = v; };
  this.getOutputBufferPosition = function() { return p; };
  this.setOutputBufferPosition = function(v) { p = v; };
}

// Main compression routine, palette indexes -> LZW code stream.
// |index_stream| must have at least one entry.
function GifWriterOutputLZWCodeStream(buf, p, min_code_size, index_stream) {
  buf[p++] = min_code_size;
  var cur_subblock = p++;  // Pointing at the length field.

  var clear_code = 1 << min_code_size;
  var code_mask = clear_code - 1;
  var eoi_code = clear_code + 1;
  var next_code = eoi_code + 1;

  var cur_code_size = min_code_size + 1;  // Number of bits per code.
  var cur_shift = 0;
  // We have at most 12-bit codes, so we should have to hold a max of 19
  // bits here (and then we would write out).
  var cur = 0;

  function emit_bytes_to_buffer(bit_block_size) {
    while (cur_shift >= bit_block_size) {
      buf[p++] = cur & 0xff;
      cur >>= 8; cur_shift -= 8;
      if (p === cur_subblock + 256) {  // Finished a subblock.
        buf[cur_subblock] = 255;
        cur_subblock = p++;
      }
    }
  }

  function emit_code(c) {
    cur |= c << cur_shift;
    cur_shift += cur_code_size;
    emit_bytes_to_buffer(8);
  }

  // I am not an expert on the topic, and I don't want to write a thesis.
  // However, it is good to outline here the basic algorithm and the few data
  // structures and optimizations here that make this implementation fast.
  // The basic idea behind LZW is to build a table of previously seen runs
  // addressed by a short id (herein called output code).  All data is
  // referenced by a code, which represents one or more values from the
  // original input stream.  All input bytes can be referenced as the same
  // value as an output code.  So if you didn't want any compression, you
  // could more or less just output the original bytes as codes (there are
  // some details to this, but it is the idea).  In order to achieve
  // compression, values greater then the input range (codes can be up to
  // 12-bit while input only 8-bit) represent a sequence of previously seen
  // inputs.  The decompressor is able to build the same mapping while
  // decoding, so there is always a shared common knowledge between the
  // encoding and decoder, which is also important for "timing" aspects like
  // how to handle variable bit width code encoding.
  //
  // One obvious but very important consequence of the table system is there
  // is always a unique id (at most 12-bits) to map the runs.  'A' might be
  // 4, then 'AA' might be 10, 'AAA' 11, 'AAAA' 12, etc.  This relationship
  // can be used for an effecient lookup strategy for the code mapping.  We
  // need to know if a run has been seen before, and be able to map that run
  // to the output code.  Since we start with known unique ids (input bytes),
  // and then from those build more unique ids (table entries), we can
  // continue this chain (almost like a linked list) to always have small
  // integer values that represent the current byte chains in the encoder.
  // This means instead of tracking the input bytes (AAAABCD) to know our
  // current state, we can track the table entry for AAAABC (it is guaranteed
  // to exist by the nature of the algorithm) and the next character D.
  // Therefor the tuple of (table_entry, byte) is guaranteed to also be
  // unique.  This allows us to create a simple lookup key for mapping input
  // sequences to codes (table indices) without having to store or search
  // any of the code sequences.  So if 'AAAA' has a table entry of 12, the
  // tuple of ('AAAA', K) for any input byte K will be unique, and can be our
  // key.  This leads to a integer value at most 20-bits, which can always
  // fit in an SMI value and be used as a fast sparse array / object key.

  // Output code for the current contents of the index buffer.
  var ib_code = index_stream[0] & code_mask;  // Load first input index.
  var code_table = { };  // Key'd on our 20-bit "tuple".

  emit_code(clear_code);  // Spec says first code should be a clear code.

  // First index already loaded, process the rest of the stream.
  for (var i = 1, il = index_stream.length; i < il; ++i) {
    var k = index_stream[i] & code_mask;
    var cur_key = ib_code << 8 | k;  // (prev, k) unique tuple.
    var cur_code = code_table[cur_key];  // buffer + k.

    // Check if we have to create a new code table entry.
    if (cur_code === undefined) {  // We don't have buffer + k.
      // Emit index buffer (without k).
      // This is an inline version of emit_code, because this is the core
      // writing routine of the compressor (and V8 cannot inline emit_code
      // because it is a closure here in a different context).  Additionally
      // we can call emit_byte_to_buffer less often, because we can have
      // 30-bits (from our 31-bit signed SMI), and we know our codes will only
      // be 12-bits, so can safely have 18-bits there without overflow.
      // emit_code(ib_code);
      cur |= ib_code << cur_shift;
      cur_shift += cur_code_size;
      while (cur_shift >= 8) {
        buf[p++] = cur & 0xff;
        cur >>= 8; cur_shift -= 8;
        if (p === cur_subblock + 256) {  // Finished a subblock.
          buf[cur_subblock] = 255;
          cur_subblock = p++;
        }
      }

      if (next_code === 4096) {  // Table full, need a clear.
        emit_code(clear_code);
        next_code = eoi_code + 1;
        cur_code_size = min_code_size + 1;
        code_table = { };
      } else {  // Table not full, insert a new entry.
        // Increase our variable bit code sizes if necessary.  This is a bit
        // tricky as it is based on "timing" between the encoding and
        // decoder.  From the encoders perspective this should happen after
        // we've already emitted the index buffer and are about to create the
        // first table entry that would overflow our current code bit size.
        if (next_code >= (1 << cur_code_size)) ++cur_code_size;
        code_table[cur_key] = next_code++;  // Insert into code table.
      }

      ib_code = k;  // Index buffer to single input k.
    } else {
      ib_code = cur_code;  // Index buffer to sequence in code table.
    }
  }

  emit_code(ib_code);  // There will still be something in the index buffer.
  emit_code(eoi_code);  // End Of Information.

  // Flush / finalize the sub-blocks stream to the buffer.
  emit_bytes_to_buffer(1);

  // Finish the sub-blocks, writing out any unfinished lengths and
  // terminating with a sub-block of length 0.  If we have already started
  // but not yet used a sub-block it can just become the terminator.
  if (cur_subblock + 1 === p) {  // Started but unused.
    buf[cur_subblock] = 0;
  } else {  // Started and used, write length and additional terminator block.
    buf[cur_subblock] = p - cur_subblock - 1;
    buf[p++] = 0;
  }
  return p;
}

function GifReader(buf) {
  var p = 0;

  // - Header (GIF87a or GIF89a).
  if (buf[p++] !== 0x47 ||            buf[p++] !== 0x49 || buf[p++] !== 0x46 ||
      buf[p++] !== 0x38 || (buf[p++]+1 & 0xfd) !== 0x38 || buf[p++] !== 0x61) {
    throw new Error("Invalid GIF 87a/89a header.");
  }

  // - Logical Screen Descriptor.
  var width = buf[p++] | buf[p++] << 8;
  var height = buf[p++] | buf[p++] << 8;
  var pf0 = buf[p++];  // <Packed Fields>.
  var global_palette_flag = pf0 >> 7;
  var num_global_colors_pow2 = pf0 & 0x7;
  var num_global_colors = 1 << (num_global_colors_pow2 + 1);
  var background = buf[p++];
  buf[p++];  // Pixel aspect ratio (unused?).

  var global_palette_offset = null;
  var global_palette_size   = null;

  if (global_palette_flag) {
    global_palette_offset = p;
    global_palette_size = num_global_colors;
    p += num_global_colors * 3;  // Seek past palette.
  }

  var no_eof = true;

  var frames = [ ];

  var delay = 0;
  var transparent_index = null;
  var disposal = 0;  // 0 - No disposal specified.
  var loop_count = null;

  this.width = width;
  this.height = height;

  while (no_eof && p < buf.length) {
    switch (buf[p++]) {
      case 0x21:  // Graphics Control Extension Block
        switch (buf[p++]) {
          case 0xff:  // Application specific block
            // Try if it's a Netscape block (with animation loop counter).
            if (buf[p   ] !== 0x0b ||  // 21 FF already read, check block size.
                // NETSCAPE2.0
                buf[p+1 ] == 0x4e && buf[p+2 ] == 0x45 && buf[p+3 ] == 0x54 &&
                buf[p+4 ] == 0x53 && buf[p+5 ] == 0x43 && buf[p+6 ] == 0x41 &&
                buf[p+7 ] == 0x50 && buf[p+8 ] == 0x45 && buf[p+9 ] == 0x32 &&
                buf[p+10] == 0x2e && buf[p+11] == 0x30 &&
                // Sub-block
                buf[p+12] == 0x03 && buf[p+13] == 0x01 && buf[p+16] == 0) {
              p += 14;
              loop_count = buf[p++] | buf[p++] << 8;
              p++;  // Skip terminator.
            } else {  // We don't know what it is, just try to get past it.
              p += 12;
              while (true) {  // Seek through subblocks.
                var block_size = buf[p++];
                // Bad block size (ex: undefined from an out of bounds read).
                if (!(block_size >= 0)) throw Error("Invalid block size");
                if (block_size === 0) break;  // 0 size is terminator
                p += block_size;
              }
            }
            break;

          case 0xf9:  // Graphics Control Extension
            if (buf[p++] !== 0x4 || buf[p+4] !== 0)
              throw new Error("Invalid graphics extension block.");
            var pf1 = buf[p++];
            delay = buf[p++] | buf[p++] << 8;
            transparent_index = buf[p++];
            if ((pf1 & 1) === 0) transparent_index = null;
            disposal = pf1 >> 2 & 0x7;
            p++;  // Skip terminator.
            break;

          case 0xfe:  // Comment Extension.
            while (true) {  // Seek through subblocks.
              var block_size = buf[p++];
              // Bad block size (ex: undefined from an out of bounds read).
              if (!(block_size >= 0)) throw Error("Invalid block size");
              if (block_size === 0) break;  // 0 size is terminator
              // console.log(buf.slice(p, p+block_size).toString('ascii'));
              p += block_size;
            }
            break;

          default:
            throw new Error(
                "Unknown graphic control label: 0x" + buf[p-1].toString(16));
        }
        break;

      case 0x2c:  // Image Descriptor.
        var x = buf[p++] | buf[p++] << 8;
        var y = buf[p++] | buf[p++] << 8;
        var w = buf[p++] | buf[p++] << 8;
        var h = buf[p++] | buf[p++] << 8;
        var pf2 = buf[p++];
        var local_palette_flag = pf2 >> 7;
        var interlace_flag = pf2 >> 6 & 1;
        var num_local_colors_pow2 = pf2 & 0x7;
        var num_local_colors = 1 << (num_local_colors_pow2 + 1);
        var palette_offset = global_palette_offset;
        var palette_size = global_palette_size;
        var has_local_palette = false;
        if (local_palette_flag) {
          var has_local_palette = true;
          palette_offset = p;  // Override with local palette.
          palette_size = num_local_colors;
          p += num_local_colors * 3;  // Seek past palette.
        }

        var data_offset = p;

        p++;  // codesize
        while (true) {
          var block_size = buf[p++];
          // Bad block size (ex: undefined from an out of bounds read).
          if (!(block_size >= 0)) throw Error("Invalid block size");
          if (block_size === 0) break;  // 0 size is terminator
          p += block_size;
        }

        frames.push({x: x, y: y, width: w, height: h,
                     has_local_palette: has_local_palette,
                     palette_offset: palette_offset,
                     palette_size: palette_size,
                     data_offset: data_offset,
                     data_length: p - data_offset,
                     transparent_index: transparent_index,
                     interlaced: !!interlace_flag,
                     delay: delay,
                     disposal: disposal});
        break;

      case 0x3b:  // Trailer Marker (end of file).
        no_eof = false;
        break;

      default:
        throw new Error("Unknown gif block: 0x" + buf[p-1].toString(16));
        break;
    }
  }

  this.numFrames = function() {
    return frames.length;
  };

  this.loopCount = function() {
    return loop_count;
  };

  this.frameInfo = function(frame_num) {
    if (frame_num < 0 || frame_num >= frames.length)
      throw new Error("Frame index out of range.");
    return frames[frame_num];
  }

  this.decodeAndBlitFrameBGRA = function(frame_num, pixels) {
    var frame = this.frameInfo(frame_num);
    var num_pixels = frame.width * frame.height;
    var index_stream = new Uint8Array(num_pixels);  // At most 8-bit indices.
    GifReaderLZWOutputIndexStream(
        buf, frame.data_offset, index_stream, num_pixels);
    var palette_offset = frame.palette_offset;

    // NOTE(deanm): It seems to be much faster to compare index to 256 than
    // to === null.  Not sure why, but CompareStub_EQ_STRICT shows up high in
    // the profile, not sure if it's related to using a Uint8Array.
    var trans = frame.transparent_index;
    if (trans === null) trans = 256;

    // We are possibly just blitting to a portion of the entire frame.
    // That is a subrect within the framerect, so the additional pixels
    // must be skipped over after we finished a scanline.
    var framewidth  = frame.width;
    var framestride = width - framewidth;
    var xleft       = framewidth;  // Number of subrect pixels left in scanline.

    // Output indicies of the top left and bottom right corners of the subrect.
    var opbeg = ((frame.y * width) + frame.x) * 4;
    var opend = ((frame.y + frame.height) * width + frame.x) * 4;
    var op    = opbeg;

    var scanstride = framestride * 4;

    // Use scanstride to skip past the rows when interlacing.  This is skipping
    // 7 rows for the first two passes, then 3 then 1.
    if (frame.interlaced === true) {
      scanstride += width * 4 * 7;  // Pass 1.
    }

    var interlaceskip = 8;  // Tracking the row interval in the current pass.

    for (var i = 0, il = index_stream.length; i < il; ++i) {
      var index = index_stream[i];

      if (xleft === 0) {  // Beginning of new scan line
        op += scanstride;
        xleft = framewidth;
        if (op >= opend) { // Catch the wrap to switch passes when interlacing.
          scanstride = framestride * 4 + width * 4 * (interlaceskip-1);
          // interlaceskip / 2 * 4 is interlaceskip << 1.
          op = opbeg + (framewidth + framestride) * (interlaceskip << 1);
          interlaceskip >>= 1;
        }
      }

      if (index === trans) {
        op += 4;
      } else {
        var r = buf[palette_offset + index * 3];
        var g = buf[palette_offset + index * 3 + 1];
        var b = buf[palette_offset + index * 3 + 2];
        pixels[op++] = b;
        pixels[op++] = g;
        pixels[op++] = r;
        pixels[op++] = 255;
      }
      --xleft;
    }
  };

  // I will go to copy and paste hell one day...
  this.decodeAndBlitFrameRGBA = function(frame_num, pixels) {
    var frame = this.frameInfo(frame_num);
    var num_pixels = frame.width * frame.height;
    var index_stream = new Uint8Array(num_pixels);  // At most 8-bit indices.
    GifReaderLZWOutputIndexStream(
        buf, frame.data_offset, index_stream, num_pixels);
    var palette_offset = frame.palette_offset;

    // NOTE(deanm): It seems to be much faster to compare index to 256 than
    // to === null.  Not sure why, but CompareStub_EQ_STRICT shows up high in
    // the profile, not sure if it's related to using a Uint8Array.
    var trans = frame.transparent_index;
    if (trans === null) trans = 256;

    // We are possibly just blitting to a portion of the entire frame.
    // That is a subrect within the framerect, so the additional pixels
    // must be skipped over after we finished a scanline.
    var framewidth  = frame.width;
    var framestride = width - framewidth;
    var xleft       = framewidth;  // Number of subrect pixels left in scanline.

    // Output indicies of the top left and bottom right corners of the subrect.
    var opbeg = ((frame.y * width) + frame.x) * 4;
    var opend = ((frame.y + frame.height) * width + frame.x) * 4;
    var op    = opbeg;

    var scanstride = framestride * 4;

    // Use scanstride to skip past the rows when interlacing.  This is skipping
    // 7 rows for the first two passes, then 3 then 1.
    if (frame.interlaced === true) {
      scanstride += width * 4 * 7;  // Pass 1.
    }

    var interlaceskip = 8;  // Tracking the row interval in the current pass.

    for (var i = 0, il = index_stream.length; i < il; ++i) {
      var index = index_stream[i];

      if (xleft === 0) {  // Beginning of new scan line
        op += scanstride;
        xleft = framewidth;
        if (op >= opend) { // Catch the wrap to switch passes when interlacing.
          scanstride = framestride * 4 + width * 4 * (interlaceskip-1);
          // interlaceskip / 2 * 4 is interlaceskip << 1.
          op = opbeg + (framewidth + framestride) * (interlaceskip << 1);
          interlaceskip >>= 1;
        }
      }

      if (index === trans) {
        op += 4;
      } else {
        var r = buf[palette_offset + index * 3];
        var g = buf[palette_offset + index * 3 + 1];
        var b = buf[palette_offset + index * 3 + 2];
        pixels[op++] = r;
        pixels[op++] = g;
        pixels[op++] = b;
        pixels[op++] = 255;
      }
      --xleft;
    }
  };
}

function GifReaderLZWOutputIndexStream(code_stream, p, output, output_length) {
  var min_code_size = code_stream[p++];

  var clear_code = 1 << min_code_size;
  var eoi_code = clear_code + 1;
  var next_code = eoi_code + 1;

  var cur_code_size = min_code_size + 1;  // Number of bits per code.
  // NOTE: This shares the same name as the encoder, but has a different
  // meaning here.  Here this masks each code coming from the code stream.
  var code_mask = (1 << cur_code_size) - 1;
  var cur_shift = 0;
  var cur = 0;

  var op = 0;  // Output pointer.

  var subblock_size = code_stream[p++];

  // TODO(deanm): Would using a TypedArray be any faster?  At least it would
  // solve the fast mode / backing store uncertainty.
  // var code_table = Array(4096);
  var code_table = new Int32Array(4096);  // Can be signed, we only use 20 bits.

  var prev_code = null;  // Track code-1.

  while (true) {
    // Read up to two bytes, making sure we always 12-bits for max sized code.
    while (cur_shift < 16) {
      if (subblock_size === 0) break;  // No more data to be read.

      cur |= code_stream[p++] << cur_shift;
      cur_shift += 8;

      if (subblock_size === 1) {  // Never let it get to 0 to hold logic above.
        subblock_size = code_stream[p++];  // Next subblock.
      } else {
        --subblock_size;
      }
    }

    // TODO(deanm): We should never really get here, we should have received
    // and EOI.
    if (cur_shift < cur_code_size)
      break;

    var code = cur & code_mask;
    cur >>= cur_code_size;
    cur_shift -= cur_code_size;

    // TODO(deanm): Maybe should check that the first code was a clear code,
    // at least this is what you're supposed to do.  But actually our encoder
    // now doesn't emit a clear code first anyway.
    if (code === clear_code) {
      // We don't actually have to clear the table.  This could be a good idea
      // for greater error checking, but we don't really do any anyway.  We
      // will just track it with next_code and overwrite old entries.

      next_code = eoi_code + 1;
      cur_code_size = min_code_size + 1;
      code_mask = (1 << cur_code_size) - 1;

      // Don't update prev_code ?
      prev_code = null;
      continue;
    } else if (code === eoi_code) {
      break;
    }

    // We have a similar situation as the decoder, where we want to store
    // variable length entries (code table entries), but we want to do in a
    // faster manner than an array of arrays.  The code below stores sort of a
    // linked list within the code table, and then "chases" through it to
    // construct the dictionary entries.  When a new entry is created, just the
    // last byte is stored, and the rest (prefix) of the entry is only
    // referenced by its table entry.  Then the code chases through the
    // prefixes until it reaches a single byte code.  We have to chase twice,
    // first to compute the length, and then to actually copy the data to the
    // output (backwards, since we know the length).  The alternative would be
    // storing something in an intermediate stack, but that doesn't make any
    // more sense.  I implemented an approach where it also stored the length
    // in the code table, although it's a bit tricky because you run out of
    // bits (12 + 12 + 8), but I didn't measure much improvements (the table
    // entries are generally not the long).  Even when I created benchmarks for
    // very long table entries the complexity did not seem worth it.
    // The code table stores the prefix entry in 12 bits and then the suffix
    // byte in 8 bits, so each entry is 20 bits.

    var chase_code = code < next_code ? code : prev_code;

    // Chase what we will output, either {CODE} or {CODE-1}.
    var chase_length = 0;
    var chase = chase_code;
    while (chase > clear_code) {
      chase = code_table[chase] >> 8;
      ++chase_length;
    }

    var k = chase;

    var op_end = op + chase_length + (chase_code !== code ? 1 : 0);
    if (op_end > output_length) {
      console.log("Warning, gif stream longer than expected.");
      return;
    }

    // Already have the first byte from the chase, might as well write it fast.
    output[op++] = k;

    op += chase_length;
    var b = op;  // Track pointer, writing backwards.

    if (chase_code !== code)  // The case of emitting {CODE-1} + k.
      output[op++] = k;

    chase = chase_code;
    while (chase_length--) {
      chase = code_table[chase];
      output[--b] = chase & 0xff;  // Write backwards.
      chase >>= 8;  // Pull down to the prefix code.
    }

    if (prev_code !== null && next_code < 4096) {
      code_table[next_code++] = prev_code << 8 | k;
      // TODO(deanm): Figure out this clearing vs code growth logic better.  I
      // have an feeling that it should just happen somewhere else, for now it
      // is awkward between when we grow past the max and then hit a clear code.
      // For now just check if we hit the max 12-bits (then a clear code should
      // follow, also of course encoded in 12-bits).
      if (next_code >= code_mask+1 && cur_code_size < 12) {
        ++cur_code_size;
        code_mask = code_mask << 1 | 1;
      }
    }

    prev_code = code;
  }

  if (op !== output_length) {
    console.log("Warning, gif stream shorter than expected.");
  }

  return output;
}

// CommonJS.
try { exports.GifWriter = GifWriter; exports.GifReader = GifReader } catch(e) {}

},{}],84:[function(require,module,exports){
(function (Buffer){(function (){
"use strict";

let interlaceUtils = require("./interlace");

let pixelBppMapper = [
  // 0 - dummy entry
  function () {},

  // 1 - L
  // 0: 0, 1: 0, 2: 0, 3: 0xff
  function (pxData, data, pxPos, rawPos) {
    if (rawPos === data.length) {
      throw new Error("Ran out of data");
    }

    let pixel = data[rawPos];
    pxData[pxPos] = pixel;
    pxData[pxPos + 1] = pixel;
    pxData[pxPos + 2] = pixel;
    pxData[pxPos + 3] = 0xff;
  },

  // 2 - LA
  // 0: 0, 1: 0, 2: 0, 3: 1
  function (pxData, data, pxPos, rawPos) {
    if (rawPos + 1 >= data.length) {
      throw new Error("Ran out of data");
    }

    let pixel = data[rawPos];
    pxData[pxPos] = pixel;
    pxData[pxPos + 1] = pixel;
    pxData[pxPos + 2] = pixel;
    pxData[pxPos + 3] = data[rawPos + 1];
  },

  // 3 - RGB
  // 0: 0, 1: 1, 2: 2, 3: 0xff
  function (pxData, data, pxPos, rawPos) {
    if (rawPos + 2 >= data.length) {
      throw new Error("Ran out of data");
    }

    pxData[pxPos] = data[rawPos];
    pxData[pxPos + 1] = data[rawPos + 1];
    pxData[pxPos + 2] = data[rawPos + 2];
    pxData[pxPos + 3] = 0xff;
  },

  // 4 - RGBA
  // 0: 0, 1: 1, 2: 2, 3: 3
  function (pxData, data, pxPos, rawPos) {
    if (rawPos + 3 >= data.length) {
      throw new Error("Ran out of data");
    }

    pxData[pxPos] = data[rawPos];
    pxData[pxPos + 1] = data[rawPos + 1];
    pxData[pxPos + 2] = data[rawPos + 2];
    pxData[pxPos + 3] = data[rawPos + 3];
  },
];

let pixelBppCustomMapper = [
  // 0 - dummy entry
  function () {},

  // 1 - L
  // 0: 0, 1: 0, 2: 0, 3: 0xff
  function (pxData, pixelData, pxPos, maxBit) {
    let pixel = pixelData[0];
    pxData[pxPos] = pixel;
    pxData[pxPos + 1] = pixel;
    pxData[pxPos + 2] = pixel;
    pxData[pxPos + 3] = maxBit;
  },

  // 2 - LA
  // 0: 0, 1: 0, 2: 0, 3: 1
  function (pxData, pixelData, pxPos) {
    let pixel = pixelData[0];
    pxData[pxPos] = pixel;
    pxData[pxPos + 1] = pixel;
    pxData[pxPos + 2] = pixel;
    pxData[pxPos + 3] = pixelData[1];
  },

  // 3 - RGB
  // 0: 0, 1: 1, 2: 2, 3: 0xff
  function (pxData, pixelData, pxPos, maxBit) {
    pxData[pxPos] = pixelData[0];
    pxData[pxPos + 1] = pixelData[1];
    pxData[pxPos + 2] = pixelData[2];
    pxData[pxPos + 3] = maxBit;
  },

  // 4 - RGBA
  // 0: 0, 1: 1, 2: 2, 3: 3
  function (pxData, pixelData, pxPos) {
    pxData[pxPos] = pixelData[0];
    pxData[pxPos + 1] = pixelData[1];
    pxData[pxPos + 2] = pixelData[2];
    pxData[pxPos + 3] = pixelData[3];
  },
];

function bitRetriever(data, depth) {
  let leftOver = [];
  let i = 0;

  function split() {
    if (i === data.length) {
      throw new Error("Ran out of data");
    }
    let byte = data[i];
    i++;
    let byte8, byte7, byte6, byte5, byte4, byte3, byte2, byte1;
    switch (depth) {
      default:
        throw new Error("unrecognised depth");
      case 16:
        byte2 = data[i];
        i++;
        leftOver.push((byte << 8) + byte2);
        break;
      case 4:
        byte2 = byte & 0x0f;
        byte1 = byte >> 4;
        leftOver.push(byte1, byte2);
        break;
      case 2:
        byte4 = byte & 3;
        byte3 = (byte >> 2) & 3;
        byte2 = (byte >> 4) & 3;
        byte1 = (byte >> 6) & 3;
        leftOver.push(byte1, byte2, byte3, byte4);
        break;
      case 1:
        byte8 = byte & 1;
        byte7 = (byte >> 1) & 1;
        byte6 = (byte >> 2) & 1;
        byte5 = (byte >> 3) & 1;
        byte4 = (byte >> 4) & 1;
        byte3 = (byte >> 5) & 1;
        byte2 = (byte >> 6) & 1;
        byte1 = (byte >> 7) & 1;
        leftOver.push(byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8);
        break;
    }
  }

  return {
    get: function (count) {
      while (leftOver.length < count) {
        split();
      }
      let returner = leftOver.slice(0, count);
      leftOver = leftOver.slice(count);
      return returner;
    },
    resetAfterLine: function () {
      leftOver.length = 0;
    },
    end: function () {
      if (i !== data.length) {
        throw new Error("extra data found");
      }
    },
  };
}

function mapImage8Bit(image, pxData, getPxPos, bpp, data, rawPos) {
  // eslint-disable-line max-params
  let imageWidth = image.width;
  let imageHeight = image.height;
  let imagePass = image.index;
  for (let y = 0; y < imageHeight; y++) {
    for (let x = 0; x < imageWidth; x++) {
      let pxPos = getPxPos(x, y, imagePass);
      pixelBppMapper[bpp](pxData, data, pxPos, rawPos);
      rawPos += bpp; //eslint-disable-line no-param-reassign
    }
  }
  return rawPos;
}

function mapImageCustomBit(image, pxData, getPxPos, bpp, bits, maxBit) {
  // eslint-disable-line max-params
  let imageWidth = image.width;
  let imageHeight = image.height;
  let imagePass = image.index;
  for (let y = 0; y < imageHeight; y++) {
    for (let x = 0; x < imageWidth; x++) {
      let pixelData = bits.get(bpp);
      let pxPos = getPxPos(x, y, imagePass);
      pixelBppCustomMapper[bpp](pxData, pixelData, pxPos, maxBit);
    }
    bits.resetAfterLine();
  }
}

exports.dataToBitMap = function (data, bitmapInfo) {
  let width = bitmapInfo.width;
  let height = bitmapInfo.height;
  let depth = bitmapInfo.depth;
  let bpp = bitmapInfo.bpp;
  let interlace = bitmapInfo.interlace;
  let bits;

  if (depth !== 8) {
    bits = bitRetriever(data, depth);
  }
  let pxData;
  if (depth <= 8) {
    pxData = Buffer.alloc(width * height * 4);
  } else {
    pxData = new Uint16Array(width * height * 4);
  }
  let maxBit = Math.pow(2, depth) - 1;
  let rawPos = 0;
  let images;
  let getPxPos;

  if (interlace) {
    images = interlaceUtils.getImagePasses(width, height);
    getPxPos = interlaceUtils.getInterlaceIterator(width, height);
  } else {
    let nonInterlacedPxPos = 0;
    getPxPos = function () {
      let returner = nonInterlacedPxPos;
      nonInterlacedPxPos += 4;
      return returner;
    };
    images = [{ width: width, height: height }];
  }

  for (let imageIndex = 0; imageIndex < images.length; imageIndex++) {
    if (depth === 8) {
      rawPos = mapImage8Bit(
        images[imageIndex],
        pxData,
        getPxPos,
        bpp,
        data,
        rawPos
      );
    } else {
      mapImageCustomBit(
        images[imageIndex],
        pxData,
        getPxPos,
        bpp,
        bits,
        maxBit
      );
    }
  }
  if (depth === 8) {
    if (rawPos !== data.length) {
      throw new Error("extra data found");
    }
  } else {
    bits.end();
  }

  return pxData;
};

}).call(this)}).call(this,require("buffer").Buffer)
},{"./interlace":94,"buffer":159}],85:[function(require,module,exports){
(function (Buffer){(function (){
"use strict";

let constants = require("./constants");

module.exports = function (dataIn, width, height, options) {
  let outHasAlpha =
    [constants.COLORTYPE_COLOR_ALPHA, constants.COLORTYPE_ALPHA].indexOf(
      options.colorType
    ) !== -1;
  if (options.colorType === options.inputColorType) {
    let bigEndian = (function () {
      let buffer = new ArrayBuffer(2);
      new DataView(buffer).setInt16(0, 256, true /* littleEndian */);
      // Int16Array uses the platform's endianness.
      return new Int16Array(buffer)[0] !== 256;
    })();
    // If no need to convert to grayscale and alpha is present/absent in both, take a fast route
    if (options.bitDepth === 8 || (options.bitDepth === 16 && bigEndian)) {
      return dataIn;
    }
  }

  // map to a UInt16 array if data is 16bit, fix endianness below
  let data = options.bitDepth !== 16 ? dataIn : new Uint16Array(dataIn.buffer);

  let maxValue = 255;
  let inBpp = constants.COLORTYPE_TO_BPP_MAP[options.inputColorType];
  if (inBpp === 4 && !options.inputHasAlpha) {
    inBpp = 3;
  }
  let outBpp = constants.COLORTYPE_TO_BPP_MAP[options.colorType];
  if (options.bitDepth === 16) {
    maxValue = 65535;
    outBpp *= 2;
  }
  let outData = Buffer.alloc(width * height * outBpp);

  let inIndex = 0;
  let outIndex = 0;

  let bgColor = options.bgColor || {};
  if (bgColor.red === undefined) {
    bgColor.red = maxValue;
  }
  if (bgColor.green === undefined) {
    bgColor.green = maxValue;
  }
  if (bgColor.blue === undefined) {
    bgColor.blue = maxValue;
  }

  function getRGBA() {
    let red;
    let green;
    let blue;
    let alpha = maxValue;
    switch (options.inputColorType) {
      case constants.COLORTYPE_COLOR_ALPHA:
        alpha = data[inIndex + 3];
        red = data[inIndex];
        green = data[inIndex + 1];
        blue = data[inIndex + 2];
        break;
      case constants.COLORTYPE_COLOR:
        red = data[inIndex];
        green = data[inIndex + 1];
        blue = data[inIndex + 2];
        break;
      case constants.COLORTYPE_ALPHA:
        alpha = data[inIndex + 1];
        red = data[inIndex];
        green = red;
        blue = red;
        break;
      case constants.COLORTYPE_GRAYSCALE:
        red = data[inIndex];
        green = red;
        blue = red;
        break;
      default:
        throw new Error(
          "input color type:" +
            options.inputColorType +
            " is not supported at present"
        );
    }

    if (options.inputHasAlpha) {
      if (!outHasAlpha) {
        alpha /= maxValue;
        red = Math.min(
          Math.max(Math.round((1 - alpha) * bgColor.red + alpha * red), 0),
          maxValue
        );
        green = Math.min(
          Math.max(Math.round((1 - alpha) * bgColor.green + alpha * green), 0),
          maxValue
        );
        blue = Math.min(
          Math.max(Math.round((1 - alpha) * bgColor.blue + alpha * blue), 0),
          maxValue
        );
      }
    }
    return { red: red, green: green, blue: blue, alpha: alpha };
  }

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      let rgba = getRGBA(data, inIndex);

      switch (options.colorType) {
        case constants.COLORTYPE_COLOR_ALPHA:
        case constants.COLORTYPE_COLOR:
          if (options.bitDepth === 8) {
            outData[outIndex] = rgba.red;
            outData[outIndex + 1] = rgba.green;
            outData[outIndex + 2] = rgba.blue;
            if (outHasAlpha) {
              outData[outIndex + 3] = rgba.alpha;
            }
          } else {
            outData.writeUInt16BE(rgba.red, outIndex);
            outData.writeUInt16BE(rgba.green, outIndex + 2);
            outData.writeUInt16BE(rgba.blue, outIndex + 4);
            if (outHasAlpha) {
              outData.writeUInt16BE(rgba.alpha, outIndex + 6);
            }
          }
          break;
        case constants.COLORTYPE_ALPHA:
        case constants.COLORTYPE_GRAYSCALE: {
          // Convert to grayscale and alpha
          let grayscale = (rgba.red + rgba.green + rgba.blue) / 3;
          if (options.bitDepth === 8) {
            outData[outIndex] = grayscale;
            if (outHasAlpha) {
              outData[outIndex + 1] = rgba.alpha;
            }
          } else {
            outData.writeUInt16BE(grayscale, outIndex);
            if (outHasAlpha) {
              outData.writeUInt16BE(rgba.alpha, outIndex + 2);
            }
          }
          break;
        }
        default:
          throw new Error("unrecognised color Type " + options.colorType);
      }

      inIndex += inBpp;
      outIndex += outBpp;
    }
  }

  return outData;
};

}).call(this)}).call(this,require("buffer").Buffer)
},{"./constants":87,"buffer":159}],86:[function(require,module,exports){
(function (process,Buffer){(function (){
"use strict";

let util = require("util");
let Stream = require("stream");

let ChunkStream = (module.exports = function () {
  Stream.call(this);

  this._buffers = [];
  this._buffered = 0;

  this._reads = [];
  this._paused = false;

  this._encoding = "utf8";
  this.writable = true;
});
util.inherits(ChunkStream, Stream);

ChunkStream.prototype.read = function (length, callback) {
  this._reads.push({
    length: Math.abs(length), // if length < 0 then at most this length
    allowLess: length < 0,
    func: callback,
  });

  process.nextTick(
    function () {
      this._process();

      // its paused and there is not enought data then ask for more
      if (this._paused && this._reads && this._reads.length > 0) {
        this._paused = false;

        this.emit("drain");
      }
    }.bind(this)
  );
};

ChunkStream.prototype.write = function (data, encoding) {
  if (!this.writable) {
    this.emit("error", new Error("Stream not writable"));
    return false;
  }

  let dataBuffer;
  if (Buffer.isBuffer(data)) {
    dataBuffer = data;
  } else {
    dataBuffer = Buffer.from(data, encoding || this._encoding);
  }

  this._buffers.push(dataBuffer);
  this._buffered += dataBuffer.length;

  this._process();

  // ok if there are no more read requests
  if (this._reads && this._reads.length === 0) {
    this._paused = true;
  }

  return this.writable && !this._paused;
};

ChunkStream.prototype.end = function (data, encoding) {
  if (data) {
    this.write(data, encoding);
  }

  this.writable = false;

  // already destroyed
  if (!this._buffers) {
    return;
  }

  // enqueue or handle end
  if (this._buffers.length === 0) {
    this._end();
  } else {
    this._buffers.push(null);
    this._process();
  }
};

ChunkStream.prototype.destroySoon = ChunkStream.prototype.end;

ChunkStream.prototype._end = function () {
  if (this._reads.length > 0) {
    this.emit("error", new Error("Unexpected end of input"));
  }

  this.destroy();
};

ChunkStream.prototype.destroy = function () {
  if (!this._buffers) {
    return;
  }

  this.writable = false;
  this._reads = null;
  this._buffers = null;

  this.emit("close");
};

ChunkStream.prototype._processReadAllowingLess = function (read) {
  // ok there is any data so that we can satisfy this request
  this._reads.shift(); // == read

  // first we need to peek into first buffer
  let smallerBuf = this._buffers[0];

  // ok there is more data than we need
  if (smallerBuf.length > read.length) {
    this._buffered -= read.length;
    this._buffers[0] = smallerBuf.slice(read.length);

    read.func.call(this, smallerBuf.slice(0, read.length));
  } else {
    // ok this is less than maximum length so use it all
    this._buffered -= smallerBuf.length;
    this._buffers.shift(); // == smallerBuf

    read.func.call(this, smallerBuf);
  }
};

ChunkStream.prototype._processRead = function (read) {
  this._reads.shift(); // == read

  let pos = 0;
  let count = 0;
  let data = Buffer.alloc(read.length);

  // create buffer for all data
  while (pos < read.length) {
    let buf = this._buffers[count++];
    let len = Math.min(buf.length, read.length - pos);

    buf.copy(data, pos, 0, len);
    pos += len;

    // last buffer wasn't used all so just slice it and leave
    if (len !== buf.length) {
      this._buffers[--count] = buf.slice(len);
    }
  }

  // remove all used buffers
  if (count > 0) {
    this._buffers.splice(0, count);
  }

  this._buffered -= read.length;

  read.func.call(this, data);
};

ChunkStream.prototype._process = function () {
  try {
    // as long as there is any data and read requests
    while (this._buffered > 0 && this._reads && this._reads.length > 0) {
      let read = this._reads[0];

      // read any data (but no more than length)
      if (read.allowLess) {
        this._processReadAllowingLess(read);
      } else if (this._buffered >= read.length) {
        // ok we can meet some expectations

        this._processRead(read);
      } else {
        // not enought data to satisfy first request in queue
        // so we need to wait for more
        break;
      }
    }

    if (this._buffers && !this.writable) {
      this._end();
    }
  } catch (ex) {
    this.emit("error", ex);
  }
};

}).call(this)}).call(this,require('_process'),require("buffer").Buffer)
},{"_process":191,"buffer":159,"stream":193,"util":214}],87:[function(require,module,exports){
"use strict";

module.exports = {
  PNG_SIGNATURE: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],

  TYPE_IHDR: 0x49484452,
  TYPE_IEND: 0x49454e44,
  TYPE_IDAT: 0x49444154,
  TYPE_PLTE: 0x504c5445,
  TYPE_tRNS: 0x74524e53, // eslint-disable-line camelcase
  TYPE_gAMA: 0x67414d41, // eslint-disable-line camelcase

  // color-type bits
  COLORTYPE_GRAYSCALE: 0,
  COLORTYPE_PALETTE: 1,
  COLORTYPE_COLOR: 2,
  COLORTYPE_ALPHA: 4, // e.g. grayscale and alpha

  // color-type combinations
  COLORTYPE_PALETTE_COLOR: 3,
  COLORTYPE_COLOR_ALPHA: 6,

  COLORTYPE_TO_BPP_MAP: {
    0: 1,
    2: 3,
    3: 1,
    4: 2,
    6: 4,
  },

  GAMMA_DIVISION: 100000,
};

},{}],88:[function(require,module,exports){
"use strict";

let crcTable = [];

(function () {
  for (let i = 0; i < 256; i++) {
    let currentCrc = i;
    for (let j = 0; j < 8; j++) {
      if (currentCrc & 1) {
        currentCrc = 0xedb88320 ^ (currentCrc >>> 1);
      } else {
        currentCrc = currentCrc >>> 1;
      }
    }
    crcTable[i] = currentCrc;
  }
})();

let CrcCalculator = (module.exports = function () {
  this._crc = -1;
});

CrcCalculator.prototype.write = function (data) {
  for (let i = 0; i < data.length; i++) {
    this._crc = crcTable[(this._crc ^ data[i]) & 0xff] ^ (this._crc >>> 8);
  }
  return true;
};

CrcCalculator.prototype.crc32 = function () {
  return this._crc ^ -1;
};

CrcCalculator.crc32 = function (buf) {
  let crc = -1;
  for (let i = 0; i < buf.length; i++) {
    crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
  }
  return crc ^ -1;
};

},{}],89:[function(require,module,exports){
(function (Buffer){(function (){
"use strict";

let paethPredictor = require("./paeth-predictor");

function filterNone(pxData, pxPos, byteWidth, rawData, rawPos) {
  for (let x = 0; x < byteWidth; x++) {
    rawData[rawPos + x] = pxData[pxPos + x];
  }
}

function filterSumNone(pxData, pxPos, byteWidth) {
  let sum = 0;
  let length = pxPos + byteWidth;

  for (let i = pxPos; i < length; i++) {
    sum += Math.abs(pxData[i]);
  }
  return sum;
}

function filterSub(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
  for (let x = 0; x < byteWidth; x++) {
    let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
    let val = pxData[pxPos + x] - left;

    rawData[rawPos + x] = val;
  }
}

function filterSumSub(pxData, pxPos, byteWidth, bpp) {
  let sum = 0;
  for (let x = 0; x < byteWidth; x++) {
    let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
    let val = pxData[pxPos + x] - left;

    sum += Math.abs(val);
  }

  return sum;
}

function filterUp(pxData, pxPos, byteWidth, rawData, rawPos) {
  for (let x = 0; x < byteWidth; x++) {
    let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
    let val = pxData[pxPos + x] - up;

    rawData[rawPos + x] = val;
  }
}

function filterSumUp(pxData, pxPos, byteWidth) {
  let sum = 0;
  let length = pxPos + byteWidth;
  for (let x = pxPos; x < length; x++) {
    let up = pxPos > 0 ? pxData[x - byteWidth] : 0;
    let val = pxData[x] - up;

    sum += Math.abs(val);
  }

  return sum;
}

function filterAvg(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
  for (let x = 0; x < byteWidth; x++) {
    let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
    let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
    let val = pxData[pxPos + x] - ((left + up) >> 1);

    rawData[rawPos + x] = val;
  }
}

function filterSumAvg(pxData, pxPos, byteWidth, bpp) {
  let sum = 0;
  for (let x = 0; x < byteWidth; x++) {
    let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
    let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
    let val = pxData[pxPos + x] - ((left + up) >> 1);

    sum += Math.abs(val);
  }

  return sum;
}

function filterPaeth(pxData, pxPos, byteWidth, rawData, rawPos, bpp) {
  for (let x = 0; x < byteWidth; x++) {
    let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
    let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
    let upleft =
      pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0;
    let val = pxData[pxPos + x] - paethPredictor(left, up, upleft);

    rawData[rawPos + x] = val;
  }
}

function filterSumPaeth(pxData, pxPos, byteWidth, bpp) {
  let sum = 0;
  for (let x = 0; x < byteWidth; x++) {
    let left = x >= bpp ? pxData[pxPos + x - bpp] : 0;
    let up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0;
    let upleft =
      pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0;
    let val = pxData[pxPos + x] - paethPredictor(left, up, upleft);

    sum += Math.abs(val);
  }

  return sum;
}

let filters = {
  0: filterNone,
  1: filterSub,
  2: filterUp,
  3: filterAvg,
  4: filterPaeth,
};

let filterSums = {
  0: filterSumNone,
  1: filterSumSub,
  2: filterSumUp,
  3: filterSumAvg,
  4: filterSumPaeth,
};

module.exports = function (pxData, width, height, options, bpp) {
  let filterTypes;
  if (!("filterType" in options) || options.filterType === -1) {
    filterTypes = [0, 1, 2, 3, 4];
  } else if (typeof options.filterType === "number") {
    filterTypes = [options.filterType];
  } else {
    throw new Error("unrecognised filter types");
  }

  if (options.bitDepth === 16) {
    bpp *= 2;
  }
  let byteWidth = width * bpp;
  let rawPos = 0;
  let pxPos = 0;
  let rawData = Buffer.alloc((byteWidth + 1) * height);

  let sel = filterTypes[0];

  for (let y = 0; y < height; y++) {
    if (filterTypes.length > 1) {
      // find best filter for this line (with lowest sum of values)
      let min = Infinity;

      for (let i = 0; i < filterTypes.length; i++) {
        let sum = filterSums[filterTypes[i]](pxData, pxPos, byteWidth, bpp);
        if (sum < min) {
          sel = filterTypes[i];
          min = sum;
        }
      }
    }

    rawData[rawPos] = sel;
    rawPos++;
    filters[sel](pxData, pxPos, byteWidth, rawData, rawPos, bpp);
    rawPos += byteWidth;
    pxPos += byteWidth;
  }
  return rawData;
};

}).call(this)}).call(this,require("buffer").Buffer)
},{"./paeth-predictor":98,"buffer":159}],90:[function(require,module,exports){
(function (Buffer){(function (){
"use strict";

let util = require("util");
let ChunkStream = require("./chunkstream");
let Filter = require("./filter-parse");

let FilterAsync = (module.exports = function (bitmapInfo) {
  ChunkStream.call(this);

  let buffers = [];
  let that = this;
  this._filter = new Filter(bitmapInfo, {
    read: this.read.bind(this),
    write: function (buffer) {
      buffers.push(buffer);
    },
    complete: function () {
      that.emit("complete", Buffer.concat(buffers));
    },
  });

  this._filter.start();
});
util.inherits(FilterAsync, ChunkStream);

}).call(this)}).call(this,require("buffer").Buffer)
},{"./chunkstream":86,"./filter-parse":92,"buffer":159,"util":214}],91:[function(require,module,exports){
(function (Buffer){(function (){
"use strict";

let SyncReader = require("./sync-reader");
let Filter = require("./filter-parse");

exports.process = function (inBuffer, bitmapInfo) {
  let outBuffers = [];
  let reader = new SyncReader(inBuffer);
  let filter = new Filter(bitmapInfo, {
    read: reader.read.bind(reader),
    write: function (bufferPart) {
      outBuffers.push(bufferPart);
    },
    complete: function () {},
  });

  filter.start();
  reader.process();

  return Buffer.concat(outBuffers);
};

}).call(this)}).call(this,require("buffer").Buffer)
},{"./filter-parse":92,"./sync-reader":105,"buffer":159}],92:[function(require,module,exports){
(function (Buffer){(function (){
"use strict";

let interlaceUtils = require("./interlace");
let paethPredictor = require("./paeth-predictor");

function getByteWidth(width, bpp, depth) {
  let byteWidth = width * bpp;
  if (depth !== 8) {
    byteWidth = Math.ceil(byteWidth / (8 / depth));
  }
  return byteWidth;
}

let Filter = (module.exports = function (bitmapInfo, dependencies) {
  let width = bitmapInfo.width;
  let height = bitmapInfo.height;
  let interlace = bitmapInfo.interlace;
  let bpp = bitmapInfo.bpp;
  let depth = bitmapInfo.depth;

  this.read = dependencies.read;
  this.write = dependencies.write;
  this.complete = dependencies.complete;

  this._imageIndex = 0;
  this._images = [];
  if (interlace) {
    let passes = interlaceUtils.getImagePasses(width, height);
    for (let i = 0; i < passes.length; i++) {
      this._images.push({
        byteWidth: getByteWidth(passes[i].width, bpp, depth),
        height: passes[i].height,
        lineIndex: 0,
      });
    }
  } else {
    this._images.push({
      byteWidth: getByteWidth(width, bpp, depth),
      height: height,
      lineIndex: 0,
    });
  }

  // when filtering the line we look at the pixel to the left
  // the spec also says it is done on a byte level regardless of the number of pixels
  // so if the depth is byte compatible (8 or 16) we subtract the bpp in order to compare back
  // a pixel rather than just a different byte part. However if we are sub byte, we ignore.
  if (depth === 8) {
    this._xComparison = bpp;
  } else if (depth === 16) {
    this._xComparison = bpp * 2;
  } else {
    this._xComparison = 1;
  }
});

Filter.prototype.start = function () {
  this.read(
    this._images[this._imageIndex].byteWidth + 1,
    this._reverseFilterLine.bind(this)
  );
};

Filter.prototype._unFilterType1 = function (
  rawData,
  unfilteredLine,
  byteWidth
) {
  let xComparison = this._xComparison;
  let xBiggerThan = xComparison - 1;

  for (let x = 0; x < byteWidth; x++) {
    let rawByte = rawData[1 + x];
    let f1Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
    unfilteredLine[x] = rawByte + f1Left;
  }
};

Filter.prototype._unFilterType2 = function (
  rawData,
  unfilteredLine,
  byteWidth
) {
  let lastLine = this._lastLine;

  for (let x = 0; x < byteWidth; x++) {
    let rawByte = rawData[1 + x];
    let f2Up = lastLine ? lastLine[x] : 0;
    unfilteredLine[x] = rawByte + f2Up;
  }
};

Filter.prototype._unFilterType3 = function (
  rawData,
  unfilteredLine,
  byteWidth
) {
  let xComparison = this._xComparison;
  let xBiggerThan = xComparison - 1;
  let lastLine = this._lastLine;

  for (let x = 0; x < byteWidth; x++) {
    let rawByte = rawData[1 + x];
    let f3Up = lastLine ? lastLine[x] : 0;
    let f3Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
    let f3Add = Math.floor((f3Left + f3Up) / 2);
    unfilteredLine[x] = rawByte + f3Add;
  }
};

Filter.prototype._unFilterType4 = function (
  rawData,
  unfilteredLine,
  byteWidth
) {
  let xComparison = this._xComparison;
  let xBiggerThan = xComparison - 1;
  let lastLine = this._lastLine;

  for (let x = 0; x < byteWidth; x++) {
    let rawByte = rawData[1 + x];
    let f4Up = lastLine ? lastLine[x] : 0;
    let f4Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0;
    let f4UpLeft = x > xBiggerThan && lastLine ? lastLine[x - xComparison] : 0;
    let f4Add = paethPredictor(f4Left, f4Up, f4UpLeft);
    unfilteredLine[x] = rawByte + f4Add;
  }
};

Filter.prototype._reverseFilterLine = function (rawData) {
  let filter = rawData[0];
  let unfilteredLine;
  let currentImage = this._images[this._imageIndex];
  let byteWidth = currentImage.byteWidth;

  if (filter === 0) {
    unfilteredLine = rawData.slice(1, byteWidth + 1);
  } else {
    unfilteredLine = Buffer.alloc(byteWidth);

    switch (filter) {
      case 1:
        this._unFilterType1(rawData, unfilteredLine, byteWidth);
        break;
      case 2:
        this._unFilterType2(rawData, unfilteredLine, byteWidth);
        break;
      case 3:
        this._unFilterType3(rawData, unfilteredLine, byteWidth);
        break;
      case 4:
        this._unFilterType4(rawData, unfilteredLine, byteWidth);
        break;
      default:
        throw new Error("Unrecognised filter type - " + filter);
    }
  }

  this.write(unfilteredLine);

  currentImage.lineIndex++;
  if (currentImage.lineIndex >= currentImage.height) {
    this._lastLine = null;
    this._imageIndex++;
    currentImage = this._images[this._imageIndex];
  } else {
    this._lastLine = unfilteredLine;
  }

  if (currentImage) {
    // read, using the byte width that may be from the new current image
    this.read(currentImage.byteWidth + 1, this._reverseFilterLine.bind(this));
  } else {
    this._lastLine = null;
    this.complete();
  }
};

}).call(this)}).call(this,require("buffer").Buffer)
},{"./interlace":94,"./paeth-predictor":98,"buffer":159}],93:[function(require,module,exports){
(function (Buffer){(function (){
"use strict";

function dePalette(indata, outdata, width, height, palette) {
  let pxPos = 0;
  // use values from palette
  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      let color = palette[indata[pxPos]];

      if (!color) {
        throw new Error("index " + indata[pxPos] + " not in palette");
      }

      for (let i = 0; i < 4; i++) {
        outdata[pxPos + i] = color[i];
      }
      pxPos += 4;
    }
  }
}

function replaceTransparentColor(indata, outdata, width, height, transColor) {
  let pxPos = 0;
  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      let makeTrans = false;

      if (transColor.length === 1) {
        if (transColor[0] === indata[pxPos]) {
          makeTrans = true;
        }
      } else if (
        transColor[0] === indata[pxPos] &&
        transColor[1] === indata[pxPos + 1] &&
        transColor[2] === indata[pxPos + 2]
      ) {
        makeTrans = true;
      }
      if (makeTrans) {
        for (let i = 0; i < 4; i++) {
          outdata[pxPos + i] = 0;
        }
      }
      pxPos += 4;
    }
  }
}

function scaleDepth(indata, outdata, width, height, depth) {
  let maxOutSample = 255;
  let maxInSample = Math.pow(2, depth) - 1;
  let pxPos = 0;

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      for (let i = 0; i < 4; i++) {
        outdata[pxPos + i] = Math.floor(
          (indata[pxPos + i] * maxOutSample) / maxInSample + 0.5
        );
      }
      pxPos += 4;
    }
  }
}

module.exports = function (indata, imageData, skipRescale = false) {
  let depth = imageData.depth;
  let width = imageData.width;
  let height = imageData.height;
  let colorType = imageData.colorType;
  let transColor = imageData.transColor;
  let palette = imageData.palette;

  let outdata = indata; // only different for 16 bits

  if (colorType === 3) {
    // paletted
    dePalette(indata, outdata, width, height, palette);
  } else {
    if (transColor) {
      replaceTransparentColor(indata, outdata, width, height, transColor);
    }
    // if it needs scaling
    if (depth !== 8 && !skipRescale) {
      // if we need to change the buffer size
      if (depth === 16) {
        outdata = Buffer.alloc(width * height * 4);
      }
      scaleDepth(indata, outdata, width, height, depth);
    }
  }
  return outdata;
};

}).call(this)}).call(this,require("buffer").Buffer)
},{"buffer":159}],94:[function(require,module,exports){
"use strict";

// Adam 7
//   0 1 2 3 4 5 6 7
// 0 x 6 4 6 x 6 4 6
// 1 7 7 7 7 7 7 7 7
// 2 5 6 5 6 5 6 5 6
// 3 7 7 7 7 7 7 7 7
// 4 3 6 4 6 3 6 4 6
// 5 7 7 7 7 7 7 7 7
// 6 5 6 5 6 5 6 5 6
// 7 7 7 7 7 7 7 7 7

let imagePasses = [
  {
    // pass 1 - 1px
    x: [0],
    y: [0],
  },
  {
    // pass 2 - 1px
    x: [4],
    y: [0],
  },
  {
    // pass 3 - 2px
    x: [0, 4],
    y: [4],
  },
  {
    // pass 4 - 4px
    x: [2, 6],
    y: [0, 4],
  },
  {
    // pass 5 - 8px
    x: [0, 2, 4, 6],
    y: [2, 6],
  },
  {
    // pass 6 - 16px
    x: [1, 3, 5, 7],
    y: [0, 2, 4, 6],
  },
  {
    // pass 7 - 32px
    x: [0, 1, 2, 3, 4, 5, 6, 7],
    y: [1, 3, 5, 7],
  },
];

exports.getImagePasses = function (width, height) {
  let images = [];
  let xLeftOver = width % 8;
  let yLeftOver = height % 8;
  let xRepeats = (width - xLeftOver) / 8;
  let yRepeats = (height - yLeftOver) / 8;
  for (let i = 0; i < imagePasses.length; i++) {
    let pass = imagePasses[i];
    let passWidth = xRepeats * pass.x.length;
    let passHeight = yRepeats * pass.y.length;
    for (let j = 0; j < pass.x.length; j++) {
      if (pass.x[j] < xLeftOver) {
        passWidth++;
      } else {
        break;
      }
    }
    for (let j = 0; j < pass.y.length; j++) {
      if (pass.y[j] < yLeftOver) {
        passHeight++;
      } else {
        break;
      }
    }
    if (passWidth > 0 && passHeight > 0) {
      images.push({ width: passWidth, height: passHeight, index: i });
    }
  }
  return images;
};

exports.getInterlaceIterator = function (width) {
  return function (x, y, pass) {
    let outerXLeftOver = x % imagePasses[pass].x.length;
    let outerX =
      ((x - outerXLeftOver) / imagePasses[pass].x.length) * 8 +
      imagePasses[pass].x[outerXLeftOver];
    let outerYLeftOver = y % imagePasses[pass].y.length;
    let outerY =
      ((y - outerYLeftOver) / imagePasses[pass].y.length) * 8 +
      imagePasses[pass].y[outerYLeftOver];
    return outerX * 4 + outerY * width * 4;
  };
};

},{}],95:[function(require,module,exports){
(function (Buffer){(function (){
"use strict";

let util = require("util");
let Stream = require("stream");
let constants = require("./constants");
let Packer = require("./packer");

let PackerAsync = (module.exports = function (opt) {
  Stream.call(this);

  let options = opt || {};

  this._packer = new Packer(options);
  this._deflate = this._packer.createDeflate();

  this.readable = true;
});
util.inherits(PackerAsync, Stream);

PackerAsync.prototype.pack = function (data, width, height, gamma) {
  // Signature
  this.emit("data", Buffer.from(constants.PNG_SIGNATURE));
  this.emit("data", this._packer.packIHDR(width, height));

  if (gamma) {
    this.emit("data", this._packer.packGAMA(gamma));
  }

  let filteredData = this._packer.filterData(data, width, height);

  // compress it
  this._deflate.on("error", this.emit.bind(this, "error"));

  this._deflate.on(
    "data",
    function (compressedData) {
      this.emit("data", this._packer.packIDAT(compressedData));
    }.bind(this)
  );

  this._deflate.on(
    "end",
    function () {
      this.emit("data", this._packer.packIEND());
      this.emit("end");
    }.bind(this)
  );

  this._deflate.end(filteredData);
};

}).call(this)}).call(this,require("buffer").Buffer)
},{"./constants":87,"./packer":97,"buffer":159,"stream":193,"util":214}],96:[function(require,module,exports){
(function (Buffer){(function (){
"use strict";

let hasSyncZlib = true;
let zlib = require("zlib");
if (!zlib.deflateSync) {
  hasSyncZlib = false;
}
let constants = require("./constants");
let Packer = require("./packer");

module.exports = function (metaData, opt) {
  if (!hasSyncZlib) {
    throw new Error(
      "To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0"
    );
  }

  let options = opt || {};

  let packer = new Packer(options);

  let chunks = [];

  // Signature
  chunks.push(Buffer.from(constants.PNG_SIGNATURE));

  // Header
  chunks.push(packer.packIHDR(metaData.width, metaData.height));

  if (metaData.gamma) {
    chunks.push(packer.packGAMA(metaData.gamma));
  }

  let filteredData = packer.filterData(
    metaData.data,
    metaData.width,
    metaData.height
  );

  // compress it
  let compressedData = zlib.deflateSync(
    filteredData,
    packer.getDeflateOptions()
  );
  filteredData = null;

  if (!compressedData || !compressedData.length) {
    throw new Error("bad png - invalid compressed data response");
  }
  chunks.push(packer.packIDAT(compressedData));

  // End
  chunks.push(packer.packIEND());

  return Buffer.concat(chunks);
};

}).call(this)}).call(this,require("buffer").Buffer)
},{"./constants":87,"./packer":97,"buffer":159,"zlib":158}],97:[function(require,module,exports){
(function (Buffer){(function (){
"use strict";

let constants = require("./constants");
let CrcStream = require("./crc");
let bitPacker = require("./bitpacker");
let filter = require("./filter-pack");
let zlib = require("zlib");

let Packer = (module.exports = function (options) {
  this._options = options;

  options.deflateChunkSize = options.deflateChunkSize || 32 * 1024;
  options.deflateLevel =
    options.deflateLevel != null ? options.deflateLevel : 9;
  options.deflateStrategy =
    options.deflateStrategy != null ? options.deflateStrategy : 3;
  options.inputHasAlpha =
    options.inputHasAlpha != null ? options.inputHasAlpha : true;
  options.deflateFactory = options.deflateFactory || zlib.createDeflate;
  options.bitDepth = options.bitDepth || 8;
  // This is outputColorType
  options.colorType =
    typeof options.colorType === "number"
      ? options.colorType
      : constants.COLORTYPE_COLOR_ALPHA;
  options.inputColorType =
    typeof options.inputColorType === "number"
      ? options.inputColorType
      : constants.COLORTYPE_COLOR_ALPHA;

  if (
    [
      constants.COLORTYPE_GRAYSCALE,
      constants.COLORTYPE_COLOR,
      constants.COLORTYPE_COLOR_ALPHA,
      constants.COLORTYPE_ALPHA,
    ].indexOf(options.colorType) === -1
  ) {
    throw new Error(
      "option color type:" + options.colorType + " is not supported at present"
    );
  }
  if (
    [
      constants.COLORTYPE_GRAYSCALE,
      constants.COLORTYPE_COLOR,
      constants.COLORTYPE_COLOR_ALPHA,
      constants.COLORTYPE_ALPHA,
    ].indexOf(options.inputColorType) === -1
  ) {
    throw new Error(
      "option input color type:" +
        options.inputColorType +
        " is not supported at present"
    );
  }
  if (options.bitDepth !== 8 && options.bitDepth !== 16) {
    throw new Error(
      "option bit depth:" + options.bitDepth + " is not supported at present"
    );
  }
});

Packer.prototype.getDeflateOptions = function () {
  return {
    chunkSize: this._options.deflateChunkSize,
    level: this._options.deflateLevel,
    strategy: this._options.deflateStrategy,
  };
};

Packer.prototype.createDeflate = function () {
  return this._options.deflateFactory(this.getDeflateOptions());
};

Packer.prototype.filterData = function (data, width, height) {
  // convert to correct format for filtering (e.g. right bpp and bit depth)
  let packedData = bitPacker(data, width, height, this._options);

  // filter pixel data
  let bpp = constants.COLORTYPE_TO_BPP_MAP[this._options.colorType];
  let filteredData = filter(packedData, width, height, this._options, bpp);
  return filteredData;
};

Packer.prototype._packChunk = function (type, data) {
  let len = data ? data.length : 0;
  let buf = Buffer.alloc(len + 12);

  buf.writeUInt32BE(len, 0);
  buf.writeUInt32BE(type, 4);

  if (data) {
    data.copy(buf, 8);
  }

  buf.writeInt32BE(
    CrcStream.crc32(buf.slice(4, buf.length - 4)),
    buf.length - 4
  );
  return buf;
};

Packer.prototype.packGAMA = function (gamma) {
  let buf = Buffer.alloc(4);
  buf.writeUInt32BE(Math.floor(gamma * constants.GAMMA_DIVISION), 0);
  return this._packChunk(constants.TYPE_gAMA, buf);
};

Packer.prototype.packIHDR = function (width, height) {
  let buf = Buffer.alloc(13);
  buf.writeUInt32BE(width, 0);
  buf.writeUInt32BE(height, 4);
  buf[8] = this._options.bitDepth; // Bit depth
  buf[9] = this._options.colorType; // colorType
  buf[10] = 0; // compression
  buf[11] = 0; // filter
  buf[12] = 0; // interlace

  return this._packChunk(constants.TYPE_IHDR, buf);
};

Packer.prototype.packIDAT = function (data) {
  return this._packChunk(constants.TYPE_IDAT, data);
};

Packer.prototype.packIEND = function () {
  return this._packChunk(constants.TYPE_IEND, null);
};

}).call(this)}).call(this,require("buffer").Buffer)
},{"./bitpacker":85,"./constants":87,"./crc":88,"./filter-pack":89,"buffer":159,"zlib":158}],98:[function(require,module,exports){
"use strict";

module.exports = function paethPredictor(left, above, upLeft) {
  let paeth = left + above - upLeft;
  let pLeft = Math.abs(paeth - left);
  let pAbove = Math.abs(paeth - above);
  let pUpLeft = Math.abs(paeth - upLeft);

  if (pLeft <= pAbove && pLeft <= pUpLeft) {
    return left;
  }
  if (pAbove <= pUpLeft) {
    return above;
  }
  return upLeft;
};

},{}],99:[function(require,module,exports){
"use strict";

let util = require("util");
let zlib = require("zlib");
let ChunkStream = require("./chunkstream");
let FilterAsync = require("./filter-parse-async");
let Parser = require("./parser");
let bitmapper = require("./bitmapper");
let formatNormaliser = require("./format-normaliser");

let ParserAsync = (module.exports = function (options) {
  ChunkStream.call(this);

  this._parser = new Parser(options, {
    read: this.read.bind(this),
    error: this._handleError.bind(this),
    metadata: this._handleMetaData.bind(this),
    gamma: this.emit.bind(this, "gamma"),
    palette: this._handlePalette.bind(this),
    transColor: this._handleTransColor.bind(this),
    finished: this._finished.bind(this),
    inflateData: this._inflateData.bind(this),
    simpleTransparency: this._simpleTransparency.bind(this),
    headersFinished: this._headersFinished.bind(this),
  });
  this._options = options;
  this.writable = true;

  this._parser.start();
});
util.inherits(ParserAsync, ChunkStream);

ParserAsync.prototype._handleError = function (err) {
  this.emit("error", err);

  this.writable = false;

  this.destroy();

  if (this._inflate && this._inflate.destroy) {
    this._inflate.destroy();
  }

  if (this._filter) {
    this._filter.destroy();
    // For backward compatibility with Node 7 and below.
    // Suppress errors due to _inflate calling write() even after
    // it's destroy()'ed.
    this._filter.on("error", function () {});
  }

  this.errord = true;
};

ParserAsync.prototype._inflateData = function (data) {
  if (!this._inflate) {
    if (this._bitmapInfo.interlace) {
      this._inflate = zlib.createInflate();

      this._inflate.on("error", this.emit.bind(this, "error"));
      this._filter.on("complete", this._complete.bind(this));

      this._inflate.pipe(this._filter);
    } else {
      let rowSize =
        ((this._bitmapInfo.width *
          this._bitmapInfo.bpp *
          this._bitmapInfo.depth +
          7) >>
          3) +
        1;
      let imageSize = rowSize * this._bitmapInfo.height;
      let chunkSize = Math.max(imageSize, zlib.Z_MIN_CHUNK);

      this._inflate = zlib.createInflate({ chunkSize: chunkSize });
      let leftToInflate = imageSize;

      let emitError = this.emit.bind(this, "error");
      this._inflate.on("error", function (err) {
        if (!leftToInflate) {
          return;
        }

        emitError(err);
      });
      this._filter.on("complete", this._complete.bind(this));

      let filterWrite = this._filter.write.bind(this._filter);
      this._inflate.on("data", function (chunk) {
        if (!leftToInflate) {
          return;
        }

        if (chunk.length > leftToInflate) {
          chunk = chunk.slice(0, leftToInflate);
        }

        leftToInflate -= chunk.length;

        filterWrite(chunk);
      });

      this._inflate.on("end", this._filter.end.bind(this._filter));
    }
  }
  this._inflate.write(data);
};

ParserAsync.prototype._handleMetaData = function (metaData) {
  this._metaData = metaData;
  this._bitmapInfo = Object.create(metaData);

  this._filter = new FilterAsync(this._bitmapInfo);
};

ParserAsync.prototype._handleTransColor = function (transColor) {
  this._bitmapInfo.transColor = transColor;
};

ParserAsync.prototype._handlePalette = function (palette) {
  this._bitmapInfo.palette = palette;
};

ParserAsync.prototype._simpleTransparency = function () {
  this._metaData.alpha = true;
};

ParserAsync.prototype._headersFinished = function () {
  // Up until this point, we don't know if we have a tRNS chunk (alpha)
  // so we can't emit metadata any earlier
  this.emit("metadata", this._metaData);
};

ParserAsync.prototype._finished = function () {
  if (this.errord) {
    return;
  }

  if (!this._inflate) {
    this.emit("error", "No Inflate block");
  } else {
    // no more data to inflate
    this._inflate.end();
  }
};

ParserAsync.prototype._complete = function (filteredData) {
  if (this.errord) {
    return;
  }

  let normalisedBitmapData;

  try {
    let bitmapData = bitmapper.dataToBitMap(filteredData, this._bitmapInfo);

    normalisedBitmapData = formatNormaliser(
      bitmapData,
      this._bitmapInfo,
      this._options.skipRescale
    );
    bitmapData = null;
  } catch (ex) {
    this._handleError(ex);
    return;
  }

  this.emit("parsed", normalisedBitmapData);
};

},{"./bitmapper":84,"./chunkstream":86,"./filter-parse-async":90,"./format-normaliser":93,"./parser":101,"util":214,"zlib":158}],100:[function(require,module,exports){
(function (Buffer){(function (){
"use strict";

let hasSyncZlib = true;
let zlib = require("zlib");
let inflateSync = require("./sync-inflate");
if (!zlib.deflateSync) {
  hasSyncZlib = false;
}
let SyncReader = require("./sync-reader");
let FilterSync = require("./filter-parse-sync");
let Parser = require("./parser");
let bitmapper = require("./bitmapper");
let formatNormaliser = require("./format-normaliser");

module.exports = function (buffer, options) {
  if (!hasSyncZlib) {
    throw new Error(
      "To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0"
    );
  }

  let err;
  function handleError(_err_) {
    err = _err_;
  }

  let metaData;
  function handleMetaData(_metaData_) {
    metaData = _metaData_;
  }

  function handleTransColor(transColor) {
    metaData.transColor = transColor;
  }

  function handlePalette(palette) {
    metaData.palette = palette;
  }

  function handleSimpleTransparency() {
    metaData.alpha = true;
  }

  let gamma;
  function handleGamma(_gamma_) {
    gamma = _gamma_;
  }

  let inflateDataList = [];
  function handleInflateData(inflatedData) {
    inflateDataList.push(inflatedData);
  }

  let reader = new SyncReader(buffer);

  let parser = new Parser(options, {
    read: reader.read.bind(reader),
    error: handleError,
    metadata: handleMetaData,
    gamma: handleGamma,
    palette: handlePalette,
    transColor: handleTransColor,
    inflateData: handleInflateData,
    simpleTransparency: handleSimpleTransparency,
  });

  parser.start();
  reader.process();

  if (err) {
    throw err;
  }

  //join together the inflate datas
  let inflateData = Buffer.concat(inflateDataList);
  inflateDataList.length = 0;

  let inflatedData;
  if (metaData.interlace) {
    inflatedData = zlib.inflateSync(inflateData);
  } else {
    let rowSize =
      ((metaData.width * metaData.bpp * metaData.depth + 7) >> 3) + 1;
    let imageSize = rowSize * metaData.height;
    inflatedData = inflateSync(inflateData, {
      chunkSize: imageSize,
      maxLength: imageSize,
    });
  }
  inflateData = null;

  if (!inflatedData || !inflatedData.length) {
    throw new Error("bad png - invalid inflate data response");
  }

  let unfilteredData = FilterSync.process(inflatedData, metaData);
  inflateData = null;

  let bitmapData = bitmapper.dataToBitMap(unfilteredData, metaData);
  unfilteredData = null;

  let normalisedBitmapData = formatNormaliser(
    bitmapData,
    metaData,
    options.skipRescale
  );

  metaData.data = normalisedBitmapData;
  metaData.gamma = gamma || 0;

  return metaData;
};

}).call(this)}).call(this,require("buffer").Buffer)
},{"./bitmapper":84,"./filter-parse-sync":91,"./format-normaliser":93,"./parser":101,"./sync-inflate":104,"./sync-reader":105,"buffer":159,"zlib":158}],101:[function(require,module,exports){
(function (Buffer){(function (){
"use strict";

let constants = require("./constants");
let CrcCalculator = require("./crc");

let Parser = (module.exports = function (options, dependencies) {
  this._options = options;
  options.checkCRC = options.checkCRC !== false;

  this._hasIHDR = false;
  this._hasIEND = false;
  this._emittedHeadersFinished = false;

  // input flags/metadata
  this._palette = [];
  this._colorType = 0;

  this._chunks = {};
  this._chunks[constants.TYPE_IHDR] = this._handleIHDR.bind(this);
  this._chunks[constants.TYPE_IEND] = this._handleIEND.bind(this);
  this._chunks[constants.TYPE_IDAT] = this._handleIDAT.bind(this);
  this._chunks[constants.TYPE_PLTE] = this._handlePLTE.bind(this);
  this._chunks[constants.TYPE_tRNS] = this._handleTRNS.bind(this);
  this._chunks[constants.TYPE_gAMA] = this._handleGAMA.bind(this);

  this.read = dependencies.read;
  this.error = dependencies.error;
  this.metadata = dependencies.metadata;
  this.gamma = dependencies.gamma;
  this.transColor = dependencies.transColor;
  this.palette = dependencies.palette;
  this.parsed = dependencies.parsed;
  this.inflateData = dependencies.inflateData;
  this.finished = dependencies.finished;
  this.simpleTransparency = dependencies.simpleTransparency;
  this.headersFinished = dependencies.headersFinished || function () {};
});

Parser.prototype.start = function () {
  this.read(constants.PNG_SIGNATURE.length, this._parseSignature.bind(this));
};

Parser.prototype._parseSignature = function (data) {
  let signature = constants.PNG_SIGNATURE;

  for (let i = 0; i < signature.length; i++) {
    if (data[i] !== signature[i]) {
      this.error(new Error("Invalid file signature"));
      return;
    }
  }
  this.read(8, this._parseChunkBegin.bind(this));
};

Parser.prototype._parseChunkBegin = function (data) {
  // chunk content length
  let length = data.readUInt32BE(0);

  // chunk type
  let type = data.readUInt32BE(4);
  let name = "";
  for (let i = 4; i < 8; i++) {
    name += String.fromCharCode(data[i]);
  }

  //console.log('chunk ', name, length);

  // chunk flags
  let ancillary = Boolean(data[4] & 0x20); // or critical
  //    priv = Boolean(data[5] & 0x20), // or public
  //    safeToCopy = Boolean(data[7] & 0x20); // or unsafe

  if (!this._hasIHDR && type !== constants.TYPE_IHDR) {
    this.error(new Error("Expected IHDR on beggining"));
    return;
  }

  this._crc = new CrcCalculator();
  this._crc.write(Buffer.from(name));

  if (this._chunks[type]) {
    return this._chunks[type](length);
  }

  if (!ancillary) {
    this.error(new Error("Unsupported critical chunk type " + name));
    return;
  }

  this.read(length + 4, this._skipChunk.bind(this));
};

Parser.prototype._skipChunk = function (/*data*/) {
  this.read(8, this._parseChunkBegin.bind(this));
};

Parser.prototype._handleChunkEnd = function () {
  this.read(4, this._parseChunkEnd.bind(this));
};

Parser.prototype._parseChunkEnd = function (data) {
  let fileCrc = data.readInt32BE(0);
  let calcCrc = this._crc.crc32();

  // check CRC
  if (this._options.checkCRC && calcCrc !== fileCrc) {
    this.error(new Error("Crc error - " + fileCrc + " - " + calcCrc));
    return;
  }

  if (!this._hasIEND) {
    this.read(8, this._parseChunkBegin.bind(this));
  }
};

Parser.prototype._handleIHDR = function (length) {
  this.read(length, this._parseIHDR.bind(this));
};
Parser.prototype._parseIHDR = function (data) {
  this._crc.write(data);

  let width = data.readUInt32BE(0);
  let height = data.readUInt32BE(4);
  let depth = data[8];
  let colorType = data[9]; // bits: 1 palette, 2 color, 4 alpha
  let compr = data[10];
  let filter = data[11];
  let interlace = data[12];

  // console.log('    width', width, 'height', height,
  //     'depth', depth, 'colorType', colorType,
  //     'compr', compr, 'filter', filter, 'interlace', interlace
  // );

  if (
    depth !== 8 &&
    depth !== 4 &&
    depth !== 2 &&
    depth !== 1 &&
    depth !== 16
  ) {
    this.error(new Error("Unsupported bit depth " + depth));
    return;
  }
  if (!(colorType in constants.COLORTYPE_TO_BPP_MAP)) {
    this.error(new Error("Unsupported color type"));
    return;
  }
  if (compr !== 0) {
    this.error(new Error("Unsupported compression method"));
    return;
  }
  if (filter !== 0) {
    this.error(new Error("Unsupported filter method"));
    return;
  }
  if (interlace !== 0 && interlace !== 1) {
    this.error(new Error("Unsupported interlace method"));
    return;
  }

  this._colorType = colorType;

  let bpp = constants.COLORTYPE_TO_BPP_MAP[this._colorType];

  this._hasIHDR = true;

  this.metadata({
    width: width,
    height: height,
    depth: depth,
    interlace: Boolean(interlace),
    palette: Boolean(colorType & constants.COLORTYPE_PALETTE),
    color: Boolean(colorType & constants.COLORTYPE_COLOR),
    alpha: Boolean(colorType & constants.COLORTYPE_ALPHA),
    bpp: bpp,
    colorType: colorType,
  });

  this._handleChunkEnd();
};

Parser.prototype._handlePLTE = function (length) {
  this.read(length, this._parsePLTE.bind(this));
};
Parser.prototype._parsePLTE = function (data) {
  this._crc.write(data);

  let entries = Math.floor(data.length / 3);
  // console.log('Palette:', entries);

  for (let i = 0; i < entries; i++) {
    this._palette.push([data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 0xff]);
  }

  this.palette(this._palette);

  this._handleChunkEnd();
};

Parser.prototype._handleTRNS = function (length) {
  this.simpleTransparency();
  this.read(length, this._parseTRNS.bind(this));
};
Parser.prototype._parseTRNS = function (data) {
  this._crc.write(data);

  // palette
  if (this._colorType === constants.COLORTYPE_PALETTE_COLOR) {
    if (this._palette.length === 0) {
      this.error(new Error("Transparency chunk must be after palette"));
      return;
    }
    if (data.length > this._palette.length) {
      this.error(new Error("More transparent colors than palette size"));
      return;
    }
    for (let i = 0; i < data.length; i++) {
      this._palette[i][3] = data[i];
    }
    this.palette(this._palette);
  }

  // for colorType 0 (grayscale) and 2 (rgb)
  // there might be one gray/color defined as transparent
  if (this._colorType === constants.COLORTYPE_GRAYSCALE) {
    // grey, 2 bytes
    this.transColor([data.readUInt16BE(0)]);
  }
  if (this._colorType === constants.COLORTYPE_COLOR) {
    this.transColor([
      data.readUInt16BE(0),
      data.readUInt16BE(2),
      data.readUInt16BE(4),
    ]);
  }

  this._handleChunkEnd();
};

Parser.prototype._handleGAMA = function (length) {
  this.read(length, this._parseGAMA.bind(this));
};
Parser.prototype._parseGAMA = function (data) {
  this._crc.write(data);
  this.gamma(data.readUInt32BE(0) / constants.GAMMA_DIVISION);

  this._handleChunkEnd();
};

Parser.prototype._handleIDAT = function (length) {
  if (!this._emittedHeadersFinished) {
    this._emittedHeadersFinished = true;
    this.headersFinished();
  }
  this.read(-length, this._parseIDAT.bind(this, length));
};
Parser.prototype._parseIDAT = function (length, data) {
  this._crc.write(data);

  if (
    this._colorType === constants.COLORTYPE_PALETTE_COLOR &&
    this._palette.length === 0
  ) {
    throw new Error("Expected palette not found");
  }

  this.inflateData(data);
  let leftOverLength = length - data.length;

  if (leftOverLength > 0) {
    this._handleIDAT(leftOverLength);
  } else {
    this._handleChunkEnd();
  }
};

Parser.prototype._handleIEND = function (length) {
  this.read(length, this._parseIEND.bind(this));
};
Parser.prototype._parseIEND = function (data) {
  this._crc.write(data);

  this._hasIEND = true;
  this._handleChunkEnd();

  if (this.finished) {
    this.finished();
  }
};

}).call(this)}).call(this,require("buffer").Buffer)
},{"./constants":87,"./crc":88,"buffer":159}],102:[function(require,module,exports){
"use strict";

let parse = require("./parser-sync");
let pack = require("./packer-sync");

exports.read = function (buffer, options) {
  return parse(buffer, options || {});
};

exports.write = function (png, options) {
  return pack(png, options);
};

},{"./packer-sync":96,"./parser-sync":100}],103:[function(require,module,exports){
(function (process,Buffer){(function (){
"use strict";

let util = require("util");
let Stream = require("stream");
let Parser = require("./parser-async");
let Packer = require("./packer-async");
let PNGSync = require("./png-sync");

let PNG = (exports.PNG = function (options) {
  Stream.call(this);

  options = options || {}; // eslint-disable-line no-param-reassign

  // coerce pixel dimensions to integers (also coerces undefined -> 0):
  this.width = options.width | 0;
  this.height = options.height | 0;

  this.data =
    this.width > 0 && this.height > 0
      ? Buffer.alloc(4 * this.width * this.height)
      : null;

  if (options.fill && this.data) {
    this.data.fill(0);
  }

  this.gamma = 0;
  this.readable = this.writable = true;

  this._parser = new Parser(options);

  this._parser.on("error", this.emit.bind(this, "error"));
  this._parser.on("close", this._handleClose.bind(this));
  this._parser.on("metadata", this._metadata.bind(this));
  this._parser.on("gamma", this._gamma.bind(this));
  this._parser.on(
    "parsed",
    function (data) {
      this.data = data;
      this.emit("parsed", data);
    }.bind(this)
  );

  this._packer = new Packer(options);
  this._packer.on("data", this.emit.bind(this, "data"));
  this._packer.on("end", this.emit.bind(this, "end"));
  this._parser.on("close", this._handleClose.bind(this));
  this._packer.on("error", this.emit.bind(this, "error"));
});
util.inherits(PNG, Stream);

PNG.sync = PNGSync;

PNG.prototype.pack = function () {
  if (!this.data || !this.data.length) {
    this.emit("error", "No data provided");
    return this;
  }

  process.nextTick(
    function () {
      this._packer.pack(this.data, this.width, this.height, this.gamma);
    }.bind(this)
  );

  return this;
};

PNG.prototype.parse = function (data, callback) {
  if (callback) {
    let onParsed, onError;

    onParsed = function (parsedData) {
      this.removeListener("error", onError);

      this.data = parsedData;
      callback(null, this);
    }.bind(this);

    onError = function (err) {
      this.removeListener("parsed", onParsed);

      callback(err, null);
    }.bind(this);

    this.once("parsed", onParsed);
    this.once("error", onError);
  }

  this.end(data);
  return this;
};

PNG.prototype.write = function (data) {
  this._parser.write(data);
  return true;
};

PNG.prototype.end = function (data) {
  this._parser.end(data);
};

PNG.prototype._metadata = function (metadata) {
  this.width = metadata.width;
  this.height = metadata.height;

  this.emit("metadata", metadata);
};

PNG.prototype._gamma = function (gamma) {
  this.gamma = gamma;
};

PNG.prototype._handleClose = function () {
  if (!this._parser.writable && !this._packer.readable) {
    this.emit("close");
  }
};

PNG.bitblt = function (src, dst, srcX, srcY, width, height, deltaX, deltaY) {
  // eslint-disable-line max-params
  // coerce pixel dimensions to integers (also coerces undefined -> 0):
  /* eslint-disable no-param-reassign */
  srcX |= 0;
  srcY |= 0;
  width |= 0;
  height |= 0;
  deltaX |= 0;
  deltaY |= 0;
  /* eslint-enable no-param-reassign */

  if (
    srcX > src.width ||
    srcY > src.height ||
    srcX + width > src.width ||
    srcY + height > src.height
  ) {
    throw new Error("bitblt reading outside image");
  }

  if (
    deltaX > dst.width ||
    deltaY > dst.height ||
    deltaX + width > dst.width ||
    deltaY + height > dst.height
  ) {
    throw new Error("bitblt writing outside image");
  }

  for (let y = 0; y < height; y++) {
    src.data.copy(
      dst.data,
      ((deltaY + y) * dst.width + deltaX) << 2,
      ((srcY + y) * src.width + srcX) << 2,
      ((srcY + y) * src.width + srcX + width) << 2
    );
  }
};

PNG.prototype.bitblt = function (
  dst,
  srcX,
  srcY,
  width,
  height,
  deltaX,
  deltaY
) {
  // eslint-disable-line max-params

  PNG.bitblt(this, dst, srcX, srcY, width, height, deltaX, deltaY);
  return this;
};

PNG.adjustGamma = function (src) {
  if (src.gamma) {
    for (let y = 0; y < src.height; y++) {
      for (let x = 0; x < src.width; x++) {
        let idx = (src.width * y + x) << 2;

        for (let i = 0; i < 3; i++) {
          let sample = src.data[idx + i] / 255;
          sample = Math.pow(sample, 1 / 2.2 / src.gamma);
          src.data[idx + i] = Math.round(sample * 255);
        }
      }
    }
    src.gamma = 0;
  }
};

PNG.prototype.adjustGamma = function () {
  PNG.adjustGamma(this);
};

}).call(this)}).call(this,require('_process'),require("buffer").Buffer)
},{"./packer-async":95,"./parser-async":99,"./png-sync":102,"_process":191,"buffer":159,"stream":193,"util":214}],104:[function(require,module,exports){
(function (process,Buffer){(function (){
"use strict";

let assert = require("assert").ok;
let zlib = require("zlib");
let util = require("util");

let kMaxLength = require("buffer").kMaxLength;

function Inflate(opts) {
  if (!(this instanceof Inflate)) {
    return new Inflate(opts);
  }

  if (opts && opts.chunkSize < zlib.Z_MIN_CHUNK) {
    opts.chunkSize = zlib.Z_MIN_CHUNK;
  }

  zlib.Inflate.call(this, opts);

  // Node 8 --> 9 compatibility check
  this._offset = this._offset === undefined ? this._outOffset : this._offset;
  this._buffer = this._buffer || this._outBuffer;

  if (opts && opts.maxLength != null) {
    this._maxLength = opts.maxLength;
  }
}

function createInflate(opts) {
  return new Inflate(opts);
}

function _close(engine, callback) {
  if (callback) {
    process.nextTick(callback);
  }

  // Caller may invoke .close after a zlib error (which will null _handle).
  if (!engine._handle) {
    return;
  }

  engine._handle.close();
  engine._handle = null;
}

Inflate.prototype._processChunk = function (chunk, flushFlag, asyncCb) {
  if (typeof asyncCb === "function") {
    return zlib.Inflate._processChunk.call(this, chunk, flushFlag, asyncCb);
  }

  let self = this;

  let availInBefore = chunk && chunk.length;
  let availOutBefore = this._chunkSize - this._offset;
  let leftToInflate = this._maxLength;
  let inOff = 0;

  let buffers = [];
  let nread = 0;

  let error;
  this.on("error", function (err) {
    error = err;
  });

  function handleChunk(availInAfter, availOutAfter) {
    if (self._hadError) {
      return;
    }

    let have = availOutBefore - availOutAfter;
    assert(have >= 0, "have should not go down");

    if (have > 0) {
      let out = self._buffer.slice(self._offset, self._offset + have);
      self._offset += have;

      if (out.length > leftToInflate) {
        out = out.slice(0, leftToInflate);
      }

      buffers.push(out);
      nread += out.length;
      leftToInflate -= out.length;

      if (leftToInflate === 0) {
        return false;
      }
    }

    if (availOutAfter === 0 || self._offset >= self._chunkSize) {
      availOutBefore = self._chunkSize;
      self._offset = 0;
      self._buffer = Buffer.allocUnsafe(self._chunkSize);
    }

    if (availOutAfter === 0) {
      inOff += availInBefore - availInAfter;
      availInBefore = availInAfter;

      return true;
    }

    return false;
  }

  assert(this._handle, "zlib binding closed");
  let res;
  do {
    res = this._handle.writeSync(
      flushFlag,
      chunk, // in
      inOff, // in_off
      availInBefore, // in_len
      this._buffer, // out
      this._offset, //out_off
      availOutBefore
    ); // out_len
    // Node 8 --> 9 compatibility check
    res = res || this._writeState;
  } while (!this._hadError && handleChunk(res[0], res[1]));

  if (this._hadError) {
    throw error;
  }

  if (nread >= kMaxLength) {
    _close(this);
    throw new RangeError(
      "Cannot create final Buffer. It would be larger than 0x" +
        kMaxLength.toString(16) +
        " bytes"
    );
  }

  let buf = Buffer.concat(buffers, nread);
  _close(this);

  return buf;
};

util.inherits(Inflate, zlib.Inflate);

function zlibBufferSync(engine, buffer) {
  if (typeof buffer === "string") {
    buffer = Buffer.from(buffer);
  }
  if (!(buffer instanceof Buffer)) {
    throw new TypeError("Not a string or buffer");
  }

  let flushFlag = engine._finishFlushFlag;
  if (flushFlag == null) {
    flushFlag = zlib.Z_FINISH;
  }

  return engine._processChunk(buffer, flushFlag);
}

function inflateSync(buffer, opts) {
  return zlibBufferSync(new Inflate(opts), buffer);
}

module.exports = exports = inflateSync;
exports.Inflate = Inflate;
exports.createInflate = createInflate;
exports.inflateSync = inflateSync;

}).call(this)}).call(this,require('_process'),require("buffer").Buffer)
},{"_process":191,"assert":150,"buffer":159,"util":214,"zlib":158}],105:[function(require,module,exports){
"use strict";

let SyncReader = (module.exports = function (buffer) {
  this._buffer = buffer;
  this._reads = [];
});

SyncReader.prototype.read = function (length, callback) {
  this._reads.push({
    length: Math.abs(length), // if length < 0 then at most this length
    allowLess: length < 0,
    func: callback,
  });
};

SyncReader.prototype.process = function () {
  // as long as there is any data and read requests
  while (this._reads.length > 0 && this._buffer.length) {
    let read = this._reads[0];

    if (
      this._buffer.length &&
      (this._buffer.length >= read.length || read.allowLess)
    ) {
      // ok there is any data so that we can satisfy this request
      this._reads.shift(); // == read

      let buf = this._buffer;

      this._buffer = buf.slice(read.length);

      read.func.call(this, buf.slice(0, read.length));
    } else {
      break;
    }
  }

  if (this._reads.length > 0) {
    throw new Error("There are some read requests waitng on finished stream");
  }

  if (this._buffer.length > 0) {
    throw new Error("unrecognised content at end of stream");
  }
};

},{}],106:[function(require,module,exports){
(function (process,global){(function (){
(function (global, undefined) {
    "use strict";

    if (global.setImmediate) {
        return;
    }

    var nextHandle = 1; // Spec says greater than zero
    var tasksByHandle = {};
    var currentlyRunningATask = false;
    var doc = global.document;
    var registerImmediate;

    function setImmediate(callback) {
      // Callback can either be a function or a string
      if (typeof callback !== "function") {
        callback = new Function("" + callback);
      }
      // Copy function arguments
      var args = new Array(arguments.length - 1);
      for (var i = 0; i < args.length; i++) {
          args[i] = arguments[i + 1];
      }
      // Store and register the task
      var task = { callback: callback, args: args };
      tasksByHandle[nextHandle] = task;
      registerImmediate(nextHandle);
      return nextHandle++;
    }

    function clearImmediate(handle) {
        delete tasksByHandle[handle];
    }

    function run(task) {
        var callback = task.callback;
        var args = task.args;
        switch (args.length) {
        case 0:
            callback();
            break;
        case 1:
            callback(args[0]);
            break;
        case 2:
            callback(args[0], args[1]);
            break;
        case 3:
            callback(args[0], args[1], args[2]);
            break;
        default:
            callback.apply(undefined, args);
            break;
        }
    }

    function runIfPresent(handle) {
        // From the spec: "Wait until any invocations of this algorithm started before this one have completed."
        // So if we're currently running a task, we'll need to delay this invocation.
        if (currentlyRunningATask) {
            // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
            // "too much recursion" error.
            setTimeout(runIfPresent, 0, handle);
        } else {
            var task = tasksByHandle[handle];
            if (task) {
                currentlyRunningATask = true;
                try {
                    run(task);
                } finally {
                    clearImmediate(handle);
                    currentlyRunningATask = false;
                }
            }
        }
    }

    function installNextTickImplementation() {
        registerImmediate = function(handle) {
            process.nextTick(function () { runIfPresent(handle); });
        };
    }

    function canUsePostMessage() {
        // The test against `importScripts` prevents this implementation from being installed inside a web worker,
        // where `global.postMessage` means something completely different and can't be used for this purpose.
        if (global.postMessage && !global.importScripts) {
            var postMessageIsAsynchronous = true;
            var oldOnMessage = global.onmessage;
            global.onmessage = function() {
                postMessageIsAsynchronous = false;
            };
            global.postMessage("", "*");
            global.onmessage = oldOnMessage;
            return postMessageIsAsynchronous;
        }
    }

    function installPostMessageImplementation() {
        // Installs an event handler on `global` for the `message` event: see
        // * https://developer.mozilla.org/en/DOM/window.postMessage
        // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages

        var messagePrefix = "setImmediate$" + Math.random() + "$";
        var onGlobalMessage = function(event) {
            if (event.source === global &&
                typeof event.data === "string" &&
                event.data.indexOf(messagePrefix) === 0) {
                runIfPresent(+event.data.slice(messagePrefix.length));
            }
        };

        if (global.addEventListener) {
            global.addEventListener("message", onGlobalMessage, false);
        } else {
            global.attachEvent("onmessage", onGlobalMessage);
        }

        registerImmediate = function(handle) {
            global.postMessage(messagePrefix + handle, "*");
        };
    }

    function installMessageChannelImplementation() {
        var channel = new MessageChannel();
        channel.port1.onmessage = function(event) {
            var handle = event.data;
            runIfPresent(handle);
        };

        registerImmediate = function(handle) {
            channel.port2.postMessage(handle);
        };
    }

    function installReadyStateChangeImplementation() {
        var html = doc.documentElement;
        registerImmediate = function(handle) {
            // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
            // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
            var script = doc.createElement("script");
            script.onreadystatechange = function () {
                runIfPresent(handle);
                script.onreadystatechange = null;
                html.removeChild(script);
                script = null;
            };
            html.appendChild(script);
        };
    }

    function installSetTimeoutImplementation() {
        registerImmediate = function(handle) {
            setTimeout(runIfPresent, 0, handle);
        };
    }

    // If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
    var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);
    attachTo = attachTo && attachTo.setTimeout ? attachTo : global;

    // Don't get fooled by e.g. browserify environments.
    if ({}.toString.call(global.process) === "[object process]") {
        // For Node.js before 0.9
        installNextTickImplementation();

    } else if (canUsePostMessage()) {
        // For non-IE10 modern browsers
        installPostMessageImplementation();

    } else if (global.MessageChannel) {
        // For web workers, where supported
        installMessageChannelImplementation();

    } else if (doc && "onreadystatechange" in doc.createElement("script")) {
        // For IE 6–8
        installReadyStateChangeImplementation();

    } else {
        // For older browsers
        installSetTimeoutImplementation();
    }

    attachTo.setImmediate = setImmediate;
    attachTo.clearImmediate = clearImmediate;
}(typeof self === "undefined" ? typeof global === "undefined" ? this : global : self));

}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"_process":191}],107:[function(require,module,exports){
/*
	Seventh

	Copyright (c) 2017 - 2020 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Promise = require( './seventh.js' ) ;



function Queue( jobRunner , concurrency = 4 ) {
	this.jobRunner = jobRunner ;
	this.jobs = new Map() ;			// all jobs
	this.pendingJobs = new Map() ;	// only pending jobs (not run)
	this.runningJobs = new Map() ;	// only running jobs (not done)
	this.errorJobs = new Map() ;	// jobs that have failed
	this.jobsDone = new Map() ;		// jobs that finished successfully
	this.concurrency = + concurrency || 1 ;

	// Internal
	this.isQueueRunning = true ;
	this.isLoopRunning = false ;
	this.canLoopAgain = false ;
	this.ready = Promise.resolved ;

	// Misc
	this.startTime = null ;		// timestamp at the first time the loop is run
	this.endTime = null ;		// timestamp at the last time the loop exited

	// External API, resolved when there is no jobs anymore in the queue, a new Promise is created when new element are injected
	this.drained = Promise.resolved ;

	// External API, resolved when the Queue has nothing to do: either it's drained or the pending jobs have dependencies that cannot be solved
	this.idle = Promise.resolved ;
}

Promise.Queue = Queue ;



function Job( id , dependencies = null , data = undefined ) {
	this.id = id ;
	this.dependencies = dependencies === null ? null : [ ... dependencies ] ;
	this.data = data === undefined ? id : data ;
	this.error = null ;
	this.startTime = null ;
	this.endTime = null ;
}

Queue.Job = Job ;



Queue.prototype.setConcurrency = function( concurrency ) { this.concurrency = + concurrency || 1 ; } ;
Queue.prototype.stop = Queue.prototype.pause = function() { this.isQueueRunning = false ; } ;
Queue.prototype.has = function( id ) { return this.jobs.has( id ) ; } ;



Queue.prototype.add = Queue.prototype.addJob = function( id , data , dependencies = null ) {
	// Don't add it twice!
	if ( this.jobs.has( id ) ) { return false ; }

	var job = new Job( id , dependencies , data ) ;
	this.jobs.set( id , job ) ;
	this.pendingJobs.set( id , job ) ;
	this.canLoopAgain = true ;
	if ( this.isQueueRunning && ! this.isLoopRunning ) { this.run() ; }
	if ( this.drained.isSettled() ) { this.drained = new Promise() ; }
	return job ;
} ;



// Add a batch of jobs, with only id (data=id) and no dependencies
Queue.prototype.addBatch = Queue.prototype.addJobBatch = function( ids ) {
	var id , job ;

	for ( id of ids ) {
		// Don't add it twice!
		if ( this.jobs.has( id ) ) { return false ; }
		job = new Job( id ) ;
		this.jobs.set( id , job ) ;
		this.pendingJobs.set( id , job ) ;
	}

	this.canLoopAgain = true ;
	if ( this.isQueueRunning && ! this.isLoopRunning ) { this.run() ; }
	if ( this.drained.isSettled() ) { this.drained = new Promise() ; }
} ;



Queue.prototype.run = Queue.prototype.resume = async function() {
	var job ;

	this.isQueueRunning = true ;

	if ( this.isLoopRunning ) { return ; }
	this.isLoopRunning = true ;

	if ( ! this.startTime ) { this.startTime = Date.now() ; }

	do {
		this.canLoopAgain = false ;

		for ( job of this.pendingJobs.values() ) {
			if ( job.dependencies && job.dependencies.some( dependencyId => ! this.jobsDone.has( dependencyId ) ) ) { continue ; }
			// This should be done synchronously:
			if ( this.idle.isSettled() ) { this.idle = new Promise() ; }
			this.canLoopAgain = true ;

			await this.ready ;

			// Something has stopped the queue while we were awaiting.
			// This check MUST be done only after "await", before is potentially synchronous, and things only change concurrently during an "await"
			if ( ! this.isQueueRunning ) { this.finishRun() ; return ; }

			this.runJob( job ) ;
		}
	} while ( this.canLoopAgain ) ;

	this.finishRun() ;
} ;



// Finish current run
Queue.prototype.finishRun = function() {
	this.isLoopRunning = false ;

	if ( ! this.pendingJobs.size ) { this.drained.resolve() ; }

	if ( ! this.runningJobs.size ) {
		this.endTime = Date.now() ;
		this.idle.resolve() ;
	}
} ;



Queue.prototype.runJob = async function( job ) {
	// Immediately remove it synchronously from the pending queue and add it to the running one
	this.pendingJobs.delete( job.id ) ;
	this.runningJobs.set( job.id , job ) ;

	if ( this.runningJobs.size >= this.concurrency ) { this.ready = new Promise() ; }

	// Async part
	try {
		job.startTime = Date.now() ;
		await this.jobRunner( job.data ) ;
		job.endTime = Date.now() ;
		this.jobsDone.set( job.id , job ) ;
		this.canLoopAgain = true ;
	}
	catch ( error ) {
		job.endTime = Date.now() ;
		job.error = error ;
		this.errorJobs.set( job.id , job ) ;
	}

	this.runningJobs.delete( job.id ) ;
	if ( this.runningJobs.size < this.concurrency ) { this.ready.resolve() ; }

	// This MUST come last, because it retry the loop: dependencies may have been unlocked!
	if ( ! this.isLoopRunning ) {
		if ( this.isQueueRunning && this.pendingJobs.size ) { this.run() ; }
		else { this.finishRun() ; }
	}
} ;



Queue.prototype.getJobTimes = function() {
	var job , stats = {} ;
	for ( job of this.jobsDone.values() ) { stats[ job.id ] = job.endTime - job.startTime ; }
	return stats ;
} ;



Queue.prototype.getStats = function() {
	var job , sum = 0 ,
		stats = {
			pending: this.pendingJobs.size ,
			running: this.runningJobs.size ,
			failed: this.errorJobs.size ,
			done: this.jobsDone.size ,
			averageJobTime: null ,
			queueTime: null
		} ;

	if ( this.jobsDone.size ) {
		for ( job of this.jobsDone.values() ) { sum += job.endTime - job.startTime ; }
		stats.averageJobTime = sum / this.jobsDone.size ;
	}

	if ( this.endTime ) { stats.queueTime = this.endTime - this.startTime ; }

	return stats ;
} ;


},{"./seventh.js":114}],108:[function(require,module,exports){
/*
	Seventh

	Copyright (c) 2017 - 2020 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Promise = require( './seventh.js' ) ;



Promise.promisifyNodeApi = ( api , suffix , multiSuffix , filter , anything ) => {
	var keys ;

	suffix = suffix || 'Async' ;
	multiSuffix = multiSuffix || 'AsyncAll' ;
	filter = filter || ( key => key[ 0 ] !== '_' && ! key.endsWith( 'Sync' ) ) ;

	if ( anything ) {
		keys = [] ;

		for ( let key in api ) {
			if ( typeof api[ key ] === 'function' ) { keys.push( key ) ; }
		}
	}
	else {
		keys = Object.keys( api ) ;
	}

	keys.filter( key => {
		if ( typeof api[ key ] !== 'function' ) { return false ; }

		// If it has any enumerable properties on its prototype, it's a constructor
		for ( let trash in api[ key ].prototype ) { return false ; }

		return filter( key , api ) ;
	} )
		.forEach( key => {
			const targetKey = key + suffix ;
			const multiTargetKey = key + multiSuffix ;

			// Do nothing if it already exists
			if ( ! api[ targetKey ] ) {
				api[ targetKey ] = Promise.promisify( api[ key ] , api ) ;
			}

			if ( ! api[ multiTargetKey ] ) {
				api[ multiTargetKey ] = Promise.promisifyAll( api[ key ] , api ) ;
			}
		} ) ;
} ;



Promise.promisifyAnyNodeApi = ( api , suffix , multiSuffix , filter ) => {
	Promise.promisifyNodeApi( api , suffix , multiSuffix , filter , true ) ;
} ;



},{"./seventh.js":114}],109:[function(require,module,exports){
/*
	Seventh

	Copyright (c) 2017 - 2020 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Promise = require( './seventh.js' ) ;



// This object is used as a special unique value for array hole (see Promise.filter())
const HOLE = {} ;

function noop() {}



Promise.all = ( iterable ) => {
	var index = - 1 , settled = false ,
		count = 0 , length = Infinity ,
		value , values = [] ,
		allPromise = new Promise() ;

	for ( value of iterable ) {
		if ( settled ) { break ; }

		const promiseIndex = ++ index ;

		Promise.resolve( value )
			.then(
				value_ => {
					if ( settled ) { return ; }

					values[ promiseIndex ] = value_ ;
					count ++ ;

					if ( count >= length ) {
						settled = true ;
						allPromise._resolveValue( values ) ;
					}
				} ,
				error => {
					if ( settled ) { return ; }
					settled = true ;
					allPromise.reject( error ) ;
				}
			) ;
	}

	length = index + 1 ;

	if ( ! length ) {
		allPromise._resolveValue( values ) ;
	}

	return allPromise ;
} ;



// Maybe faster, but can't find any reasonable grounds for that ATM
//Promise.all =
Promise._allArray = ( iterable ) => {
	var length = iterable.length ;

	if ( ! length ) { Promise._resolveValue( [] ) ; }

	var index ,
		runtime = {
			settled: false ,
			count: 0 ,
			length: length ,
			values: [] ,
			allPromise: new Promise()
		} ;

	for ( index = 0 ; ! runtime.settled && index < length ; index ++ ) {
		Promise._allArrayOne( iterable[ index ] , index , runtime ) ;
	}

	return runtime.allPromise ;
} ;



// internal for allArray
Promise._allArrayOne = ( value , index , runtime ) => {
	Promise._bareThen( value ,
		value_ => {
			if ( runtime.settled ) { return ; }

			runtime.values[ index ] = value_ ;
			runtime.count ++ ;

			if ( runtime.count >= runtime.length ) {
				runtime.settled = true ;
				runtime.allPromise._resolveValue( runtime.values ) ;
			}
		} ,
		error => {
			if ( runtime.settled ) { return ; }
			runtime.settled = true ;
			runtime.allPromise.reject( error ) ;
		}
	) ;
} ;



Promise.allSettled = ( iterable ) => {
	var index = - 1 , settled = false ,
		count = 0 , length = Infinity ,
		value , values = [] ,
		allPromise = new Promise() ;

	for ( value of iterable ) {
		if ( settled ) { break ; }

		const promiseIndex = ++ index ;

		Promise.resolve( value )
			.then(
				value_ => {
					if ( settled ) { return ; }

					values[ promiseIndex ] = { status: 'fulfilled' , value: value_ } ;
					count ++ ;

					if ( count >= length ) {
						settled = true ;
						allPromise._resolveValue( values ) ;
					}
				} ,
				error => {
					if ( settled ) { return ; }

					values[ promiseIndex ] = { status: 'rejected' ,  reason: error } ;
					count ++ ;

					if ( count >= length ) {
						settled = true ;
						allPromise._resolveValue( values ) ;
					}
				}
			) ;
	}

	length = index + 1 ;

	if ( ! length ) {
		allPromise._resolveValue( values ) ;
	}

	return allPromise ;
} ;



// Promise.all() with an iterator
Promise.every =
Promise.map = ( iterable , iterator ) => {
	var index = - 1 , settled = false ,
		count = 0 , length = Infinity ,
		value , values = [] ,
		allPromise = new Promise() ;

	for ( value of iterable ) {
		if ( settled ) { break ; }

		const promiseIndex = ++ index ;

		Promise.resolve( value )
			.then( value_ => {
				if ( settled ) { return ; }
				return iterator( value_ , promiseIndex ) ;
			} )
			.then(
				value_ => {
					if ( settled ) { return ; }

					values[ promiseIndex ] = value_ ;
					count ++ ;

					if ( count >= length ) {
						settled = true ;
						allPromise._resolveValue( values ) ;
					}
				} ,
				error => {
					if ( settled ) { return ; }
					settled = true ;
					allPromise.reject( error ) ;
				}
			) ;
	}

	length = index + 1 ;

	if ( ! length ) {
		allPromise._resolveValue( values ) ;
	}

	return allPromise ;
} ;



/*
	It works symmetrically with Promise.all(), the resolve and reject logic are switched.
	Therefore, it resolves to the first resolving promise OR reject if all promises are rejected
	with, as a reason an AggregateError of all promise rejection reasons.
*/
Promise.any = ( iterable ) => {
	var index = - 1 , settled = false ,
		count = 0 , length = Infinity ,
		value ,
		errors = [] ,
		anyPromise = new Promise() ;

	for ( value of iterable ) {
		if ( settled ) { break ; }

		const promiseIndex = ++ index ;

		Promise.resolve( value )
			.then(
				value_ => {
					if ( settled ) { return ; }

					settled = true ;
					anyPromise._resolveValue( value_ ) ;
				} ,
				error => {
					if ( settled ) { return ; }

					errors[ promiseIndex ] = error ;
					count ++ ;

					if ( count >= length ) {
						settled = true ;
						anyPromise.reject( new AggregateError( errors ) , 'Promise.any(): All promises have rejected' ) ;
					}
				}
			) ;
	}

	length = index + 1 ;

	if ( ! length ) {
		anyPromise.reject( new RangeError( 'Promise.any(): empty array' ) ) ;
	}

	return anyPromise ;
} ;



// Like Promise.any() but with an iterator
Promise.some = ( iterable , iterator ) => {
	var index = - 1 , settled = false ,
		count = 0 , length = Infinity ,
		value ,
		errors = [] ,
		anyPromise = new Promise() ;

	for ( value of iterable ) {
		if ( settled ) { break ; }

		const promiseIndex = ++ index ;

		Promise.resolve( value )
			.then( value_ => {
				if ( settled ) { return ; }
				return iterator( value_ , promiseIndex ) ;
			} )
			.then(
				value_ => {
					if ( settled ) { return ; }

					settled = true ;
					anyPromise._resolveValue( value_ ) ;
				} ,
				error => {
					if ( settled ) { return ; }

					errors[ promiseIndex ] = error ;
					count ++ ;

					if ( count >= length ) {
						settled = true ;
						anyPromise.reject( new AggregateError( errors , 'Promise.some(): All promises have rejected' ) ) ;
					}
				}
			) ;
	}

	length = index + 1 ;

	if ( ! length ) {
		anyPromise.reject( new RangeError( 'Promise.some(): empty array' ) ) ;
	}

	return anyPromise ;
} ;



/*
	More closed to Array#filter().
	The iterator should return truthy if the array element should be kept,
	or falsy if the element should be filtered out.
	Any rejection reject the whole promise.
*/
Promise.filter = ( iterable , iterator ) => {
	var index = - 1 , settled = false ,
		count = 0 , length = Infinity ,
		value , values = [] ,
		filterPromise = new Promise() ;

	for ( value of iterable ) {
		if ( settled ) { break ; }

		const promiseIndex = ++ index ;

		Promise.resolve( value )
			.then( value_ => {
				if ( settled ) { return ; }
				values[ promiseIndex ] = value_ ;
				return iterator( value_ , promiseIndex ) ;
			} )
			.then(
				iteratorValue => {
					if ( settled ) { return ; }

					count ++ ;

					if ( ! iteratorValue ) { values[ promiseIndex ] = HOLE ; }

					if ( count >= length ) {
						settled = true ;
						values = values.filter( e => e !== HOLE ) ;
						filterPromise._resolveValue( values ) ;
					}
				} ,
				error => {
					if ( settled ) { return ; }
					settled = true ;
					filterPromise.reject( error ) ;
				}
			) ;
	}

	length = index + 1 ;

	if ( ! length ) {
		filterPromise._resolveValue( values ) ;
	}
	else if ( count >= length ) {
		settled = true ;
		values = values.filter( e => e !== HOLE ) ;
		filterPromise._resolveValue( values ) ;
	}

	return filterPromise ;
} ;



// forEach performs reduce as well, if a third argument is supplied
// Force a function statement because we are using arguments.length, so we can support accumulator equals to undefined
Promise.foreach =
Promise.forEach = function( iterable , iterator , accumulator ) {
	var index = - 1 ,
		isReduce = arguments.length >= 3 ,
		it = iterable[Symbol.iterator]() ,
		forEachPromise = new Promise() ,
		lastPromise = Promise.resolve( accumulator ) ;

	// The array-like may contains promises that could be rejected before being handled
	if ( Promise.warnUnhandledRejection ) { Promise._handleAll( iterable ) ; }

	var nextElement = () => {
		lastPromise.then(
			accumulator_ => {
				let { value , done } = it.next() ;
				index ++ ;

				if ( done ) {
					forEachPromise.resolve( accumulator_ ) ;
				}
				else {
					lastPromise = Promise.resolve( value ).then(
						isReduce ?
							value_ => iterator( accumulator_ , value_ , index ) :
							value_ => iterator( value_ , index )
					) ;

					nextElement() ;
				}
			} ,
			error => {
				forEachPromise.reject( error ) ;

				// We have to eat all remaining promise errors
				for ( ;; ) {
					let { value , done } = it.next() ;
					if ( done ) { break ; }

					//if ( ( value instanceof Promise ) || ( value instanceof NativePromise ) )
					if ( Promise.isThenable( value ) ) {
						value.then( noop , noop ) ;
					}
				}
			}
		) ;
	} ;

	nextElement() ;

	return forEachPromise ;
} ;



Promise.reduce = ( iterable , iterator , accumulator ) => {
	// Force 3 arguments
	return Promise.forEach( iterable , iterator , accumulator ) ;
} ;



/*
	Same than map, but iterate over an object and produce an object.
	Think of it as a kind of Object#map() (which of course does not exist).
*/
Promise.mapObject = ( inputObject , iterator ) => {
	var settled = false ,
		count = 0 ,
		keys = Object.keys( inputObject ) ,
		length = keys.length ,
		outputObject = {} ,
		mapPromise = new Promise() ;

	for ( let i = 0 ; ! settled && i < length ; i ++ ) {
		const key = keys[ i ] ;
		const value = inputObject[ key ] ;

		Promise.resolve( value )
			.then( value_ => {
				if ( settled ) { return ; }
				return iterator( value_ , key ) ;
			} )
			.then(
				value_ => {
					if ( settled ) { return ; }

					outputObject[ key ] = value_ ;
					count ++ ;

					if ( count >= length ) {
						settled = true ;
						mapPromise._resolveValue( outputObject ) ;
					}
				} ,
				error => {
					if ( settled ) { return ; }
					settled = true ;
					mapPromise.reject( error ) ;
				}
			) ;
	}

	if ( ! length ) {
		mapPromise._resolveValue( outputObject ) ;
	}

	return mapPromise ;
} ;



// Like map, but with a concurrency limit
Promise.concurrent = ( limit , iterable , iterator ) => {
	var index = - 1 , settled = false ,
		running = 0 ,
		count = 0 , length = Infinity ,
		value , done = false ,
		values = [] ,
		it = iterable[Symbol.iterator]() ,
		concurrentPromise = new Promise() ;

	// The array-like may contains promises that could be rejected before being handled
	if ( Promise.warnUnhandledRejection ) { Promise._handleAll( iterable ) ; }

	limit = + limit || 1 ;

	const runBatch = () => {
		while ( ! done && running < limit ) {

			//console.log( "Pre" , index ) ;
			( { value , done } = it.next() ) ;

			if ( done ) {
				length = index + 1 ;

				if ( count >= length ) {
					settled = true ;
					concurrentPromise._resolveValue( values ) ;
					return ;
				}
				break ;
			}

			if ( settled ) { break ; }

			const promiseIndex = ++ index ;
			running ++ ;
			//console.log( "Launch" , promiseIndex ) ;

			Promise.resolve( value )
				.then( value_ => {
					if ( settled ) { return ; }
					return iterator( value_ , promiseIndex ) ;
				} )
				.then(
					value_ => {
					//console.log( "Done" , promiseIndex , value_ ) ;
						if ( settled ) { return ; }

						values[ promiseIndex ] = value_ ;
						count ++ ;
						running -- ;

						//console.log( "count/length" , count , length ) ;
						if ( count >= length ) {
							settled = true ;
							concurrentPromise._resolveValue( values ) ;
							return ;
						}

						if ( running < limit ) {
							runBatch() ;
							return ;
						}
					} ,
					error => {
						if ( settled ) { return ; }
						settled = true ;
						concurrentPromise.reject( error ) ;
					}
				) ;
		}
	} ;

	runBatch() ;

	if ( index < 0 ) {
		concurrentPromise._resolveValue( values ) ;
	}

	return concurrentPromise ;
} ;



/*
	Like native Promise.race(), it is hanging forever if the array is empty.
	It resolves or rejects to the first resolved/rejected promise.
*/
Promise.race = ( iterable ) => {
	var settled = false ,
		value ,
		racePromise = new Promise() ;

	for ( value of iterable ) {
		if ( settled ) { break ; }

		Promise.resolve( value )
			.then(
				value_ => {
					if ( settled ) { return ; }

					settled = true ;
					racePromise._resolveValue( value_ ) ;
				} ,
				error => {
					if ( settled ) { return ; }

					settled = true ;
					racePromise.reject( error ) ;
				}
			) ;
	}

	return racePromise ;
} ;


},{"./seventh.js":114}],110:[function(require,module,exports){
(function (process,global,setImmediate){(function (){
/*
	Seventh

	Copyright (c) 2017 - 2020 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



/*
	Prerequisite.
*/



const NativePromise = global.Promise ;

// Cross-platform next tick function
var nextTick ;

if ( ! process.browser ) {
	nextTick = process.nextTick ;
}
else {
	// Browsers suck, they don't have setImmediate() except IE/Edge.
	// A module is needed to emulate it.
	require( 'setimmediate' ) ;
	nextTick = setImmediate ;
}



/*
	Constructor.
*/



function Promise( fn ) {
	this.fn = fn ;
	this._then = Promise._dormantThen ;
	this.value = null ;
	this.thenHandlers = null ;
	this.handledRejection = null ;

	if ( this.fn ) {
		this._exec() ;
	}
}

module.exports = Promise ;



Promise.Native = NativePromise ;
Promise.warnUnhandledRejection = true ;
Promise.nextTick = nextTick ;



Promise.prototype._exec = function() {
	this._then = Promise._pendingThen ;

	try {
		this.fn(
			// Don't return anything, it would create nasty bugs! E.g.:
			// bad: error => this.reject( error_ )
			// good: error_ => { this.reject( error_ ) ; }
			result_ => { this.resolve( result_ ) ; } ,
			error_ => { this.reject( error_ ) ; }
		) ;
	}
	catch ( error ) {
		this.reject( error ) ;
	}
} ;



/*
	Resolve/reject and then-handlers management.
*/



Promise.prototype.resolve = Promise.prototype.fulfill = function( value ) {
	// Throw an error?
	if ( this._then.settled ) { return this ; }

	if ( Promise.isThenable( value ) ) {
		this._execThenPromise( value ) ;
		return this ;
	}

	return this._resolveValue( value ) ;
} ;



Promise.prototype._resolveValue = function( value ) {
	this._then = Promise._fulfilledThen ;
	this.value = value ;
	if ( this.thenHandlers && this.thenHandlers.length ) { this._execFulfillHandlers() ; }

	return this ;
} ;



// Faster on node v8.x
Promise.prototype._execThenPromise = function( thenPromise ) {
	try {
		thenPromise.then(
			result_ => { this.resolve( result_ ) ; } ,
			error_ => { this.reject( error_ ) ; }
		) ;
	}
	catch ( error ) {
		this.reject( error ) ;
	}
} ;



Promise.prototype.reject = function( error ) {
	// Throw an error?
	if ( this._then.settled ) { return this ; }

	this._then = Promise._rejectedThen ;
	this.value = error ;

	if ( this.thenHandlers && this.thenHandlers.length ) {
		this._execRejectionHandlers() ;
	}
	else if ( Promise.warnUnhandledRejection && ! this.handledRejection ) {
		this._unhandledRejection() ;
	}

	return this ;
} ;



Promise.prototype._execFulfillHandlers = function() {
	var i ,
		length = this.thenHandlers.length ;

	// Do cache the length, if a handler is synchronously added, it will be called on next tick
	for ( i = 0 ; i < length ; i += 3 ) {
		if ( this.thenHandlers[ i + 1 ] ) {
			this._execOneFulfillHandler( this.thenHandlers[ i ] , this.thenHandlers[ i + 1 ] ) ;
		}
		else {
			this.thenHandlers[ i ].resolve( this.value ) ;
		}
	}
} ;



// Faster on node v8.x?
//*
Promise.prototype._execOneFulfillHandler = function( promise , onFulfill ) {
	try {
		promise.resolve( onFulfill( this.value ) ) ;
	}
	catch ( error_ ) {
		promise.reject( error_ ) ;
	}
} ;
//*/



Promise.prototype._execRejectionHandlers = function() {
	var i ,
		length = this.thenHandlers.length ;

	// Do cache the length, if a handler is synchronously added, it will be called on next tick
	for ( i = 0 ; i < length ; i += 3 ) {
		if ( this.thenHandlers[ i + 2 ] ) {
			this._execOneRejectHandler( this.thenHandlers[ i ] , this.thenHandlers[ i + 2 ] ) ;
		}
		else {
			this.thenHandlers[ i ].reject( this.value ) ;
		}
	}
} ;



// Faster on node v8.x?
//*
Promise.prototype._execOneRejectHandler = function( promise , onReject ) {
	try {
		promise.resolve( onReject( this.value ) ) ;
	}
	catch ( error_ ) {
		promise.reject( error_ ) ;
	}
} ;
//*/



Promise.prototype.resolveTimeout = Promise.prototype.fulfillTimeout = function( time , result ) {
	setTimeout( () => this.resolve( result ) , time ) ;
} ;



Promise.prototype.rejectTimeout = function( time , error ) {
	setTimeout( () => this.reject( error ) , time ) ;
} ;



Promise.prototype.resolveNextTick = Promise.prototype.fulfillNextTick = function( result ) {
	nextTick( () => this.resolve( result ) ) ;
} ;



Promise.prototype.rejectNextTick = function( error ) {
	nextTick( () => this.reject( error ) ) ;
} ;



/*
	.then() variants depending on the state
*/



// .then() variant when the promise is dormant
Promise._dormantThen = function( onFulfill , onReject ) {
	if ( this.fn ) {
		// If this is a dormant promise, wake it up now!
		this._exec() ;

		// Return now, some sync stuff can change the status
		return this._then( onFulfill , onReject ) ;
	}

	var promise = new Promise() ;

	if ( ! this.thenHandlers ) {
		this.thenHandlers = [ promise , onFulfill , onReject ] ;
	}
	else {
		//this.thenHandlers.push( onFulfill ) ;
		this.thenHandlers[ this.thenHandlers.length ] = promise ;
		this.thenHandlers[ this.thenHandlers.length ] = onFulfill ;
		this.thenHandlers[ this.thenHandlers.length ] = onReject ;
	}

	return promise ;
} ;

Promise._dormantThen.settled = false ;



// .then() variant when the promise is pending
Promise._pendingThen = function( onFulfill , onReject ) {
	var promise = new Promise() ;

	if ( ! this.thenHandlers ) {
		this.thenHandlers = [ promise , onFulfill , onReject ] ;
	}
	else {
		//this.thenHandlers.push( onFulfill ) ;
		this.thenHandlers[ this.thenHandlers.length ] = promise ;
		this.thenHandlers[ this.thenHandlers.length ] = onFulfill ;
		this.thenHandlers[ this.thenHandlers.length ] = onReject ;
	}

	return promise ;
} ;

Promise._pendingThen.settled = false ;



// .then() variant when the promise is fulfilled
Promise._fulfilledThen = function( onFulfill ) {
	if ( ! onFulfill ) { return this ; }

	var promise = new Promise() ;

	// This handler should not fire in this code sync flow
	nextTick( () => {
		try {
			promise.resolve( onFulfill( this.value ) ) ;
		}
		catch ( error ) {
			promise.reject( error ) ;
		}
	} ) ;

	return promise ;
} ;

Promise._fulfilledThen.settled = true ;



// .then() variant when the promise is rejected
Promise._rejectedThen = function( onFulfill , onReject ) {
	if ( ! onReject ) { return this ; }

	this.handledRejection = true ;
	var promise = new Promise() ;

	// This handler should not fire in this code sync flow
	nextTick( () => {
		try {
			promise.resolve( onReject( this.value ) ) ;
		}
		catch ( error ) {
			promise.reject( error ) ;
		}
	} ) ;

	return promise ;
} ;

Promise._rejectedThen.settled = true ;



/*
	.then() and short-hands.
*/



Promise.prototype.then = function( onFulfill , onReject ) {
	return this._then( onFulfill , onReject ) ;
} ;



Promise.prototype.catch = function( onReject = () => undefined ) {
	return this._then( undefined , onReject ) ;
} ;



Promise.prototype.finally = function( onSettled ) {
	return this._then( onSettled , onSettled ) ;
} ;



Promise.prototype.tap = Promise.prototype.tapThen = function( onFulfill ) {
	this._then( onFulfill , undefined ) ;
	return this ;
} ;



Promise.prototype.tapCatch = function( onReject ) {
	this._then( undefined , onReject ) ;
	return this ;
} ;



Promise.prototype.tapFinally = function( onSettled ) {
	this._then( onSettled , onSettled ) ;
	return this ;
} ;



// Any unhandled error throw ASAP
Promise.prototype.fatal = function() {
	this._then( undefined , error => {
		// Throw async, otherwise it would be catched by .then()
		nextTick( () => { throw error ; } ) ;
	} ) ;
} ;



Promise.prototype.done = function( onFulfill , onReject ) {
	this._then( onFulfill , onReject ).fatal() ;
	return this ;
} ;



Promise.prototype.callback = function( cb ) {
	this._then(
		value => { cb( undefined , value ) ; } ,
		error => { cb( error ) ; }
	).fatal() ;

	return this ;
} ;



Promise.prototype.callbackAll = function( cb ) {
	this._then(
		values => {
			if ( Array.isArray( values ) ) { cb( undefined , ... values ) ; }
			else { cb( undefined , values ) ; }
		} ,
		error => { cb( error ) ; }
	).fatal() ;

	return this ;
} ;



/*
	The reverse of .callback(), it calls the function with a callback argument and return a promise that resolve or reject depending on that callback invocation.
	Usage:
		await Promise.callback( callback => myFunctionRelyingOnCallback( [arg1] , [arg2] , [...] , callback ) ;
*/
Promise.callback = function( fn ) {
	return new Promise( ( resolve , reject ) => {
		fn( ( error , arg ) => {
			if ( error ) { reject( error ) ; }
			else { resolve( arg ) ; }
		} ) ;
	} ) ;
} ;



Promise.callbackAll = function( fn ) {
	return new Promise( ( resolve , reject ) => {
		fn( ( error , ... args ) => {
			if ( error ) { reject( error ) ; }
			else { resolve( args ) ; }
		} ) ;
	} ) ;
} ;



Promise.prototype.toPromise =	// <-- DEPRECATED, use .propagate
Promise.prototype.propagate = function( promise ) {
	this._then(
		value => { promise.resolve( value ) ; } ,
		error => { promise.reject( error ) ; }
	) ;

	return this ;
} ;





/*
	Foreign promises facilities
*/



Promise.propagate = function( foreignPromise , promise ) {
	foreignPromise.then(
		value => { promise.resolve( value ) ; } ,
		error => { promise.reject( error ) ; }
	) ;

	return foreignPromise ;
} ;



Promise.finally = function( foreignPromise , onSettled ) {
	return foreignPromise.then( onSettled , onSettled ) ;
} ;





/*
	Static factories.
*/



Promise.resolve = Promise.fulfill = function( value ) {
	if ( Promise.isThenable( value ) ) { return Promise.fromThenable( value ) ; }
	return Promise._resolveValue( value ) ;
} ;



Promise._resolveValue = function( value ) {
	var promise = new Promise() ;
	promise._then = Promise._fulfilledThen ;
	promise.value = value ;
	return promise ;
} ;



Promise.reject = function( error ) {
	//return new Promise().reject( error ) ;
	var promise = new Promise() ;
	promise._then = Promise._rejectedThen ;
	promise.value = error ;
	return promise ;
} ;



Promise.resolveTimeout = Promise.fulfillTimeout = function( timeout , value ) {
	return new Promise( resolve => setTimeout( () => resolve( value ) , timeout ) ) ;
} ;



Promise.rejectTimeout = function( timeout , error ) {
	return new Promise( ( resolve , reject ) => setTimeout( () => reject( error ) , timeout ) ) ;
} ;



Promise.resolveNextTick = Promise.fulfillNextTick = function( value ) {
	return new Promise( resolve => nextTick( () => resolve( value ) ) ) ;
} ;



Promise.rejectNextTick = function( error ) {
	return new Promise( ( resolve , reject ) => nextTick( () => reject( error ) ) ) ;
} ;



// A dormant promise is activated the first time a then handler is assigned
Promise.dormant = function( fn ) {
	var promise = new Promise() ;
	promise.fn = fn ;
	return promise ;
} ;



// Try-catched Promise.resolve( fn() )
Promise.try = function( fn ) {
	try {
		return Promise.resolve( fn() ) ;
	}
	catch ( error ) {
		return Promise.reject( error ) ;
	}
} ;



/*
	Thenables.
*/



Promise.isThenable = function( value ) {
	return value && typeof value === 'object' && typeof value.then === 'function' ;
} ;



// We assume a thenable object here
Promise.fromThenable = function( thenable ) {
	if ( thenable instanceof Promise ) { return thenable ; }

	return new Promise( ( resolve , reject ) => {
		thenable.then(
			value => { resolve( value ) ; } ,
			error => { reject( error ) ; }
		) ;
	} ) ;
} ;



// When you just want a fast then() function out of anything, without any desync and unchainable
Promise._bareThen = function( value , onFulfill , onReject ) {
	//if ( Promise.isThenable( value ) )
	if( value && typeof value === 'object' ) {
		if ( value instanceof Promise ) {
			if ( value._then === Promise._fulfilledThen ) { onFulfill( value.value ) ; }
			else if ( value._then === Promise._rejectedThen ) { onReject( value.value ) ; }
			else { value._then( onFulfill , onReject ) ; }
		}
		else if ( typeof value.then === 'function' ) {
			value.then( onFulfill , onReject ) ;
		}
		else {
			onFulfill( value ) ;
		}
	}
	else {
		onFulfill( value ) ;
	}
} ;



/*
	Misc.
*/



// Internal usage, mark all promises as handled ahead of time, useful for series,
// because a warning would be displayed for unhandled rejection for promises that are not yet processed.
Promise._handleAll = function( iterable ) {
	var value ;

	for ( value of iterable ) {
		//if ( ( value instanceof Promise ) || ( value instanceof NativePromise ) )
		if ( Promise.isThenable( value ) ) {
			value.handledRejection = true ;
		}
	}
} ;



Promise.prototype._unhandledRejection = function() {
	// This promise is currently unhandled
	// If still unhandled at the end of the synchronous block of code,
	// output an error message.

	this.handledRejection = false ;

	// Don't know what is the correct way to inform node.js about that.
	// There is no doc about that, and emitting unhandledRejection,
	// does not produce what is expected.

	//process.emit( 'unhandledRejection' , this.value , this ) ;

	/*
	nextTick( () => {
		if ( this.handledRejection === false )
		{
			process.emit( 'unhandledRejection' , this.value , this ) ;
		}
	} ) ;
	*/

	// It looks like 'await' inside a 'try-catch' does not handle the promise soon enough -_-'
	//const nextTick_ = nextTick ;
	const nextTick_ = cb => setTimeout( cb , 0 ) ;

	//*
	if ( this.value instanceof Error ) {
		nextTick_( () => {
			if ( this.handledRejection === false ) {
				this.value.message = 'Unhandled promise rejection: ' + this.value.message ;
				console.error( this.value ) ;
			}
		} ) ;
	}
	else {
		// Avoid starting the stack trace in the nextTick()...
		let error_ = new Error( 'Unhandled promise rejection' ) ;
		nextTick_( () => {
			if ( this.handledRejection === false ) {
				console.error( error_ ) ;
				console.error( 'Rejection reason:' , this.value ) ;
			}
		} ) ;
	}
	//*/
} ;



Promise.prototype.isSettled = function() { return this._then.settled ; } ;



Promise.prototype.getStatus = function() {
	switch ( this._then ) {
		case Promise._dormantThen :
			return 'dormant' ;
		case Promise._pendingThen :
			return 'pending' ;
		case Promise._fulfilledThen :
			return 'fulfilled' ;
		case Promise._rejectedThen :
			return 'rejected' ;
	}
} ;



Promise.prototype.inspect = function() {
	switch ( this._then ) {
		case Promise._dormantThen :
			return 'Promise { <DORMANT> }' ;
		case Promise._pendingThen :
			return 'Promise { <PENDING> }' ;
		case Promise._fulfilledThen :
			return 'Promise { <FULFILLED> ' + this.value + ' }' ;
		case Promise._rejectedThen :
			return 'Promise { <REJECTED> ' + this.value + ' }' ;
	}
} ;



// A shared dummy promise, when you just want to return an immediately thenable
Promise.resolved = Promise.dummy = Promise.resolve() ;





/*
	Browser specific.
*/



if ( process.browser ) {
	Promise.prototype.resolveAtAnimationFrame = function( value ) {
		window.requestAnimationFrame( () => this.resolve( value ) ) ;
	} ;

	Promise.prototype.rejectAtAnimationFrame = function( error ) {
		window.requestAnimationFrame( () => this.reject( error ) ) ;
	} ;

	Promise.resolveAtAnimationFrame = function( value ) {
		return new Promise( resolve => window.requestAnimationFrame( () => resolve( value ) ) ) ;
	} ;

	Promise.rejectAtAnimationFrame = function( error ) {
		return new Promise( ( resolve , reject ) => window.requestAnimationFrame( () => reject( error ) ) ) ;
	} ;
}


}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},require("timers").setImmediate)
},{"_process":191,"setimmediate":106,"timers":209}],111:[function(require,module,exports){
/*
	Seventh

	Copyright (c) 2017 - 2020 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Promise = require( './seventh.js' ) ;
const noop = () => undefined ;



Promise.promisifyAll = ( nodeAsyncFn , thisBinding ) => {
	// Little optimization here to have a promisified function as fast as possible
	if ( thisBinding ) {
		return ( ... args ) => {
			return new Promise( ( resolve , reject ) => {
				nodeAsyncFn.call( thisBinding , ... args , ( error , ... cbArgs ) => {
					if ( error ) {
						if ( cbArgs.length && error instanceof Error ) { error.args = cbArgs ; }
						reject( error ) ;
					}
					else {
						resolve( cbArgs ) ;
					}
				} ) ;
			} ) ;
		} ;
	}

	return function( ... args ) {
		return new Promise( ( resolve , reject ) => {
			nodeAsyncFn.call( this , ... args , ( error , ... cbArgs ) => {
				if ( error ) {
					if ( cbArgs.length && error instanceof Error ) { error.args = cbArgs ; }
					reject( error ) ;
				}
				else {
					resolve( cbArgs ) ;
				}
			} ) ;
		} ) ;
	} ;

} ;



// Same than .promisifyAll() but only return the callback args #1 instead of an array of args from #1 to #n
Promise.promisify = ( nodeAsyncFn , thisBinding ) => {
	// Little optimization here to have a promisified function as fast as possible
	if ( thisBinding ) {
		return ( ... args ) => {
			return new Promise( ( resolve , reject ) => {
				nodeAsyncFn.call( thisBinding , ... args , ( error , cbArg ) => {
					if ( error ) {
						if ( cbArg !== undefined && error instanceof Error ) { error.arg = cbArg ; }
						reject( error ) ;
					}
					else {
						resolve( cbArg ) ;
					}
				} ) ;
			} ) ;
		} ;
	}

	return function( ... args ) {
		return new Promise( ( resolve , reject ) => {
			nodeAsyncFn.call( this , ... args , ( error , cbArg ) => {
				if ( error ) {
					if ( cbArg !== undefined && error instanceof Error ) { error.arg = cbArg ; }
					reject( error ) ;
				}
				else {
					resolve( cbArg ) ;
				}
			} ) ;
		} ) ;
	} ;
} ;



/*
	Intercept each decoratee resolve/reject.
*/
Promise.interceptor = ( asyncFn , interceptor , errorInterceptor , thisBinding ) => {
	if ( typeof errorInterceptor !== 'function' ) {
		thisBinding = errorInterceptor ;
		errorInterceptor = noop ;
	}

	return function( ... args ) {
		var localThis = thisBinding || this ,
			maybePromise = asyncFn.call( localThis , ... args ) ;

		Promise.resolve( maybePromise ).then(
			value => interceptor.call( localThis , value ) ,
			error => errorInterceptor.call( localThis , error )
		) ;

		return maybePromise ;
	} ;
} ;



/*
	Run only once, return always the same promise.
*/
Promise.once = ( asyncFn , thisBinding ) => {
	var functionInstance = null ,	// instance when not called as an object's method but a regular function ('this' is undefined)
		instanceMap = new WeakMap() ;

	const getInstance = ( localThis ) => {
		var instance = localThis ? instanceMap.get( localThis ) : functionInstance ;
		if ( instance ) { return instance ; }

		instance = {
			triggered: false ,
			result: undefined
		} ;

		if ( localThis ) { instanceMap.set( localThis , instance ) ; }
		else { functionInstance = instance ; }

		return instance ;
	} ;

	return function( ... args ) {
		var localThis = thisBinding || this ,
			instance = getInstance( localThis ) ;

		if ( ! instance.triggered ) {
			instance.triggered = true ;
			instance.result = asyncFn.call( localThis , ... args ) ;
		}

		return instance.result ;
	} ;
} ;



/*
	The decoratee execution does not overlap, multiple calls are serialized.
*/
Promise.serialize = ( asyncFn , thisBinding ) => {
	var functionInstance = null ,	// instance when not called as an object's method but a regular function ('this' is undefined)
		instanceMap = new WeakMap() ;

	const getInstance = ( localThis ) => {
		var instance = localThis ? instanceMap.get( localThis ) : functionInstance ;
		if ( instance ) { return instance ; }

		instance = { lastPromise: Promise.resolve() } ;

		if ( localThis ) { instanceMap.set( localThis , instance ) ; }
		else { functionInstance = instance ; }

		return instance ;
	} ;

	return function( ... args ) {
		var localThis = thisBinding || this ,
			instance = getInstance( localThis ) ,
			promise = new Promise() ;

		instance.lastPromise.finally( () => {
			Promise.propagate( asyncFn.call( localThis , ... args ) , promise ) ;
		} ) ;

		instance.lastPromise = promise ;

		return promise ;
	} ;
} ;



/*
	It does nothing if the decoratee is still in progress, but it returns the promise of the action in progress.
*/
Promise.debounce = ( asyncFn , thisBinding ) => {
	var functionInstance = null ,	// instance when not called as an object's method but a regular function ('this' is undefined)
		instanceMap = new WeakMap() ;

	const getInstance = ( localThis ) => {
		var instance = localThis ? instanceMap.get( localThis ) : functionInstance ;
		if ( instance ) { return instance ; }

		instance = { inProgress: null } ;

		if ( localThis ) { instanceMap.set( localThis , instance ) ; }
		else { functionInstance = instance ; }

		return instance ;
	} ;

	return function( ... args ) {
		var localThis = thisBinding || this ,
			instance = getInstance( localThis ) ;

		if ( instance.inProgress ) { return instance.inProgress ; }

		let inProgress = instance.inProgress = asyncFn.call( localThis , ... args ) ;
		Promise.finally( inProgress , () => instance.inProgress = null ) ;
		return inProgress ;
	} ;
} ;



/*
	Like .debounce(), but subsequent call continue to return the last promise for some extra time after it resolved.
*/
Promise.debounceDelay = ( delay , asyncFn , thisBinding ) => {
	var functionInstance = null ,	// instance when not called as an object's method but a regular function ('this' is undefined)
		instanceMap = new WeakMap() ;

	const getInstance = ( localThis ) => {
		var instance = localThis ? instanceMap.get( localThis ) : functionInstance ;
		if ( instance ) { return instance ; }

		instance = { inProgress: null } ;

		if ( localThis ) { instanceMap.set( localThis , instance ) ; }
		else { functionInstance = instance ; }

		return instance ;
	} ;

	return function( ... args ) {
		var localThis = thisBinding || this ,
			instance = getInstance( localThis ) ;

		if ( instance.inProgress ) { return instance.inProgress ; }

		let inProgress = instance.inProgress = asyncFn.call( localThis , ... args ) ;
		Promise.finally( inProgress , () => setTimeout( () => instance.inProgress = null , delay ) ) ;
		return inProgress ;
	} ;
} ;



/*
	debounceNextTick( [asyncFn|syncFn] , thisBinding ) => {

	It does nothing until the next tick.
	The decoratee is called only once with the arguments of the last decorator call.
	The function argument can be sync or async.

	The use case is can be some niche case of .update()/.refresh()/.redraw() functions.
*/
Promise.debounceNextTick = ( asyncFn , thisBinding ) => {
	var inWrapper = null ,
		outWrapper = null ,
		waitFn = null ,
		functionInstance = null ,	// instance when not called as an object's method but a regular function ('this' is undefined)
		instanceMap = new WeakMap() ;

	const getInstance = ( localThis ) => {
		var instance = localThis ? instanceMap.get( localThis ) : functionInstance ;
		if ( instance ) { return instance ; }

		instance = {
			inProgress: null ,
			waitingNextTick: false ,
			currentUpdateWith: null ,
			currentUpdatePromise: null ,
			nextUpdateWith: null ,
			nextUpdatePromise: null
		} ;

		if ( localThis ) { instanceMap.set( localThis , instance ) ; }
		else { functionInstance = instance ; }

		return instance ;
	} ;


	const nextUpdate = function() {
		var instance = getInstance( this ) ;
		instance.inProgress = instance.currentUpdatePromise = null ;

		if ( instance.nextUpdateWith ) {
			let args = instance.nextUpdateWith ;
			instance.nextUpdateWith = null ;
			let sharedPromise = instance.nextUpdatePromise ;
			instance.nextUpdatePromise = null ;

			instance.inProgress = inWrapper.call( this , args ) ;
			// Forward the result to the pending promise
			Promise.propagate( instance.inProgress , sharedPromise ) ;
		}
	} ;


	inWrapper = function( args ) {
		var instance = getInstance( this ) ;

		instance.inProgress = new Promise() ;
		instance.currentUpdateWith = args ;
		instance.waitingNextTick = true ;

		Promise.nextTick( () => {
			instance.waitingNextTick = false ;
			let maybePromise = asyncFn.call( this , ... instance.currentUpdateWith ) ;

			if ( Promise.isThenable( maybePromise ) ) {
				instance.currentUpdatePromise = maybePromise ;
				Promise.finally( maybePromise , nextUpdate.bind( this ) ) ;
				Promise.propagate( maybePromise , instance.inProgress ) ;
			}
			else {
				// the function was synchronous
				instance.currentUpdatePromise = null ;
				instance.inProgress.resolve( maybePromise ) ;
				nextUpdate.call( this ) ;
			}
		} ) ;

		return instance.inProgress ;
	} ;

	return function( ... args ) {
		var localThis = thisBinding || this ,
			instance = getInstance( localThis ) ;

		if ( instance.waitingNextTick ) {
			instance.currentUpdateWith = args ;
			return instance.inProgress ;
		}

		if ( instance.currentUpdatePromise ) {
			if ( ! instance.nextUpdatePromise ) { instance.nextUpdatePromise = new Promise() ; }
			instance.nextUpdateWith = args ;
			return instance.nextUpdatePromise ;
		}

		return inWrapper.call( localThis , args ) ;
	} ;
} ;



/*
	debounceUpdate( [options] , asyncFn , thisBinding ) => {

	It does nothing if the decoratee is still in progress.
	Instead, the decoratee is called again after finishing once and only once, if it was tried one or more time during its progress.
	In case of multiple calls, the arguments of the last call will be used.

	The use case is .update()/.refresh()/.redraw() functions.

	If 'options' is given, it is an object, with:
		* delay: `number` a delay before calling again the decoratee
		* delayFn: async `function` called before calling again the decoratee
		* waitFn: async `function` called before calling the decoratee (even the first try), use-case: Window.requestAnimationFrame()
*/
Promise.debounceUpdate = ( options , asyncFn , thisBinding ) => {
	var inWrapper = null ,
		outWrapper = null ,
		delay = 0 ,
		delayFn = null ,
		waitFn = null ,
		functionInstance = null ,	// instance when not called as an object's method but a regular function ('this' is undefined)
		instanceMap = new WeakMap() ;

	// Manage arguments
	if ( typeof options === 'function' ) {
		thisBinding = asyncFn ;
		asyncFn = options ;
	}
	else {
		if ( typeof options.delay === 'number' ) { delay = options.delay ; }
		if ( typeof options.delayFn === 'function' ) { delayFn = options.delayFn ; }

		if ( options.waitNextTick ) { waitFn = Promise.resolveNextTick ; }
		else if ( typeof options.waitFn === 'function' ) { waitFn = options.waitFn ; }
	}


	const getInstance = ( localThis ) => {
		var instance = localThis ? instanceMap.get( localThis ) : functionInstance ;
		if ( instance ) { return instance ; }

		instance = {
			inProgress: null ,
			waitInProgress: null ,
			currentUpdateWith: null ,
			currentUpdatePromise: null ,
			nextUpdateWith: null ,
			nextUpdatePromise: null
		} ;

		if ( localThis ) { instanceMap.set( localThis , instance ) ; }
		else { functionInstance = instance ; }

		return instance ;
	} ;


	const nextUpdate = function() {
		var instance = getInstance( this ) ;
		instance.inProgress = instance.currentUpdatePromise = null ;

		if ( instance.nextUpdateWith ) {
			let args = instance.nextUpdateWith ;
			instance.nextUpdateWith = null ;
			let sharedPromise = instance.nextUpdatePromise ;
			instance.nextUpdatePromise = null ;

			instance.inProgress = inWrapper.call( this , args ) ;
			// Forward the result to the pending promise
			Promise.propagate( instance.inProgress , sharedPromise ) ;
		}
	} ;


	// Build outWrapper
	if ( delayFn ) {
		outWrapper = function() {
			delayFn().then( nextUpdate.bind( this ) ) ;
		} ;
	}
	else if ( delay ) {
		outWrapper = function() {
			setTimeout( nextUpdate.bind( this ) , delay ) ;
		} ;
	}
	else {
		outWrapper = nextUpdate ;
	}


	if ( waitFn ) {
		inWrapper = function( args ) {
			var instance = getInstance( this ) ;

			instance.inProgress = new Promise() ;
			instance.currentUpdateWith = args ;
			instance.waitInProgress = waitFn() ;

			Promise.finally( instance.waitInProgress , () => {
				instance.waitInProgress = null ;
				instance.currentUpdatePromise = asyncFn.call( this , ... instance.currentUpdateWith ) ;
				Promise.finally( instance.currentUpdatePromise , outWrapper.bind( this ) ) ;
				Promise.propagate( instance.currentUpdatePromise , instance.inProgress ) ;
			} ) ;

			return instance.inProgress ;
		} ;

		return function( ... args ) {
			var localThis = thisBinding || this ,
				instance = getInstance( localThis ) ;

			if ( instance.waitInProgress ) {
				instance.currentUpdateWith = args ;
				return instance.inProgress ;
			}

			if ( instance.currentUpdatePromise ) {
				if ( ! instance.nextUpdatePromise ) { instance.nextUpdatePromise = new Promise() ; }
				instance.nextUpdateWith = args ;
				return instance.nextUpdatePromise ;
			}

			return inWrapper.call( localThis , args ) ;
		} ;
	}


	// Variant without a waitFn

	inWrapper = function( args ) {
		var instance = getInstance( this ) ;

		instance.inProgress = asyncFn.call( this , ... args ) ;
		Promise.finally( instance.inProgress , outWrapper.bind( this ) ) ;
		return instance.inProgress ;
	} ;

	return function( ... args ) {
		var localThis = thisBinding || this ,
			instance = getInstance( localThis ) ;

		if ( instance.inProgress ) {
			if ( ! instance.nextUpdatePromise ) { instance.nextUpdatePromise = new Promise() ; }
			instance.nextUpdateWith = args ;
			return instance.nextUpdatePromise ;
		}

		return inWrapper.call( localThis , args ) ;
	} ;
} ;



// Used to ensure that the sync is done immediately if not busy
Promise.NO_DELAY = {} ;

// Used to ensure that the sync is done immediately if not busy, but for the first of a batch
Promise.BATCH_NO_DELAY = {} ;

/*
	Debounce for synchronization algorithm.
	Get two functions, one for getting from upstream, one for a full sync with upstream (getting AND updating).
	No operation overlap for a given resourceId.
	Depending on the configuration, it is either like .debounce() or like .debounceUpdate().

	*Params:
		fn: the function
		thisBinding: the this binding, if any
		delay: the minimum delay between to call
			for get: nothing is done is the delay is not met, simply return the last promise
			for update/fullSync, it waits for that delay before synchronizing again
		onDebounce: *ONLY* for GET ATM, a callback called when debounced
*/
Promise.debounceSync = ( getParams , fullSyncParams ) => {
	var perResourceData = new Map() ;

	const getResourceData = resourceId => {
		var resourceData = perResourceData.get( resourceId ) ;

		if ( ! resourceData ) {
			resourceData = {
				inProgress: null ,
				inProgressIsFull: null ,
				last: null ,				// Get or full sync promise
				lastTime: null ,			// Get or full sync time
				lastFullSync: null ,		// last full sync promise
				lastFullSyncTime: null ,	// last full sync time
				nextFullSyncPromise: null ,	// the promise for the next fullSync iteration
				nextFullSyncWith: null , 	// the 'this' and arguments for the next fullSync iteration
				noDelayBatches: new Set()		// only the first of the batch has no delay
			} ;

			perResourceData.set( resourceId , resourceData ) ;
		}

		return resourceData ;
	} ;


	const outWrapper = ( resourceData , level ) => {
		// level 2: fullSync, 1: get, 0: nothing but a delay
		var delta , args , sharedPromise , now = new Date() ;
		//lastTime = resourceData.lastTime , lastFullSyncTime = resourceData.lastFullSyncTime ;

		resourceData.inProgress = null ;

		if ( level >= 2 ) { resourceData.lastFullSyncTime = resourceData.lastTime = now ; }
		else if ( level >= 1 ) { resourceData.lastTime = now ; }

		if ( resourceData.nextFullSyncWith ) {
			if ( fullSyncParams.delay && resourceData.lastFullSyncTime && ( delta = now - resourceData.lastFullSyncTime - fullSyncParams.delay ) < 0 ) {
				resourceData.inProgress = Promise.resolveTimeout( - delta + 1 ) ;	// Strangely, sometime it is trigerred 1ms too soon
				resourceData.inProgress.finally( () => outWrapper( resourceData , 0 ) ) ;
				return resourceData.nextFullSyncPromise ;
			}

			args = resourceData.nextFullSyncWith ;
			resourceData.nextFullSyncWith = null ;
			sharedPromise = resourceData.nextFullSyncPromise ;
			resourceData.nextFullSyncPromise = null ;

			// Call the fullSyncParams.fn again
			resourceData.lastFullSync = resourceData.last = resourceData.inProgress = fullSyncParams.fn.call( ... args ) ;

			// Forward the result to the pending promise
			Promise.propagate( resourceData.inProgress , sharedPromise ) ;

			// BTW, trigger again the outWrapper
			Promise.finally( resourceData.inProgress , () => outWrapper( resourceData , 2 ) ) ;

			return resourceData.inProgress ;
		}
	} ;

	const getInWrapper = function( resourceId , ... args ) {
		var noDelay = false ,
			localThis = getParams.thisBinding || this ,
			resourceData = getResourceData( resourceId ) ;

		if ( args[ 0 ] === Promise.NO_DELAY ) {
			noDelay = true ;
			args.shift() ;
		}
		else if ( args[ 0 ] === Promise.BATCH_NO_DELAY ) {
			args.shift() ;
			let batchId = args.shift() ;
			if ( ! resourceData.noDelayBatches.has( batchId ) ) {
				resourceData.noDelayBatches.add( batchId ) ;
				noDelay = true ;
			}
		}

		if ( resourceData.inProgress ) { return resourceData.inProgress ; }

		if ( ! noDelay && getParams.delay && resourceData.lastTime && new Date() - resourceData.lastTime < getParams.delay ) {
			if ( typeof getParams.onDebounce === 'function' ) { getParams.onDebounce( resourceId , ... args ) ; }
			return resourceData.last ;
		}

		resourceData.last = resourceData.inProgress = getParams.fn.call( localThis , resourceId , ... args ) ;
		resourceData.inProgressIsFull = false ;
		Promise.finally( resourceData.inProgress , () => outWrapper( resourceData , 1 ) ) ;
		return resourceData.inProgress ;
	} ;

	const fullSyncInWrapper = function( resourceId , ... args ) {
		var delta ,
			noDelay = false ,
			localThis = fullSyncParams.thisBinding || this ,
			resourceData = getResourceData( resourceId ) ;

		if ( args[ 0 ] === Promise.NO_DELAY ) {
			noDelay = true ;
			args.shift() ;
		}
		else if ( args[ 0 ] === Promise.BATCH_NO_DELAY ) {
			args.shift() ;
			let batchId = args.shift() ;
			if ( ! resourceData.noDelayBatches.has( batchId ) ) {
				resourceData.noDelayBatches.add( batchId ) ;
				noDelay = true ;
			}
		}

		if ( ! resourceData.inProgress && ! noDelay && fullSyncParams.delay && resourceData.lastFullSyncTime && ( delta = new Date() - resourceData.lastFullSyncTime - fullSyncParams.delay ) < 0 ) {
			resourceData.inProgress = Promise.resolveTimeout( - delta + 1 ) ;	// Strangely, sometime it is trigerred 1ms too soon
			Promise.finally( resourceData.inProgress , () => outWrapper( resourceData , 0 ) ) ;
		}

		if ( resourceData.inProgress ) {
			// No difference between in-progress is 'get' or 'fullSync'
			if ( ! resourceData.nextFullSyncPromise ) { resourceData.nextFullSyncPromise = new Promise() ; }
			resourceData.nextFullSyncWith = [ localThis , resourceId , ... args ] ;
			return resourceData.nextFullSyncPromise ;
		}

		resourceData.lastFullSync = resourceData.last = resourceData.inProgress = fullSyncParams.fn.call( localThis , resourceId , ... args ) ;
		Promise.finally( resourceData.inProgress , () => outWrapper( resourceData , 2 ) ) ;
		return resourceData.inProgress ;
	} ;

	return [ getInWrapper , fullSyncInWrapper ] ;
} ;



// The call reject with a timeout error if it takes too much time
Promise.timeout = ( timeout , asyncFn , thisBinding ) => {
	return function( ... args ) {
		var promise = new Promise() ;
		Promise.propagate( asyncFn.call( thisBinding || this , ... args ) , promise ) ;
		setTimeout( () => promise.reject( new Error( 'Timeout' ) ) , timeout ) ;
		return promise ;
	} ;
} ;



// Like .timeout(), but here the timeout value is not passed at creation, but as the first arg of each call
Promise.variableTimeout = ( asyncFn , thisBinding ) => {
	return function( timeout , ... args ) {
		var promise = new Promise() ;
		Promise.propagate( asyncFn.call( thisBinding || this , ... args ) , promise ) ;
		setTimeout( () => promise.reject( new Error( 'Timeout' ) ) , timeout ) ;
		return promise ;
	} ;
} ;


},{"./seventh.js":114}],112:[function(require,module,exports){
(function (process){(function (){
/*
	Seventh

	Copyright (c) 2017 - 2020 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Promise = require( './seventh.js' ) ;



/*
	Asynchronously exit.

	Wait for all listeners of the 'asyncExit' event (on the 'process' object) to have called their callback.
	The listeners receive the exit code about to be produced and a completion callback.
*/

var exitInProgress = false ;

Promise.asyncExit = function( exitCode , timeout ) {
	// Already exiting? no need to call it twice!
	if ( exitInProgress ) { return ; }

	exitInProgress = true ;

	var listeners = process.listeners( 'asyncExit' ) ;

	if ( ! listeners.length ) { process.exit( exitCode ) ; return ; }

	if ( timeout === undefined ) { timeout = 1000 ; }

	const callListener = listener => {

		if ( listener.length < 3 ) {
			// This listener does not have a callback, it is interested in the event but does not need to perform critical stuff.
			// E.g. a server will not accept connection or data anymore, but doesn't need cleanup.
			listener( exitCode , timeout ) ;
			return Promise.dummy ;
		}

		// This listener have a callback, it probably has critical stuff to perform before exiting.
		// E.g. a server that needs to gracefully exit will not accept connection or data anymore,
		// but still want to deliver request in progress.
		return new Promise( resolve => {
			listener( exitCode , timeout , () => { resolve() ; } ) ;
		} ) ;

	} ;

	// We don't care about errors here... We are exiting!
	Promise.map( listeners , callListener )
		.finally( () => process.exit( exitCode ) ) ;

	// Quit anyway if it's too long
	setTimeout( () => process.exit( exitCode ) , timeout ) ;
} ;



// A timeout that ensure a task get the time to perform its action (when there are CPU-bound tasks)
Promise.resolveSafeTimeout = function( timeout , value ) {
	return new Promise( resolve => {
		setTimeout( () => {
			setTimeout( () => {
				setTimeout( () => {
					setTimeout( () => resolve( value ) , 0 ) ;
				} , timeout / 2 ) ;
			} , timeout / 2 ) ;
		} , 0 ) ;
	} ) ;
} ;


}).call(this)}).call(this,require('_process'))
},{"./seventh.js":114,"_process":191}],113:[function(require,module,exports){
/*
	Seventh

	Copyright (c) 2017 - 2020 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Promise = require( './seventh.js' ) ;



/*
	This parasite the native promise, bringing some of seventh features into them.
*/

Promise.parasite = () => {

	var compatibleProtoFn = [
		'tap' , 'tapCatch' , 'finally' ,
		'fatal' , 'done' ,
		'callback' , 'callbackAll'
	] ;

	compatibleProtoFn.forEach( fn => Promise.Native.prototype[ fn ] = Promise.prototype[ fn ] ) ;
	Promise.Native.prototype._then = Promise.Native.prototype.then ;
} ;


},{"./seventh.js":114}],114:[function(require,module,exports){
/*
	Seventh

	Copyright (c) 2017 - 2020 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const seventh = require( './core.js' ) ;
module.exports = seventh ;

// The order matters
require( './batch.js' ) ;
require( './wrapper.js' ) ;
require( './decorators.js' ) ;
require( './Queue.js' ) ;
require( './api.js' ) ;
require( './parasite.js' ) ;
require( './misc.js' ) ;


},{"./Queue.js":107,"./api.js":108,"./batch.js":109,"./core.js":110,"./decorators.js":111,"./misc.js":112,"./parasite.js":113,"./wrapper.js":115}],115:[function(require,module,exports){
/*
	Seventh

	Copyright (c) 2017 - 2020 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const Promise = require( './seventh.js' ) ;



Promise.timeLimit = ( timeout , asyncFnOrPromise ) => {
	return new Promise( ( resolve , reject ) => {
		if ( typeof asyncFnOrPromise === 'function' ) { asyncFnOrPromise = asyncFnOrPromise() ; }
		Promise.resolve( asyncFnOrPromise ).then( resolve , reject ) ;
		setTimeout( () => reject( new Error( "Timeout" ) ) , timeout ) ;
	} ) ;
} ;



/*
	options:
		retries: number of retry
		coolDown: time before retrying
		raiseFactor: time multiplier for each successive cool down
		maxCoolDown: maximum cool-down, the raising time is capped to this value
		timeout: time before assuming it has failed, 0 = no time limit
		catch: `function` (optional) if absent, the function is always retried until it reaches the limit,
			if present, that catch-function is used like a normal promise catch block, the function is retry
			only if the catch-function does not throw or return a rejecting promise
*/
Promise.retry = ( options , asyncFn ) => {
	var count = options.retries || 1 ,
		coolDown = options.coolDown || 0 ,
		raiseFactor = options.raiseFactor || 1 ,
		maxCoolDown = options.maxCoolDown || Infinity ,
		timeout = options.timeout || 0 ,
		catchFn = options.catch || null ;

	const oneTry = () => {
		return ( timeout ? Promise.timeLimit( timeout , asyncFn ) : asyncFn() ).catch( error => {
			if ( ! count -- ) { throw error ; }

			var currentCoolDown = coolDown ;
			coolDown = Math.min( coolDown * raiseFactor , maxCoolDown ) ;

			if ( catchFn ) {
				// Call the custom catch function
				// Let it crash, if it throw we are already in a .catch() block
				return Promise.resolve( catchFn( error ) ).then( () => Promise.resolveTimeout( currentCoolDown ).then( oneTry ) ) ;
			}

			return Promise.resolveTimeout( currentCoolDown ).then( oneTry ) ;
		} ) ;
	} ;

	return oneTry() ;
} ;



// Resolve once an event is fired
Promise.onceEvent = ( emitter , eventName ) => {
	return new Promise( resolve => emitter.once( eventName , resolve ) ) ;
} ;



// Resolve once an event is fired, resolve with an array of arguments
Promise.onceEventAll = ( emitter , eventName ) => {
	return new Promise( resolve => emitter.once( eventName , ( ... args ) => resolve( args ) ) ) ;
} ;



// Resolve once an event is fired, or reject on error
Promise.onceEventOrError = ( emitter , eventName , excludeEvents , _internalAllArgs = false ) => {
	return new Promise( ( resolve , reject ) => {
		var altRejects ;

		// We care about removing listener, especially 'error', because if an error kick in after, it should throw because there is no listener
		var resolve_ = ( ... args ) => {
			emitter.removeListener( 'error' , reject_ ) ;

			if ( altRejects ) {
				for ( let event in altRejects ) {
					emitter.removeListener( event , altRejects[ event ] ) ;
				}
			}

			resolve( _internalAllArgs ? args : args[ 0 ] ) ;
		} ;

		var reject_ = arg => {
			emitter.removeListener( eventName , resolve_ ) ;

			if ( altRejects ) {
				for ( let event in altRejects ) {
					emitter.removeListener( event , altRejects[ event ] ) ;
				}
			}

			reject( arg ) ;
		} ;

		emitter.once( eventName , resolve_ ) ;
		emitter.once( 'error' , reject_ ) ;

		if ( excludeEvents ) {
			if ( ! Array.isArray( excludeEvents ) ) { excludeEvents = [ excludeEvents ] ; }

			altRejects = {} ;

			excludeEvents.forEach( event => {
				var altReject = ( ... args ) => {
					emitter.removeListener( 'error' , reject_ ) ;
					emitter.removeListener( eventName , resolve_ ) ;

					var error = new Error( "Received an excluded event: " + event ) ;
					error.event = event ;
					error.eventArgs = args ;
					reject( error ) ;
				} ;

				emitter.once( event , altReject ) ;

				altRejects[ event ] = altReject ;
			} ) ;
		}
	} ) ;
} ;



// Resolve once an event is fired, or reject on error, resolve with an array of arguments, reject with the first argument
Promise.onceEventAllOrError = ( emitter , eventName , excludeEvents ) => {
	return Promise.onceEventOrError( emitter , eventName , excludeEvents , true ) ;
} ;


},{"./seventh.js":114}],116:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



/*
	Number formatting class.
	.format() should entirely use it for everything related to number formatting.
	It avoids unsolvable rounding error with epsilon.
	It is dedicated for number display, not for computing.
*/



const NUMERALS = [
	'0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' ,
	'a' , 'b' , 'c' , 'd' , 'e' , 'f' ,
	'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z'
] ;

//const NUMERAL_MAP = buildNumeralMap( NUMERALS ) ;



function StringNumber( number , options = {} ) {
	this.sign = 1 ;
	this.digits = [] ;
	this.exposant = 0 ;
	this.special = null ;	// It stores special values like NaN, Infinity, etc

	this.decimalSeparator = options.decimalSeparator ?? '.' ;
	this.forceDecimalSeparator = !! options.forceDecimalSeparator ;
	this.groupSeparator = options.groupSeparator ?? '' ;

	this.numerals = options.numerals ?? NUMERALS ;
	this.numeralZero = options.numeralZero ?? null ;	// Special numeral when the result is EXACTLY 0 (used for Roman Numerals)
	this.placeNumerals = options.placeNumerals ?? null ;
	//this.numeralMap = NUMERAL_MAP ;
	//if ( options.numerals ) { this.numeralMap = buildNumeralMap( options.numerals ) ; }

	this.set( number ) ;
}

module.exports = StringNumber ;



/*
function buildNumeralMap( numbers ) {
	var map = new Map() ;

	for ( let i = 0 ; i < numbers.length ; i ++ ) {
		let number = numbers[ i ] ;
		map.set( NUMERALS[ i ] , numbers[ i ] ) ;
	}

	return map ;
}
*/



StringNumber.prototype.set = function( number ) {
	var matches , v , i , iMax , index , hasNonZeroHead , tailIndex ;

	number = + number ;

	// Reset anything, if it was already used...
	this.sign = 1 ;
	this.digits.length = 0 ;
	this.exposant = 0 ;
	this.special = null ;

	if ( ! Number.isFinite( number ) ) {
		this.special = number ;
		return null ;
	}

	number = '' + number ;
	matches = number.match( /(-)?([0-9]+)(?:.([0-9]+))?(?:e([+-][0-9]+))?/ ) ;
	if ( ! matches ) { throw new Error( 'Unexpected error' ) ; }

	this.sign = matches[ 1 ] ? -1 : 1 ;
	this.exposant = matches[ 2 ].length + ( parseInt( matches[ 4 ] , 10 ) || 0 ) ;

	// Copy each digits and cast them back into a number
	index = 0 ;
	hasNonZeroHead = false ;
	tailIndex = 0 ;	// used to cut trailing zero

	for ( i = 0 , iMax = matches[ 2 ].length ; i < iMax ; i ++ ) {
		v = + matches[ 2 ][ i ] ;
		if ( v !== 0 ) {
			hasNonZeroHead = true ;
			this.digits[ index ] = v ;
			index ++ ;
			tailIndex = index ;
		}
		else if ( hasNonZeroHead ) {
			this.digits[ index ] = v ;
			index ++ ;
		}
		else {
			this.exposant -- ;
		}
	}

	if ( matches[ 3 ] ) {
		for ( i = 0 , iMax = matches[ 3 ].length ; i < iMax ; i ++ ) {
			v = + matches[ 3 ][ i ] ;

			if ( v !== 0 ) {
				hasNonZeroHead = true ;
				this.digits[ index ] = v ;
				index ++ ;
				tailIndex = index ;
			}
			else if ( hasNonZeroHead ) {
				this.digits[ index ] = v ;
				index ++ ;
			}
			else {
				this.exposant -- ;
			}
		}
	}

	if ( tailIndex !== index ) {
		this.digits.length = tailIndex ;
	}
} ;



StringNumber.prototype.toNumber = function() {
	// Using a string representation
	if ( this.special !== null ) { return this.special ; }
	return parseFloat( ( this.sign < 0 ? '-' : '' ) + '0.' + this.digits.join( '' ) + 'e' + this.exposant ) ;
} ;



StringNumber.prototype.toString = function( ... args ) {
	if ( this.special !== null ) { return '' + this.special ; }
	if ( this.exposant > 20 || this.exposant < -20 ) { return this.toScientificString( ... args ) ; }
	return this.toNoExpString( ... args ) ;
} ;



StringNumber.prototype.toExponential =
StringNumber.prototype.toExponentialString = function() {
	if ( this.special !== null ) { return '' + this.special ; }

	var str = this.sign < 0 ? '-' : '' ;
	if ( ! this.digits.length ) { return str + '0' ; }

	str += this.digits[ 0 ] ;

	if ( this.digits.length > 1 ) {
		str += this.decimalSeparator + this.digits.join( '' ).slice( 1 ) ;
	}

	str += 'e' + ( this.exposant > 0 ? '+' : '' ) + ( this.exposant - 1 ) ;
	return str ;
} ;



const SUPER_NUMBER = [ '⁰' , '¹' , '²' , '³' , '⁴' , '⁵' , '⁶' , '⁷' , '⁸' , '⁹' ] ;
const SUPER_PLUS = '⁺' ;
const SUPER_MINUS = '⁻' ;
const ZERO_CHAR_CODE = '0'.charCodeAt( 0 ) ;

StringNumber.prototype.toScientific =
StringNumber.prototype.toScientificString = function() {
	if ( this.special !== null ) { return '' + this.special ; }

	var str = this.sign < 0 ? '-' : '' ;
	if ( ! this.digits.length ) { return str + '0' ; }

	str += this.digits[ 0 ] ;

	if ( this.digits.length > 1 ) {
		str += this.decimalSeparator + this.digits.join( '' ).slice( 1 ) ;
	}

	var exposantStr =
		( this.exposant <= 0 ? SUPER_MINUS : '' ) +
		( '' + Math.abs( this.exposant - 1 ) ).split( '' ).map( c => SUPER_NUMBER[ c.charCodeAt( 0 ) - ZERO_CHAR_CODE ] )
			.join( '' ) ;

	str += ' × 10' + exposantStr ;
	return str ;
} ;



// leadingZero = minimal number of numbers before the dot, they will be left-padded with zero if needed.
// trailingZero = minimal number of numbers after the dot, they will be right-padded with zero if needed.
// onlyIfDecimal: set it to true if you don't want right padding zero when there is no decimal
StringNumber.prototype.toNoExp =
StringNumber.prototype.toNoExpString = function( leadingZero = 1 , trailingZero = 0 , onlyIfDecimal = false , forcePlusSign = false , exposant = this.exposant ) {
	if ( this.special !== null ) { return '' + this.special ; }

	var integerDigits = [] , decimalDigits = [] ,
		str = this.sign < 0 ? '-' : forcePlusSign ? '+' : '' ;

	if ( ! this.digits.length ) {
		if ( leadingZero > 1 ) {
			this.fillZeroes( integerDigits , leadingZero - 1 , leadingZero ) ;
		}

		integerDigits.push( this.numeralZero ?? this.placeNumerals?.[ 0 ]?.[ 0 ] ?? this.numerals[ 0 ] ) ;

		if ( trailingZero && ! onlyIfDecimal ) {
			this.fillZeroes( decimalDigits , trailingZero ) ;
		}
	}
	else if ( exposant <= 0 ) {
		// This number is of type 0.[0...]xyz
		this.fillZeroes( integerDigits , leadingZero ) ;

		this.fillZeroes( decimalDigits , -exposant , trailingZero - this.digits.length ) ;
		this.appendNumerals( decimalDigits , this.digits , undefined , undefined , -exposant - 1 ) ;

		if ( trailingZero && this.digits.length - exposant < trailingZero ) {
			this.fillZeroes( decimalDigits , trailingZero - this.digits.length + exposant ) ;
		}
	}
	else if ( exposant >= this.digits.length ) {
		// This number is of type xyz[0...]
		if ( exposant < leadingZero ) { this.fillZeroes( integerDigits , leadingZero - exposant , exposant - 1 ) ; }
		this.appendNumerals( integerDigits , this.digits , undefined , undefined , exposant - 1 ) ;
		this.fillZeroes( integerDigits , exposant - this.digits.length ) ;

		if ( trailingZero && ! onlyIfDecimal ) {
			this.fillZeroes( decimalDigits , trailingZero ) ;
		}
	}
	else {
		// Here the digits are splitted with a dot in the middle
		if ( exposant < leadingZero ) { this.fillZeroes( integerDigits , leadingZero - exposant ) ; }
		this.appendNumerals( integerDigits , this.digits , 0 , exposant , exposant - 1 ) ;

		this.appendNumerals( decimalDigits , this.digits , exposant , undefined , this.digits.length - exposant ) ;

		if (
			trailingZero && this.digits.length - exposant < trailingZero
			&& ( ! onlyIfDecimal || this.digits.length - exposant > 0 )
		) {
			this.fillZeroes( decimalDigits , trailingZero - this.digits.length + exposant ) ;
		}
	}

	str += this.groupSeparator ?
		this.groupDigits( integerDigits , this.groupSeparator ) :
		integerDigits.join( '' ) ;

	if ( decimalDigits.length ) {
		str += this.decimalSeparator + (
			this.decimalGroupSeparator ?
				this.groupDigits( decimalDigits , this.decimalGroupSeparator ) :
				decimalDigits.join( '' )
		) ;
	}
	else if ( this.forceDecimalSeparator ) {
		str += this.decimalSeparator ;
	}

	return str ;
} ;



// Metric prefix
const MUL_PREFIX = [ '' , 'k' , 'M' , 'G' , 'T' , 'P' , 'E' , 'Z' , 'Y' ] ;
const SUB_MUL_PREFIX = [ '' , 'm' , 'µ' , 'n' , 'p' , 'f' , 'a' , 'z' , 'y' ] ;



StringNumber.prototype.toMetric =
StringNumber.prototype.toMetricString = function( leadingZero = 1 , trailingZero = 0 , onlyIfDecimal = false , forcePlusSign = false ) {
	if ( this.special !== null ) { return '' + this.special ; }
	if ( ! this.digits.length ) { return this.sign > 0 ? '0' : '-0' ; }

	var prefix = '' , fakeExposant ;

	if ( this.exposant > 0 ) {
		fakeExposant = 1 + ( ( this.exposant - 1 ) % 3 ) ;
		prefix = MUL_PREFIX[ Math.floor( ( this.exposant - 1 ) / 3 ) ] ;
		// Fallback to scientific if the number is to big
		if ( prefix === undefined ) { return this.toScientificString() ; }
	}
	else {
		fakeExposant = 3 - ( -this.exposant % 3 ) ;
		prefix = SUB_MUL_PREFIX[ 1 + Math.floor( -this.exposant / 3 ) ] ;
		// Fallback to scientific if the number is to small
		if ( prefix === undefined ) { return this.toScientificString() ; }
	}

	return this.toNoExpString( leadingZero , trailingZero , onlyIfDecimal , forcePlusSign , fakeExposant ) + prefix ;
} ;



/*
	type: 0=round, -1=floor, 1=ceil
	Floor if < .99999
	Ceil if >= .00001
*/
StringNumber.prototype.precision = function( n , type = 0 ) {
	var roundUp ;

	if ( this.special !== null || n >= this.digits.length ) { return this ; }

	if ( n < 0 ) { this.digits.length = 0 ; return this ; }

	type *= this.sign ;

	if ( type < 0 ) {
		roundUp =
			this.digits.length > n + 4
			&& this.digits[ n ] === 9 && this.digits[ n + 1 ] === 9
			&& this.digits[ n + 2 ] === 9 && this.digits[ n + 3 ] === 9 && this.digits[ n + 4 ] === 9 ;
	}
	else if ( type > 0 ) {
		roundUp =
			this.digits[ n ] > 0 || this.digits[ n + 1 ] > 0
			|| this.digits[ n + 2 ] > 0 || this.digits[ n + 3 ] > 0 || this.digits[ n + 4 ] > 0 ;
	}
	else {
		roundUp = this.digits[ n ] >= 5 ;
	}

	if ( roundUp ) {
		let i = n - 1 ,
			done = false ;

		// Cascading increase
		for ( ; i >= 0 ; i -- ) {
			if ( this.digits[ i ] < 9 ) { this.digits[ i ] ++ ; done = true ; break ; }
			else { this.digits[ i ] = 0 ; }
		}

		if ( ! done ) {
			this.exposant ++ ;
			this.digits[ 0 ] = 1 ;
			this.digits.length = 1 ;
		}
		else {
			this.digits.length = i + 1 ;
		}
	}
	else {
		this.digits.length = n ;
		this.removeTrailingZero() ;
	}

	return this ;
} ;



StringNumber.prototype.round = function( decimalPlace = 0 , type = 0 ) {
	var n = this.exposant + decimalPlace ;
	return this.precision( n , type ) ;
} ;



StringNumber.prototype.floor = function( decimalPlace = 0 ) {
	var n = this.exposant + decimalPlace ;
	return this.precision( n , -1 ) ;
} ;



StringNumber.prototype.ceil = function( decimalPlace = 0 ) {
	var n = this.exposant + decimalPlace ;
	return this.precision( n , 1 ) ;
} ;



StringNumber.prototype.removeTrailingZero = function() {
	var i = this.digits.length - 1 ;
	while( i >= 0 && this.digits[ i ] === 0 ) { i -- ; }
	this.digits.length = i + 1 ;
} ;



const GROUP_SIZE = 3 ;

StringNumber.prototype.groupDigits = function( digits , separator , inverseOrder = false ) {
	var str = '' ,
		offset = inverseOrder ? 0 : GROUP_SIZE - ( digits.length % GROUP_SIZE ) ,
		i = 0 ,
		iMax = digits.length ;

	for ( ; i < iMax ; i ++ ) {
		str += i && ( ( i + offset ) % GROUP_SIZE === 0 ) ? separator + digits[ i ] : digits[ i ] ;
	}

	return str ;
} ;



StringNumber.prototype.appendNumerals = function( intoArray , sourceArray , start = 0 , end = sourceArray.length , leftPlace = end ) {
	//console.log( "appendNumerals:" , { intoArray , sourceArray , start , end , leftPlace } ) ;
	for ( let i = start , place = leftPlace ; i < end ; i ++ , place -- ) {
		let numerals = this.placeNumerals?.[ place ] ?? this.numerals ;
		intoArray.push( numerals[ sourceArray[ i ] ] ?? sourceArray[ i ] ) ;
	}

	return intoArray ;
} ;



StringNumber.prototype.fillZeroes = function( intoArray , count , leftPlace = count - 1 ) {
	//console.log( "fillZeroes:" , { intoArray , count , leftPlace } ) ;
	for ( let i = 0 , place = leftPlace ; i < count ; i ++ , place -- ) {
		let numerals = this.placeNumerals?.[ place ] ?? this.numerals ;
		intoArray.push( numerals[ 0 ] ?? 0 ) ;
	}

	return intoArray ;
} ;



const ROMAN_OPTIONS = {
	numeralZero: 'N' ,
	placeNumerals: [
		[ '' , 'I' , 'II' , 'III' , 'IV' , 'V' , 'VI' , 'VII' , 'VIII' , 'IX' ] ,
		[ '' , 'X' , 'XX' , 'XXX' , 'XL' , 'L' , 'LX' , 'LXX' , 'LXXX' , 'XC' ] ,
		[ '' , 'C' , 'CC' , 'CCC' , 'CD' , 'D' , 'DC' , 'DCC' , 'DCCC' , 'CM' ] ,
		[ '' , 'M' , 'MM' , 'MMM' , 'MMMM' , 'ↁ' , 'ↁↀ' , 'ↁↀↀ' , 'ↁↀↀↀ' , 'ↁↀↀↀↀ' ]
	]
} ;

const ADDITIVE_ROMAN_OPTIONS = {
	numeralZero: 'N' ,
	placeNumerals: [
		[ '' , 'I' , 'II' , 'III' , 'IIII' , 'V' , 'VI' , 'VII' , 'VIII' , 'VIIII' ] ,
		[ '' , 'X' , 'XX' , 'XXX' , 'XXXX' , 'L' , 'LX' , 'LXX' , 'LXXX' , 'LXXXX' ] ,
		[ '' , 'C' , 'CC' , 'CCC' , 'CCCC' , 'D' , 'DC' , 'DCC' , 'DCCC' , 'DCCCC' ] ,
		[ '' , 'M' , 'MM' , 'MMM' , 'MMMM' , 'ↁ' , 'ↁↀ' , 'ↁↀↀ' , 'ↁↀↀↀ' , 'ↁↀↀↀↀ' ]
	]
} ;

const APOSTROPHUS_ROMAN_OPTIONS = {
	numeralZero: 'N' ,
	placeNumerals: [
		[ '' , 'I' , 'II' , 'III' , 'IV' , 'V' , 'VI' , 'VII' , 'VIII' , 'IX' ] ,
		[ '' , 'X' , 'XX' , 'XXX' , 'XL' , 'L' , 'LX' , 'LXX' , 'LXXX' , 'XC' ] ,
		[ '' , 'C' , 'CC' , 'CCC' , 'CD' , 'D' , 'DC' , 'DCC' , 'DCCC' , 'CM' ] ,
		[ '' , 'M' , 'MM' , 'MMM' , 'MMMM' , 'IↃↃ' , 'IↃↃCIↃ' , 'IↃↃCIↃCIↃ' , 'IↃↃCIↃCIↃCIↃ' , 'IↃↃCIↃCIↃCIↃCIↃ' ] ,
		[ '' , 'CCIↃↃ' , 'CCIↃↃCCIↃↃ' , 'CCIↃↃCCIↃↃCCIↃↃ' , 'CCIↃↃCCIↃↃCCIↃↃCCIↃↃ' , 'IↃↃↃ' , 'IↃↃↃCCIↃↃ' , 'IↃↃↃCCIↃↃCCIↃↃ' , 'IↃↃↃCCIↃↃCCIↃↃCCIↃↃ' , 'IↃↃↃCCIↃↃCCIↃↃCCIↃↃCCIↃↃ' ] ,
		[ '' , 'CCCIↃↃↃ' , 'CCCIↃↃↃCCCIↃↃↃ' , 'CCCIↃↃↃCCCIↃↃↃCCCIↃↃↃ' , 'CCCIↃↃↃCCCIↃↃↃCCCIↃↃↃCCCIↃↃↃ' , 'IↃↃↃↃ' , 'IↃↃↃↃCCCIↃↃↃ' , 'IↃↃↃↃCCCIↃↃↃCCCIↃↃↃ' , 'IↃↃↃↃCCCIↃↃↃCCCIↃↃↃCCCIↃↃↃ' , 'IↃↃↃↃCCCIↃↃↃCCCIↃↃↃCCCIↃↃↃCCCIↃↃↃ' ]
	]
} ;

StringNumber.roman = ( number , options ) => {
	options = options ? Object.assign( {} , options , ROMAN_OPTIONS ) : ROMAN_OPTIONS ;
	return new StringNumber( number , options ) ;
} ;

StringNumber.additiveRoman = ( number , options ) => {
	options = options ? Object.assign( {} , options , ADDITIVE_ROMAN_OPTIONS ) : ADDITIVE_ROMAN_OPTIONS ;
	return new StringNumber( number , options ) ;
} ;


},{}],117:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



// To solve dependency hell, we do not rely on terminal-kit anymore.
const ansi = {
	reset: '\x1b[0m' ,
	bold: '\x1b[1m' ,
	dim: '\x1b[2m' ,
	italic: '\x1b[3m' ,
	underline: '\x1b[4m' ,
	inverse: '\x1b[7m' ,

	defaultColor: '\x1b[39m' ,
	black: '\x1b[30m' ,
	red: '\x1b[31m' ,
	green: '\x1b[32m' ,
	yellow: '\x1b[33m' ,
	blue: '\x1b[34m' ,
	magenta: '\x1b[35m' ,
	cyan: '\x1b[36m' ,
	white: '\x1b[37m' ,
	grey: '\x1b[90m' ,
	gray: '\x1b[90m' ,
	brightBlack: '\x1b[90m' ,
	brightRed: '\x1b[91m' ,
	brightGreen: '\x1b[92m' ,
	brightYellow: '\x1b[93m' ,
	brightBlue: '\x1b[94m' ,
	brightMagenta: '\x1b[95m' ,
	brightCyan: '\x1b[96m' ,
	brightWhite: '\x1b[97m' ,

	defaultBgColor: '\x1b[49m' ,
	bgBlack: '\x1b[40m' ,
	bgRed: '\x1b[41m' ,
	bgGreen: '\x1b[42m' ,
	bgYellow: '\x1b[43m' ,
	bgBlue: '\x1b[44m' ,
	bgMagenta: '\x1b[45m' ,
	bgCyan: '\x1b[46m' ,
	bgWhite: '\x1b[47m' ,
	bgGrey: '\x1b[100m' ,
	bgGray: '\x1b[100m' ,
	bgBrightBlack: '\x1b[100m' ,
	bgBrightRed: '\x1b[101m' ,
	bgBrightGreen: '\x1b[102m' ,
	bgBrightYellow: '\x1b[103m' ,
	bgBrightBlue: '\x1b[104m' ,
	bgBrightMagenta: '\x1b[105m' ,
	bgBrightCyan: '\x1b[106m' ,
	bgBrightWhite: '\x1b[107m'
} ;

module.exports = ansi ;



ansi.fgColor = {
	defaultColor: ansi.defaultColor ,
	black: ansi.black ,
	red: ansi.red ,
	green: ansi.green ,
	yellow: ansi.yellow ,
	blue: ansi.blue ,
	magenta: ansi.magenta ,
	cyan: ansi.cyan ,
	white: ansi.white ,
	grey: ansi.grey ,
	gray: ansi.gray ,
	brightBlack: ansi.brightBlack ,
	brightRed: ansi.brightRed ,
	brightGreen: ansi.brightGreen ,
	brightYellow: ansi.brightYellow ,
	brightBlue: ansi.brightBlue ,
	brightMagenta: ansi.brightMagenta ,
	brightCyan: ansi.brightCyan ,
	brightWhite: ansi.brightWhite
} ;



ansi.bgColor = {
	defaultColor: ansi.defaultBgColor ,
	black: ansi.bgBlack ,
	red: ansi.bgRed ,
	green: ansi.bgGreen ,
	yellow: ansi.bgYellow ,
	blue: ansi.bgBlue ,
	magenta: ansi.bgMagenta ,
	cyan: ansi.bgCyan ,
	white: ansi.bgWhite ,
	grey: ansi.bgGrey ,
	gray: ansi.bgGray ,
	brightBlack: ansi.bgBrightBlack ,
	brightRed: ansi.bgBrightRed ,
	brightGreen: ansi.bgBrightGreen ,
	brightYellow: ansi.bgBrightYellow ,
	brightBlue: ansi.bgBrightBlue ,
	brightMagenta: ansi.bgBrightMagenta ,
	brightCyan: ansi.bgBrightCyan ,
	brightWhite: ansi.bgBrightWhite
} ;



ansi.trueColor = ( r , g , b ) => {
	if ( g === undefined && typeof r === 'string' ) {
		let hex = r ;
		if ( hex[ 0 ] === '#' ) { hex = hex.slice( 1 ) ; }	// Strip the # if necessary
		if ( hex.length === 3 ) { hex = hex[ 0 ] + hex[ 0 ] + hex[ 1 ] + hex[ 1 ] + hex[ 2 ] + hex[ 2 ] ; }
		r = parseInt( hex.slice( 0 , 2 ) , 16 ) || 0 ;
		g = parseInt( hex.slice( 2 , 4 ) , 16 ) || 0 ;
		b = parseInt( hex.slice( 4 , 6 ) , 16 ) || 0 ;
	}

	return '\x1b[38;2;' + r + ';' + g + ';' + b + 'm' ;
} ;



ansi.bgTrueColor = ( r , g , b ) => {
	if ( g === undefined && typeof r === 'string' ) {
		let hex = r ;
		if ( hex[ 0 ] === '#' ) { hex = hex.slice( 1 ) ; }	// Strip the # if necessary
		if ( hex.length === 3 ) { hex = hex[ 0 ] + hex[ 0 ] + hex[ 1 ] + hex[ 1 ] + hex[ 2 ] + hex[ 2 ] ; }
		r = parseInt( hex.slice( 0 , 2 ) , 16 ) || 0 ;
		g = parseInt( hex.slice( 2 , 4 ) , 16 ) || 0 ;
		b = parseInt( hex.slice( 4 , 6 ) , 16 ) || 0 ;
	}

	return '\x1b[48;2;' + r + ';' + g + ';' + b + 'm' ;
} ;



const ANSI_CODES = {
	'0': null ,

	'1': { bold: true } ,
	'2': { dim: true } ,
	'22': { bold: false , dim: false } ,
	'3': { italic: true } ,
	'23': { italic: false } ,
	'4': { underline: true } ,
	'24': { underline: false } ,
	'5': { blink: true } ,
	'25': { blink: false } ,
	'7': { inverse: true } ,
	'27': { inverse: false } ,
	'8': { hidden: true } ,
	'28': { hidden: false } ,
	'9': { strike: true } ,
	'29': { strike: false } ,

	'30': { color: 0 } ,
	'31': { color: 1 } ,
	'32': { color: 2 } ,
	'33': { color: 3 } ,
	'34': { color: 4 } ,
	'35': { color: 5 } ,
	'36': { color: 6 } ,
	'37': { color: 7 } ,
	//'39': { defaultColor: true } ,
	'39': { color: 'default' } ,

	'90': { color: 8 } ,
	'91': { color: 9 } ,
	'92': { color: 10 } ,
	'93': { color: 11 } ,
	'94': { color: 12 } ,
	'95': { color: 13 } ,
	'96': { color: 14 } ,
	'97': { color: 15 } ,

	'40': { bgColor: 0 } ,
	'41': { bgColor: 1 } ,
	'42': { bgColor: 2 } ,
	'43': { bgColor: 3 } ,
	'44': { bgColor: 4 } ,
	'45': { bgColor: 5 } ,
	'46': { bgColor: 6 } ,
	'47': { bgColor: 7 } ,
	//'49': { bgDefaultColor: true } ,
	'49': { bgColor: 'default' } ,

	'100': { bgColor: 8 } ,
	'101': { bgColor: 9 } ,
	'102': { bgColor: 10 } ,
	'103': { bgColor: 11 } ,
	'104': { bgColor: 12 } ,
	'105': { bgColor: 13 } ,
	'106': { bgColor: 14 } ,
	'107': { bgColor: 15 }
} ;



// Parse ANSI codes, output is compatible with the markup parser
ansi.parse = str => {
	var ansiCodes , raw , part , style , output = [] ;

	for ( [ , ansiCodes , raw ] of str.matchAll( /\x1b\[([0-9;]+)m|(.[^\x1b]*)/g ) ) {
		if ( raw ) {
			if ( output.length ) { output[ output.length - 1 ].text += raw ; }
			else { output.push( { text: raw } ) ; }
		}
		else {
			ansiCodes.split( ';' ).forEach( ansiCode => {
				style = ANSI_CODES[ ansiCode ] ;
				if ( style === undefined ) { return ; }

				if ( ! output.length || output[ output.length - 1 ].text ) {
					if ( ! style ) {
						part = { text: '' } ;
					}
					else {
						part = Object.assign( {} , part , style ) ;
						part.text = '' ;
					}

					output.push( part ) ;
				}
				else {
					// There is no text, no need to create a new part
					if ( ! style ) {
						// Replace the last part
						output[ output.length - 1 ] = { text: '' } ;
					}
					else {
						// update the last part
						Object.assign( part , style ) ;
					}
				}
			} ) ;
		}
	}

	return output ;
} ;


},{}],118:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



var camel = {} ;
module.exports = camel ;



// Transform alphanum separated by underscore or minus to camel case
camel.toCamelCase = function( str , preserveUpperCase = false , initialUpperCase = false ) {
	if ( ! str || typeof str !== 'string' ) { return '' ; }

	return str.replace(
		/(?:^[\s_-]*|([\s_-]+))(([^\s_-]?)([^\s_-]*))/g ,
		( match , isNotFirstWord , word , firstLetter , endOfWord ) => {
			if ( preserveUpperCase ) {
				if ( ! isNotFirstWord && ! initialUpperCase ) { return word ; }
				if ( ! firstLetter ) { return '' ; }
				return firstLetter.toUpperCase() + endOfWord ;
			}

			if ( ! isNotFirstWord && ! initialUpperCase ) { return word.toLowerCase() ; }
			if ( ! firstLetter ) { return '' ; }
			return firstLetter.toUpperCase() + endOfWord.toLowerCase() ;
		}
	) ;
} ;



camel.camelCaseToSeparated = function( str , separator = ' ' , acronym = true ) {
	if ( ! str || typeof str !== 'string' ) { return '' ; }

	if ( ! acronym ) {
		return str.replace( /^([A-Z])|([A-Z])/g , ( match , firstLetter , letter ) => {
			if ( firstLetter ) { return firstLetter.toLowerCase() ; }
			return separator + letter.toLowerCase() ;
		} ) ;
	}

	// (^)? and (^)? does not work, so we have to use (?:(^)|)) and (?:($)|)) to capture end or not
	return str.replace( /(?:(^)|)([A-Z]+)(?:($)|(?=[a-z]))/g , ( match , isStart , letters , isEnd ) => {
		isStart = isStart === '' ;
		isEnd = isEnd === '' ;

		var prefix = isStart ? '' : separator ;

		return letters.length === 1 ? prefix + letters.toLowerCase() :
			isEnd ? prefix + letters :
			letters.length === 2 ? prefix + letters[ 0 ].toLowerCase() + separator + letters[ 1 ].toLowerCase() :
			prefix + letters.slice( 0 , -1 ) + separator + letters.slice( -1 ).toLowerCase() ;
	} ) ;
} ;



// Transform camel case to alphanum separated by minus
camel.camelCaseToDash =
camel.camelCaseToDashed = ( str ) => camel.camelCaseToSeparated( str , '-' , false ) ;


},{}],119:[function(require,module,exports){
/*
	Book Source

	Copyright (c) 2023 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const latinize = require( './latinize.js' ) ;
const english = require( './english.js' ) ;

const KEYWORD_TO_CHARLIST = require( './json-data/emoji-keyword-to-charlist.json' ) ;
const CHAR_TO_CANONICAL_NAME = require( './json-data/emoji-char-to-canonical-name.json' ) ;



const emoji = {} ;
module.exports = emoji ;



emoji.getCanonicalName = emojiChar => CHAR_TO_CANONICAL_NAME[ emojiChar ] ;



const emojiToKeywordsCache = {} ;

// Return a cached and frozen array
emoji.getKeywords = emojiChar => {
	if ( ! CHAR_TO_CANONICAL_NAME[ emojiChar ] ) { return ; }

	if ( ! emojiToKeywordsCache[ emojiChar ] ) {
		emojiToKeywordsCache[ emojiChar ] = emoji.splitIntoKeywords( CHAR_TO_CANONICAL_NAME[ emojiChar ] ) ;
		Object.freeze( emojiToKeywordsCache[ emojiChar ] ) ;
	}

	return emojiToKeywordsCache[ emojiChar ] ;
} ;



emoji.search = ( name , bestOnly = false ) => {
	var keywords = emoji.splitIntoKeywords( name ) ,
		matches = {} ,
		score ,
		bestScore = 0 ;

	for ( let keyword of keywords ) {
		if ( ! KEYWORD_TO_CHARLIST[ keyword ] ) { continue ; }

		for ( let emojiChar of KEYWORD_TO_CHARLIST[ keyword ] ) {
			if ( ! matches[ emojiChar ] ) {
				score = 1 ;
				matches[ emojiChar ] = {
					emoji: emojiChar ,
					score ,
					canonical: CHAR_TO_CANONICAL_NAME[ emojiChar ] ,
					keywords: emoji.getKeywords( emojiChar )
				} ;
			}
			else {
				score = matches[ emojiChar ].score + 1 ;
				matches[ emojiChar ].score = score ;
			}

			if ( score > bestScore ) { bestScore = score ; }
		}
	}

	var results = [ ... Object.values( matches ) ] ;
	if ( bestOnly ) { results = results.filter( e => e.score === bestScore ) ; }
	results.sort( ( a , b ) => ( b.score - a.score ) || ( a.keywords.length - b.keywords.length ) ) ;
	return results ;
} ;

emoji.searchBest = name => emoji.search( name , true ) ;
emoji.get = name => emoji.search( name , true )[ 0 ]?.emoji ;





// Internal API, exposed because it is used by the builder



emoji.simplifyName = name => {
	name = name.toLowerCase().replace( /[“”().!]/g , '' ).replace( /[ ’',]/g , '-' ).replace( /-+/g , '-' ) ;
	name = latinize( name ) ;
	return name ;
} ;



emoji.simplifyKeyword = inputKeyword => {
	var kw = inputKeyword ;
	kw = english.undoPresentParticiple( kw ) ;

	//if ( kw !== inputKeyword ) { console.log( "Changing KW:" , inputKeyword , "-->" , kw ) ; }
	return kw ;
} ;



const KEYWORD_EXCLUSION = new Set( [ 'with' , 'of' ] ) ;

emoji.splitIntoKeywords = ( name , noSimplify = false ) => {
	if ( ! noSimplify ) { name = emoji.simplifyName( name ) ; }
	var keywords = name.split( /-/g ).filter( keyword => keyword.length >= 2 && ! KEYWORD_EXCLUSION.has( keyword ) ) ;
	keywords = keywords.map( emoji.simplifyKeyword ) ;
	keywords = [ ... new Set( keywords ) ] ;	// Make them unique
	return keywords ;
} ;


},{"./english.js":120,"./json-data/emoji-char-to-canonical-name.json":125,"./json-data/emoji-keyword-to-charlist.json":126,"./latinize.js":129}],120:[function(require,module,exports){
/*
	Book Source

	Copyright (c) 2023 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const english = {} ;
module.exports = english ;



const VOWELS = new Set( [ 'a' , 'e' , 'i' , 'o' , 'u' , 'y' ] ) ;
const VOWELS_ADDING_E = new Set( [ 'a' , 'i' , 'o' , 'u' ] ) ;
const CONSONANTS_AFTER_VOWEL_ADDING_E = new Set( [ 's' ] ) ;
const CONSONANTS_ADDING_E = new Set( [ 'v' ] ) ;
const CONSONANTS_NOT_ADDING_E = new Set( [ 'w' , 'x' ] ) ;
const DOUBLE_CONSONANTS_ADDING_E = new Set( [ 'cl' , 'dl' , 'gl' , 'kl' , 'nc' , 'pl' , 'tl' ] ) ;
const REDUCING_DOUBLE_CONSONANTS = new Set( [ 'd' , 'g' , 'm' , 'n' , 'p' ] ) ;
const CONSONANTS_AFTER_VOWELS_COMBO_NOT_ADDING_E = new Set( [ 'or' ] ) ;	// Last chance fix

// Remove -ing and transform to a proper verb or noun
english.undoPresentParticiple = word => {
	if ( word.endsWith( 'ing' ) && word.length >= 6 && ! word.endsWith( 'ghtning' ) ) {
		let ingSuffix = 'ing' ;
		//if ( word.endsWith( 'ling' ) ) { ingSuffix = 'ling' ; }

		let ingLen = ingSuffix.length ;

		let before = word[ word.length - ingLen - 1 ] ,
			before2 = word[ word.length - ingLen - 2 ] ,
			before3 = word[ word.length - ingLen - 3 ] ;

		if ( VOWELS.has( before ) ) {
			word = word.slice( 0 , -ingLen ) ;
		}
		else if ( VOWELS.has( before2 ) ) {
			if ( VOWELS.has( before3 ) ) {
				if ( CONSONANTS_AFTER_VOWEL_ADDING_E.has( before ) && ! CONSONANTS_AFTER_VOWELS_COMBO_NOT_ADDING_E.has( before2 + before ) ) {
					word = word.slice( 0 , -ingLen ) + 'e' ;
				}
				else {
					word = word.slice( 0 , -ingLen ) ;
				}
			}
			else if ( VOWELS_ADDING_E.has( before2 ) && ! CONSONANTS_NOT_ADDING_E.has( before ) ) {
				word = word.slice( 0 , -ingLen ) + 'e' ;
			}
			else {
				word = word.slice( 0 , -ingLen ) ;
			}
		}
		else if ( before === before2 && REDUCING_DOUBLE_CONSONANTS.has( before ) ) {
			word = word.slice( 0 , -ingLen - 1 ) ;
		}
		else if ( CONSONANTS_ADDING_E.has( before ) || DOUBLE_CONSONANTS_ADDING_E.has( before2 + before ) ) {
			word = word.slice( 0 , -ingLen ) + 'e' ;
		}
		else {
			word = word.slice( 0 , -ingLen ) ;
		}
	}

	return word ;
} ;


},{}],121:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

/*
	Escape collection.
*/



"use strict" ;



// From Mozilla Developper Network
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
exports.regExp = exports.regExpPattern = str => str.replace( /([.*+?^${}()|[\]/\\])/g , '\\$1' ) ;

// This replace any single $ by a double $$
exports.regExpReplacement = str => str.replace( /\$/g , '$$$$' ) ;

// Escape for string.format()
// This replace any single % by a double %%
exports.format = str => str.replace( /%/g , '%%' ) ;

exports.jsSingleQuote = str => exports.control( str ).replace( /'/g , "\\'" ) ;
exports.jsDoubleQuote = str => exports.control( str ).replace( /"/g , '\\"' ) ;

exports.shellArg = str => '\'' + str.replace( /'/g , "'\\''" ) + '\'' ;



var escapeControlMap = {
	'\r': '\\r' ,
	'\n': '\\n' ,
	'\t': '\\t' ,
	'\x7f': '\\x7f'
} ;

// Escape \r \n \t so they become readable again, escape all ASCII control character as well, using \x syntaxe
exports.control = ( str , keepNewLineAndTab = false ) => str.replace( /[\x00-\x1f\x7f]/g , match => {
	if ( keepNewLineAndTab && ( match === '\n' || match === '\t' ) ) { return match ; }
	if ( escapeControlMap[ match ] !== undefined ) { return escapeControlMap[ match ] ; }
	var hex = match.charCodeAt( 0 ).toString( 16 ) ;
	if ( hex.length % 2 ) { hex = '0' + hex ; }
	return '\\x' + hex ;
} ) ;



var escapeHtmlMap = {
	'&': '&amp;' ,
	'<': '&lt;' ,
	'>': '&gt;' ,
	'"': '&quot;' ,
	"'": '&#039;'
} ;

// Only escape & < > so this is suited for content outside tags
exports.html = str => str.replace( /[&<>]/g , match => escapeHtmlMap[ match ] ) ;

// Escape & < > " so this is suited for content inside a double-quoted attribute
exports.htmlAttr = str => str.replace( /[&<>"]/g , match => escapeHtmlMap[ match ] ) ;

// Escape all html special characters & < > " '
exports.htmlSpecialChars = str => str.replace( /[&<>"']/g , match => escapeHtmlMap[ match ] ) ;

// Percent-encode all control chars and codepoint greater than 255 using percent encoding
exports.unicodePercentEncode = str => str.replace( /[\x00-\x1f\u0100-\uffff\x7f%]/g , match => {
	try {
		return encodeURI( match ) ;
	}
	catch ( error ) {
		// encodeURI can throw on bad surrogate pairs, but we just strip those characters
		return '' ;
	}
} ) ;

// Encode HTTP header value
exports.httpHeaderValue = str => exports.unicodePercentEncode( str ) ;


},{}],122:[function(require,module,exports){
(function (Buffer){(function (){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

/*
	String formater, inspired by C's sprintf().
*/



"use strict" ;



const inspect = require( './inspect.js' ).inspect ;
const inspectError = require( './inspect.js' ).inspectError ;
const escape = require( './escape.js' ) ;
const ansi = require( './ansi.js' ) ;
const unicode = require( './unicode.js' ) ;
const naturalSort = require( './naturalSort.js' ) ;
const StringNumber = require( './StringNumber.js' ) ;



/*
	%%		a single %
	%s		string
	%S		string, interpret ^ formatting
	%r		raw string: without sanitizer
	%n		natural: output the most natural representation for this type, object entries are sorted by keys
	%N		even more natural: avoid type hinting marks like bracket for array
	%f		float
	%k		number with metric system prefixes
	%e		for exponential notation (e.g. 1.23e+2)
	%K		for scientific notation (e.g. 1.23 × 10²)
	%i	%d	integer
	%u		unsigned integer
	%U		unsigned positive integer (>0)
	%P		number to (absolute) percent (e.g.: 0.75 -> 75%)
	%p		number to relative percent (e.g.: 1.25 -> +25% ; 0.75 -> -25%)
	%T		date/time display, using ISO date, or Intl module
	%t		time duration, convert ms into h min s, e.g.: 2h14min52s or 2:14:52
	%m		convert degree into degree, minutes and seconds
	%h		hexadecimal (input is a number)
	%x		hexadecimal (input is a number), force pair of symbols (e.g. 'f' -> '0f')
	%o		octal
	%b		binary
	%X		hexadecimal: convert a string into hex charcode, force pair of symbols (e.g. 'f' -> '0f')
	%z		base64
	%Z		base64url
	%I		call string-kit's inspect()
	%Y		call string-kit's inspect(), but do not inspect non-enumerable
	%O		object (like inspect, but with ultra minimal options)
	%E		call string-kit's inspectError()
	%J		JSON.stringify()
	%v		roman numerals, additive variant (e.g. 4 is IIII instead of IV)
	%V		roman numerals
	%D		drop
	%F		filter function existing in the 'this' context, e.g. %[filter:%a%a]F
	%a		argument for a function

	Candidate format:
	%A		for automatic type? probably not good: it's like %n Natural
	%c		for char? (can receive a string or an integer translated into an UTF8 chars)
	%C		for currency formating?
	%B		for Buffer objects?
*/

exports.formatMethod = function( ... args ) {
	var arg ,
		str = args[ 0 ] ,
		autoIndex = 1 ,
		length = args.length ;

	if ( typeof str !== 'string' ) {
		if ( ! str ) { str = '' ; }
		else if ( typeof str.toString === 'function' ) { str = str.toString() ; }
		else { str = '' ; }
	}

	var runtime = {
		hasMarkup: false ,
		shift: null ,
		markupStack: []
	} ;

	if ( this.markupReset && this.startingMarkupReset ) {
		str = ( typeof this.markupReset === 'function' ? this.markupReset( runtime.markupStack ) : this.markupReset ) + str ;
	}

	//console.log( 'format args:' , arguments ) ;

	// /!\ each changes here should be reported on string.format.count() and string.format.hasFormatting() too /!\
	// Note: the closing bracket is optional to prevent ReDoS
	str = str.replace( /\^\[([^\]]*)]?|\^(.)|(%%)|%([+-]?)([0-9]*)(?:\[([^\]]*)\])?([a-zA-Z])/g ,
		( match , complexMarkup , markup , doublePercent , relative , index , modeArg , mode ) => {
			var replacement , i , tmp , fn , fnArgString , argMatches , argList = [] ;

			//console.log( 'replaceArgs:' , arguments ) ;
			if ( doublePercent ) { return '%' ; }

			if ( complexMarkup ) { markup = complexMarkup ; }
			if ( markup ) {
				if ( this.noMarkup ) { return '^' + markup ; }
				return markupReplace.call( this , runtime , match , markup ) ;
			}

			if ( index ) {
				index = parseInt( index , 10 ) ;

				if ( relative ) {
					if ( relative === '+' ) { index = autoIndex + index ; }
					else if ( relative === '-' ) { index = autoIndex - index ; }
				}
			}
			else {
				index = autoIndex ;
			}

			autoIndex ++ ;

			if ( index >= length || index < 1 ) { arg = undefined ; }
			else { arg = args[ index ] ; }

			if ( modes[ mode ] ) {
				replacement = modes[ mode ]( arg , modeArg , this ) ;
				if ( this.argumentSanitizer && ! modes[ mode ].noSanitize ) { replacement = this.argumentSanitizer( replacement ) ; }
				if ( this.escapeMarkup && ! modes[ mode ].noEscapeMarkup ) { replacement = exports.escapeMarkup( replacement ) ; }
				if ( modeArg && ! modes[ mode ].noCommonModeArg ) { replacement = commonModeArg( replacement , modeArg ) ; }
				return replacement ;
			}

			// Function mode
			if ( mode === 'F' ) {
				autoIndex -- ;	// %F does not eat any arg

				if ( modeArg === undefined ) { return '' ; }
				tmp = modeArg.split( ':' ) ;
				fn = tmp[ 0 ] ;
				fnArgString = tmp[ 1 ] ;
				if ( ! fn ) { return '' ; }

				if ( fnArgString && ( argMatches = fnArgString.match( /%([+-]?)([0-9]*)[a-zA-Z]/g ) ) ) {
					//console.log( argMatches ) ;
					//console.log( fnArgString ) ;
					for ( i = 0 ; i < argMatches.length ; i ++ ) {
						relative = argMatches[ i ][ 1 ] ;
						index = argMatches[ i ][ 2 ] ;

						if ( index ) {
							index = parseInt( index , 10 ) ;

							if ( relative ) {
								if ( relative === '+' ) { index = autoIndex + index ; }		// jshint ignore:line
								else if ( relative === '-' ) { index = autoIndex - index ; }	// jshint ignore:line
							}
						}
						else {
							index = autoIndex ;
						}

						autoIndex ++ ;

						if ( index >= length || index < 1 ) { argList[ i ] = undefined ; }
						else { argList[ i ] = args[ index ] ; }
					}
				}

				if ( ! this || ! this.fn || typeof this.fn[ fn ] !== 'function' ) { return '' ; }
				return this.fn[ fn ].apply( this , argList ) ;
			}

			return '' ;
		}
	) ;

	if ( runtime.hasMarkup && this.markupReset && this.endingMarkupReset ) {
		str += typeof this.markupReset === 'function' ? this.markupReset( runtime.markupStack ) : this.markupReset ;
	}

	if ( this.extraArguments ) {
		for ( ; autoIndex < length ; autoIndex ++ ) {
			arg = args[ autoIndex ] ;
			if ( arg === null || arg === undefined ) { continue ; }
			else if ( typeof arg === 'string' ) { str += arg ; }
			else if ( typeof arg === 'number' ) { str += arg ; }
			else if ( typeof arg.toString === 'function' ) { str += arg.toString() ; }
		}
	}

	return str ;
} ;



exports.markupMethod = function( str ) {
	if ( typeof str !== 'string' ) {
		if ( ! str ) { str = '' ; }
		else if ( typeof str.toString === 'function' ) { str = str.toString() ; }
		else { str = '' ; }
	}

	var runtime = {
		hasMarkup: false ,
		shift: null ,
		markupStack: []
	} ;

	if ( this.parse ) {
		let markupObjects , markupObject , match , complexMarkup , markup , raw , lastChunk ,
			output = [] ;

		// Note: the closing bracket is optional to prevent ReDoS
		for ( [ match , complexMarkup , markup , raw ] of str.matchAll( /\^\[([^\]]*)]?|\^(.)|([^^]+)/g ) ) {
			if ( raw ) {
				if ( output.length ) { output[ output.length - 1 ].text += raw ; }
				else { output.push( { text: raw } ) ; }
				continue ;
			}

			if ( complexMarkup ) { markup = complexMarkup ; }
			markupObjects = markupReplace.call( this , runtime , match , markup ) ;

			if ( ! Array.isArray( markupObjects ) ) { markupObjects = [ markupObjects ] ; }

			for ( markupObject of markupObjects ) {
				lastChunk = output.length ? output[ output.length - 1 ] : null ;
				if ( typeof markupObject === 'string' ) {
					// This markup is actually a text to add to the last chunk (e.g. "^^" markup is converted to a single "^")
					if ( lastChunk ) { lastChunk.text += markupObject ; }
					else { output.push( { text: markupObject } ) ; }
				}
				else if ( ! markupObject ) {
					// Null is for a markup's style reset
					if ( lastChunk && lastChunk.text.length && Object.keys( lastChunk ).length > 1 ) {
						// If there was style and text on the last chunk, then this means that the new markup starts a new chunk
						// markupObject can be null for markup reset function, but we have to create a new chunk
						output.push( { text: '' } ) ;
					}
				}
				else {
					if ( lastChunk && lastChunk.text.length ) {
						// If there was text on the last chunk, then this means that the new markup starts a new chunk
						output.push( Object.assign( { text: '' } , ... runtime.markupStack ) ) ;
					}
					else {
						// There wasn't any text added, so append the current markup style to the current chunk
						if ( lastChunk ) { Object.assign( lastChunk , markupObject ) ; }
						else { output.push( Object.assign( { text: '' } , markupObject ) ) ; }
					}
				}
			}
		}

		return output ;
	}

	if ( this.markupReset && this.startingMarkupReset ) {
		str = ( typeof this.markupReset === 'function' ? this.markupReset( runtime.markupStack ) : this.markupReset ) + str ;
	}

	str = str.replace( /\^\[([^\]]*)]?|\^(.)/g , ( match , complexMarkup , markup ) => markupReplace.call( this , runtime , match , complexMarkup || markup ) ) ;

	if ( runtime.hasMarkup && this.markupReset && this.endingMarkupReset ) {
		str += typeof this.markupReset === 'function' ? this.markupReset( runtime.markupStack ) : this.markupReset ;
	}

	return str ;
} ;



// Used by both formatMethod and markupMethod
function markupReplace( runtime , match , markup ) {
	var markupTarget , key , value , replacement , colonIndex ;

	if ( markup === '^' ) { return '^' ; }

	if ( this.shiftMarkup && this.shiftMarkup[ markup ] ) {
		runtime.shift = this.shiftMarkup[ markup ] ;
		return '' ;
	}

	if ( markup.length > 1 && this.dataMarkup && ( colonIndex = markup.indexOf( ':' ) ) !== -1 ) {
		key = markup.slice( 0 , colonIndex ) ;
		markupTarget = this.dataMarkup[ key ] ;

		if ( markupTarget === undefined ) {
			if ( this.markupCatchAll === undefined ) { return '' ; }
			markupTarget = this.markupCatchAll ;
		}

		runtime.hasMarkup = true ;
		value = markup.slice( colonIndex + 1 ) ;

		if ( typeof markupTarget === 'function' ) {
			replacement = markupTarget( runtime.markupStack , key , value ) ;
			// method should manage markup stack themselves
		}
		else {
			replacement = { [ markupTarget ]: value } ;
			stackMarkup( runtime , replacement ) ;
		}

		return replacement ;
	}

	if ( runtime.shift ) {
		markupTarget = this.shiftedMarkup?.[ runtime.shift ]?.[ markup ] ;
		runtime.shift = null ;
	}
	else {
		markupTarget = this.markup?.[ markup ] ;
	}

	if ( markupTarget === undefined ) {
		if ( this.markupCatchAll === undefined ) { return '' ; }
		markupTarget = this.markupCatchAll ;
	}

	runtime.hasMarkup = true ;

	if ( typeof markupTarget === 'function' ) {
		replacement = markupTarget( runtime.markupStack , markup ) ;
		// method should manage markup stack themselves
	}
	else {
		replacement = markupTarget ;
		stackMarkup( runtime , replacement ) ;
	}

	return replacement ;
}



// internal method for markupReplace()
function stackMarkup( runtime , replacement ) {
	if ( Array.isArray( replacement ) ) {
		for ( let item of replacement ) {
			if ( item === null ) { runtime.markupStack.length = 0 ; }
			else { runtime.markupStack.push( item ) ; }
		}
	}
	else {
		if ( replacement === null ) { runtime.markupStack.length = 0 ; }
		else { runtime.markupStack.push( replacement ) ; }
	}
}



// Note: the closing bracket is optional to prevent ReDoS
exports.stripMarkup = str => str.replace( /\^\[[^\]]*]?|\^./g , match =>
	match === '^^' ? '^' :
	match === '^ ' ? ' ' :
	''
) ;

exports.escapeMarkup = str => str.replace( /\^/g , '^^' ) ;



const DEFAULT_FORMATTER = {
	argumentSanitizer: str => escape.control( str , true ) ,
	extraArguments: true ,
	color: false ,
	noMarkup: false ,
	escapeMarkup: false ,
	endingMarkupReset: true ,
	startingMarkupReset: false ,
	markupReset: ansi.reset ,
	shiftMarkup: {
		'#': 'background'
	} ,
	markup: {
		":": ansi.reset ,
		" ": ansi.reset + " " ,

		"-": ansi.dim ,
		"+": ansi.bold ,
		"_": ansi.underline ,
		"/": ansi.italic ,
		"!": ansi.inverse ,

		"b": ansi.blue ,
		"B": ansi.brightBlue ,
		"c": ansi.cyan ,
		"C": ansi.brightCyan ,
		"g": ansi.green ,
		"G": ansi.brightGreen ,
		"k": ansi.black ,
		"K": ansi.brightBlack ,
		"m": ansi.magenta ,
		"M": ansi.brightMagenta ,
		"r": ansi.red ,
		"R": ansi.brightRed ,
		"w": ansi.white ,
		"W": ansi.brightWhite ,
		"y": ansi.yellow ,
		"Y": ansi.brightYellow
	} ,
	shiftedMarkup: {
		background: {
			":": ansi.reset ,
			" ": ansi.reset + " " ,

			"b": ansi.bgBlue ,
			"B": ansi.bgBrightBlue ,
			"c": ansi.bgCyan ,
			"C": ansi.bgBrightCyan ,
			"g": ansi.bgGreen ,
			"G": ansi.bgBrightGreen ,
			"k": ansi.bgBlack ,
			"K": ansi.bgBrightBlack ,
			"m": ansi.bgMagenta ,
			"M": ansi.bgBrightMagenta ,
			"r": ansi.bgRed ,
			"R": ansi.bgBrightRed ,
			"w": ansi.bgWhite ,
			"W": ansi.bgBrightWhite ,
			"y": ansi.bgYellow ,
			"Y": ansi.bgBrightYellow
		}
	} ,
	dataMarkup: {
		fg: ( markupStack , key , value ) => {
			var str = ansi.fgColor[ value ] || ansi.trueColor( value ) ;
			markupStack.push( str ) ;
			return str ;
		} ,
		bg: ( markupStack , key , value ) => {
			var str = ansi.bgColor[ value ] || ansi.bgTrueColor( value ) ;
			markupStack.push( str ) ;
			return str ;
		}
	} ,
	markupCatchAll: ( markupStack , key , value ) => {
		var str = '' ;

		if ( value === undefined ) {
			if ( key[ 0 ] === '#' ) {
				str = ansi.trueColor( key ) ;
			}
			else if ( typeof ansi[ key ] === 'string' ) {
				str = ansi[ key ] ;
			}
		}

		markupStack.push( str ) ;
		return str ;
	}
} ;

// Aliases
DEFAULT_FORMATTER.dataMarkup.color = DEFAULT_FORMATTER.dataMarkup.c = DEFAULT_FORMATTER.dataMarkup.fgColor = DEFAULT_FORMATTER.dataMarkup.fg ;
DEFAULT_FORMATTER.dataMarkup.bgColor = DEFAULT_FORMATTER.dataMarkup.bg ;



exports.createFormatter = ( options ) => exports.formatMethod.bind( Object.assign( {} , DEFAULT_FORMATTER , options ) ) ;
exports.format = exports.formatMethod.bind( DEFAULT_FORMATTER ) ;
exports.format.default = DEFAULT_FORMATTER ;

exports.formatNoMarkup = exports.formatMethod.bind( Object.assign( {} , DEFAULT_FORMATTER , { noMarkup: true } ) ) ;
// For passing string to Terminal-Kit, it will interpret markup on its own
exports.formatThirdPartyMarkup = exports.formatMethod.bind( Object.assign( {} , DEFAULT_FORMATTER , { noMarkup: true , escapeMarkup: true } ) ) ;

exports.createMarkup = ( options ) => exports.markupMethod.bind( Object.assign( {} , DEFAULT_FORMATTER , options ) ) ;
exports.markup = exports.markupMethod.bind( DEFAULT_FORMATTER ) ;



// Count the number of parameters needed for this string
exports.format.count = function( str , noMarkup = false ) {
	var markup , index , relative , autoIndex = 1 , maxIndex = 0 ;

	if ( typeof str !== 'string' ) { return 0 ; }

	// This regex differs slightly from the main regex: we do not count '%%' and %F is excluded
	// Note: the closing bracket is optional to prevent ReDoS
	var regexp = noMarkup ?
		/%([+-]?)([0-9]*)(?:\[[^\]]*\])?[a-zA-EG-Z]/g :
		/%([+-]?)([0-9]*)(?:\[[^\]]*\])?[a-zA-EG-Z]|(\^\[[^\]]*]?|\^.)/g ;

	for ( [ , relative , index , markup ] of str.matchAll( regexp ) ) {
		if ( markup ) { continue ; }

		if ( index ) {
			index = parseInt( index , 10 ) ;

			if ( relative ) {
				if ( relative === '+' ) { index = autoIndex + index ; }
				else if ( relative === '-' ) { index = autoIndex - index ; }
			}
		}
		else {
			index = autoIndex ;
		}

		autoIndex ++ ;

		if ( maxIndex < index ) { maxIndex = index ; }
	}

	return maxIndex ;
} ;



// Tell if this string contains formatter chars
exports.format.hasFormatting = function( str ) {
	if ( str.search( /\^(.?)|(%%)|%([+-]?)([0-9]*)(?:\[([^\]]*)\])?([a-zA-Z])/ ) !== -1 ) { return true ; }
	return false ;
} ;



// --- Format MODES ---

const modes = {} ;
exports.format.modes = modes ;	// <-- expose modes, used by Babel-Tower for String Kit interop'



// string
modes.s = arg => {
	if ( typeof arg === 'string' ) { return arg ; }
	if ( arg === null || arg === undefined || arg === true || arg === false ) { return '(' + arg + ')' ; }
	if ( typeof arg === 'number' ) { return '' + arg ; }
	if ( typeof arg.toString === 'function' ) { return arg.toString() ; }
	return '(' + arg + ')' ;
} ;

modes.r = arg => modes.s( arg ) ;
modes.r.noSanitize = true ;



// string, interpret ^ formatting
modes.S = ( arg , modeArg , options ) => {
	// We do the sanitizing part on our own
	var interpret = options.escapeMarkup ? str => ( options.argumentSanitizer ? options.argumentSanitizer( str ) : str ) :
		str => exports.markupMethod.call( options , options.argumentSanitizer ? options.argumentSanitizer( str ) : str ) ;

	if ( typeof arg === 'string' ) { return interpret( arg ) ; }
	if ( arg === null || arg === undefined || arg === true || arg === false ) { return '(' + arg + ')' ; }
	if ( typeof arg === 'number' ) { return '' + arg ; }
	if ( typeof arg.toString === 'function' ) { return interpret( arg.toString() ) ; }
	return interpret( '(' + arg + ')' ) ;
} ;

modes.S.noSanitize = true ;
modes.S.noEscapeMarkup = true ;
modes.S.noCommonModeArg = true ;



// natural (WIP)
modes.N = ( arg , modeArg ) => genericNaturalMode( arg , modeArg , false ) ;
modes.n = ( arg , modeArg ) => genericNaturalMode( arg , modeArg , true ) ;



// float
modes.f = ( arg , modeArg ) => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg !== 'number' ) { arg = 0 ; }

	var subModes = floatModeArg( modeArg ) ,
		sn = new StringNumber( arg , { decimalSeparator: '.' , groupSeparator: subModes.groupSeparator } ) ;

	if ( subModes.rounding !== null ) { sn.round( subModes.rounding ) ; }
	if ( subModes.precision ) { sn.precision( subModes.precision ) ; }

	return sn.toString( subModes.leftPadding , subModes.rightPadding , subModes.rightPaddingOnlyIfDecimal ) ;
} ;

modes.f.noSanitize = true ;



// roman numeral, additive variant
modes.v = ( arg , modeArg ) => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg !== 'number' ) { arg = 0 ; }

	var subModes = floatModeArg( modeArg ) ,
		sn = StringNumber.additiveRoman( arg , { decimalSeparator: '.' , groupSeparator: subModes.groupSeparator } ) ;

	if ( subModes.rounding !== null ) { sn.round( subModes.rounding ) ; }
	if ( subModes.precision ) { sn.precision( subModes.precision ) ; }

	return sn.toString( subModes.leftPadding , subModes.rightPadding , subModes.rightPaddingOnlyIfDecimal ) ;
} ;

modes.v.noSanitize = true ;



// roman numeral
modes.V = ( arg , modeArg ) => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg !== 'number' ) { arg = 0 ; }

	var subModes = floatModeArg( modeArg ) ,
		sn = StringNumber.roman( arg , { decimalSeparator: '.' , groupSeparator: subModes.groupSeparator } ) ;

	if ( subModes.rounding !== null ) { sn.round( subModes.rounding ) ; }
	if ( subModes.precision ) { sn.precision( subModes.precision ) ; }

	return sn.toString( subModes.leftPadding , subModes.rightPadding , subModes.rightPaddingOnlyIfDecimal ) ;
} ;

modes.V.noSanitize = true ;



// absolute percent
modes.P = ( arg , modeArg ) => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg !== 'number' ) { arg = 0 ; }

	arg *= 100 ;

	var subModes = floatModeArg( modeArg ) ,
		sn = new StringNumber( arg , { decimalSeparator: '.' , groupSeparator: subModes.groupSeparator } ) ;

	// Force rounding to zero by default
	if ( subModes.rounding !== null || ! subModes.precision ) { sn.round( subModes.rounding || 0 ) ; }
	if ( subModes.precision ) { sn.precision( subModes.precision ) ; }

	return sn.toNoExpString( subModes.leftPadding , subModes.rightPadding , subModes.rightPaddingOnlyIfDecimal ) + '%' ;
} ;

modes.P.noSanitize = true ;



// relative percent
modes.p = ( arg , modeArg ) => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg !== 'number' ) { arg = 0 ; }

	arg = ( arg - 1 ) * 100 ;

	var subModes = floatModeArg( modeArg ) ,
		sn = new StringNumber( arg , { decimalSeparator: '.' , groupSeparator: subModes.groupSeparator } ) ;

	// Force rounding to zero by default
	if ( subModes.rounding !== null || ! subModes.precision ) { sn.round( subModes.rounding || 0 ) ; }
	if ( subModes.precision ) { sn.precision( subModes.precision ) ; }

	// 4th argument force a '+' sign
	return sn.toNoExpString( subModes.leftPadding , subModes.rightPadding , subModes.rightPaddingOnlyIfDecimal , true ) + '%' ;
} ;

modes.p.noSanitize = true ;



// metric system
modes.k = ( arg , modeArg ) => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg !== 'number' ) { return '0' ; }

	var subModes = floatModeArg( modeArg ) ,
		sn = new StringNumber( arg , { decimalSeparator: '.' , groupSeparator: subModes.groupSeparator } ) ;

	if ( subModes.rounding !== null ) { sn.round( subModes.rounding ) ; }
	// Default to 3 numbers precision
	if ( subModes.precision || subModes.rounding === null ) { sn.precision( subModes.precision || 3 ) ; }

	return sn.toMetricString( subModes.leftPadding , subModes.rightPadding , subModes.rightPaddingOnlyIfDecimal ) ;
} ;

modes.k.noSanitize = true ;



// exponential notation, a.k.a. "E notation" (e.g. 1.23e+2)
modes.e = ( arg , modeArg ) => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg !== 'number' ) { arg = 0 ; }

	var subModes = floatModeArg( modeArg ) ,
		sn = new StringNumber( arg , { decimalSeparator: '.' , groupSeparator: subModes.groupSeparator } ) ;

	if ( subModes.rounding !== null ) { sn.round( subModes.rounding ) ; }
	if ( subModes.precision ) { sn.precision( subModes.precision ) ; }

	return sn.toExponential() ;
} ;

modes.e.noSanitize = true ;



// scientific notation (e.g. 1.23 × 10²)
modes.K = ( arg , modeArg ) => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg !== 'number' ) { arg = 0 ; }

	var subModes = floatModeArg( modeArg ) ,
		sn = new StringNumber( arg , { decimalSeparator: '.' , groupSeparator: subModes.groupSeparator } ) ;

	if ( subModes.rounding !== null ) { sn.round( subModes.rounding ) ; }
	if ( subModes.precision ) { sn.precision( subModes.precision ) ; }

	return sn.toScientific() ;
} ;

modes.K.noSanitize = true ;



// integer
modes.d = modes.i = arg => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg === 'number' ) { return '' + Math.floor( arg ) ; }
	return '0' ;
} ;

modes.i.noSanitize = true ;



// unsigned integer
modes.u = arg => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg === 'number' ) { return '' + Math.max( Math.floor( arg ) , 0 ) ; }
	return '0' ;
} ;

modes.u.noSanitize = true ;



// unsigned positive integer
modes.U = arg => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg === 'number' ) { return '' + Math.max( Math.floor( arg ) , 1 ) ; }
	return '1' ;
} ;

modes.U.noSanitize = true ;



// /!\ Should use StringNumber???
// Degree, minutes and seconds.
// Unlike %t which receive ms, here the input is in degree.
modes.m = arg => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg !== 'number' ) { return '(NaN)' ; }

	var minus = '' ;
	if ( arg < 0 ) { minus = '-' ; arg = -arg ; }

	var degrees = epsilonFloor( arg ) ,
		frac = arg - degrees ;

	if ( ! frac ) { return minus + degrees + '°' ; }

	var minutes = epsilonFloor( frac * 60 ) ,
		seconds = epsilonFloor( frac * 3600 - minutes * 60 ) ;

	if ( seconds ) {
		return minus + degrees + '°' + ( '' + minutes ).padStart( 2 , '0' ) + '′' + ( '' + seconds ).padStart( 2 , '0' ) + '″' ;
	}

	return minus + degrees + '°' + ( '' + minutes ).padStart( 2 , '0' ) + '′' ;

} ;

modes.m.noSanitize = true ;



// Date/time
// Minimal Date formating, only support sort of ISO ATM.
// It will be improved later.
modes.T = ( arg , modeArg ) => {
	// Always get a copy of the arg
	try {
		arg = new Date( arg ) ;
	}
	catch ( error ) {
		return '(invalid)' ;
	}

	if ( Number.isNaN( arg.getTime() ) ) {
		return '(invalid)' ;
	}

	var datePart = '' ,
		timePart = '' ,
		str = '' ,
		subModes = dateTimeModeArg( modeArg ) ,
		roundingType = subModes.roundingType ,
		forceDecimalSeparator = subModes.useAbbreviation ;

	// For instance, we only support the ISO-like type

	if ( subModes.years ) {
		if ( datePart ) { datePart += '-' ; }
		datePart += arg.getFullYear() ;
	}

	if ( subModes.months ) {
		if ( datePart ) { datePart += '-' ; }
		datePart += ( '' + ( arg.getMonth() + 1 ) ).padStart( 2 , '0' ) ;
	}

	if ( subModes.days ) {
		if ( datePart ) { datePart += '-' ; }
		datePart += ( '' + arg.getDate() ).padStart( 2 , '0' ) ;
	}

	if ( subModes.hours ) {
		if ( timePart && ! subModes.useAbbreviation ) { timePart += ':' ; }
		timePart += ( '' + arg.getHours() ).padStart( 2 , '0' ) ;
		if ( subModes.useAbbreviation ) { timePart += 'h' ; }
	}

	if ( subModes.minutes ) {
		if ( timePart && ! subModes.useAbbreviation ) { timePart += ':' ; }
		timePart += ( '' + arg.getMinutes() ).padStart( 2 , '0' ) ;
		if ( subModes.useAbbreviation ) { timePart += 'min' ; }
	}

	if ( subModes.seconds ) {
		if ( timePart && ! subModes.useAbbreviation ) { timePart += ':' ; }
		timePart += ( '' + arg.getSeconds() ).padStart( 2 , '0' ) ;
		if ( subModes.useAbbreviation ) { timePart += 's' ; }
	}

	if ( datePart ) {
		if ( str ) { str += ' ' ; }
		str += datePart ;
	}

	if ( timePart ) {
		if ( str ) { str += ' ' ; }
		str += timePart ;
	}

	return str ;
} ;

modes.T.noSanitize = true ;



// Time duration, transform ms into H:min:s
modes.t = ( arg , modeArg ) => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg !== 'number' ) { return '(NaN)' ; }

	var h , min , s , sn , sStr ,
		sign = '' ,
		subModes = timeDurationModeArg( modeArg ) ,
		roundingType = subModes.roundingType ,
		hSeparator = subModes.useAbbreviation ? 'h' : ':' ,
		minSeparator = subModes.useAbbreviation ? 'min' : ':' ,
		sSeparator = subModes.useAbbreviation ? 's' : '.' ,
		forceDecimalSeparator = subModes.useAbbreviation ;

	s = arg / 1000 ;

	if ( s < 0 ) {
		s = -s ;
		roundingType *= -1 ;
		sign = '-' ;
	}

	if ( s < 60 && ! subModes.forceMinutes ) {
		sn = new StringNumber( s , { decimalSeparator: sSeparator , forceDecimalSeparator } ) ;
		sn.round( subModes.rounding , roundingType ) ;

		// Check if rounding has made it reach 60
		if ( sn.toNumber() < 60 ) {
			sStr = sn.toString( 1 , subModes.rightPadding , subModes.rightPaddingOnlyIfDecimal ) ;
			return sign + sStr ;
		}

		s = 60 ;

	}

	min = Math.floor( s / 60 ) ;
	s = s % 60 ;

	sn = new StringNumber( s , { decimalSeparator: sSeparator , forceDecimalSeparator } ) ;
	sn.round( subModes.rounding , roundingType ) ;

	// Check if rounding has made it reach 60
	if ( sn.toNumber() < 60 ) {
		sStr = sn.toString( 2 , subModes.rightPadding , subModes.rightPaddingOnlyIfDecimal ) ;
	}
	else {
		min ++ ;
		s = 0 ;
		sn.set( s ) ;
		sStr = sn.toString( 2 , subModes.rightPadding , subModes.rightPaddingOnlyIfDecimal ) ;
	}

	if ( min < 60 && ! subModes.forceHours ) {
		return sign + min + minSeparator + sStr ;
	}

	h = Math.floor( min / 60 ) ;
	min = min % 60 ;

	return sign + h + hSeparator + ( '' + min ).padStart( 2 , '0' ) + minSeparator + sStr ;
} ;

modes.t.noSanitize = true ;



// unsigned hexadecimal
modes.h = arg => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg === 'number' ) { return '' + Math.max( Math.floor( arg ) , 0 ).toString( 16 ) ; }
	return '0' ;
} ;

modes.h.noSanitize = true ;



// unsigned hexadecimal, force pair of symboles
modes.x = arg => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg !== 'number' ) { return '00' ; }

	var value = '' + Math.max( Math.floor( arg ) , 0 ).toString( 16 ) ;

	if ( value.length % 2 ) { value = '0' + value ; }
	return value ;
} ;

modes.x.noSanitize = true ;



// unsigned octal
modes.o = arg => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg === 'number' ) { return '' + Math.max( Math.floor( arg ) , 0 ).toString( 8 ) ; }
	return '0' ;
} ;

modes.o.noSanitize = true ;



// unsigned binary
modes.b = arg => {
	if ( typeof arg === 'string' ) { arg = parseFloat( arg ) ; }
	if ( typeof arg === 'number' ) { return '' + Math.max( Math.floor( arg ) , 0 ).toString( 2 ) ; }
	return '0' ;
} ;

modes.b.noSanitize = true ;



// String to hexadecimal, force pair of symboles
modes.X = arg => {
	if ( typeof arg === 'string' ) { arg = Buffer.from( arg ) ; }
	else if ( ! Buffer.isBuffer( arg ) ) { return '' ; }
	return arg.toString( 'hex' ) ;
} ;

modes.X.noSanitize = true ;



// base64
modes.z = arg => {
	if ( typeof arg === 'string' ) { arg = Buffer.from( arg ) ; }
	else if ( ! Buffer.isBuffer( arg ) ) { return '' ; }
	return arg.toString( 'base64' ) ;
} ;



// base64url
modes.Z = arg => {
	if ( typeof arg === 'string' ) { arg = Buffer.from( arg ) ; }
	else if ( ! Buffer.isBuffer( arg ) ) { return '' ; }
	return arg.toString( 'base64' ).replace( /\+/g , '-' )
		.replace( /\//g , '_' )
		.replace( /[=]{1,2}$/g , '' ) ;
} ;



// Inspect
const I_OPTIONS = {} ;
modes.I = ( arg , modeArg , options ) => genericInspectMode( arg , modeArg , options , I_OPTIONS ) ;
modes.I.noSanitize = true ;



// More minimalist inspect
const Y_OPTIONS = {
	noFunc: true ,
	enumOnly: true ,
	noDescriptor: true ,
	useInspect: true ,
	useInspectPropertyBlackList: true
} ;
modes.Y = ( arg , modeArg , options ) => genericInspectMode( arg , modeArg , options , Y_OPTIONS ) ;
modes.Y.noSanitize = true ;



// Even more minimalist inspect
const O_OPTIONS = { minimal: true , bulletIndex: true , noMarkup: true } ;
modes.O = ( arg , modeArg , options ) => genericInspectMode( arg , modeArg , options , O_OPTIONS ) ;
modes.O.noSanitize = true ;



// Inspect error
const E_OPTIONS = {} ;
modes.E = ( arg , modeArg , options ) => genericInspectMode( arg , modeArg , options , E_OPTIONS , true ) ;
modes.E.noSanitize = true ;



// JSON
modes.J = arg => arg === undefined ? 'null' : JSON.stringify( arg ) ;



// drop
modes.D = () => '' ;
modes.D.noSanitize = true ;



// ModeArg formats

// The format for commonModeArg
const COMMON_MODE_ARG_FORMAT_REGEX = /([a-zA-Z])(.[^a-zA-Z]*)/g ;

// The format for specific mode arg
const MODE_ARG_FORMAT_REGEX = /([a-zA-Z]|^)([^a-zA-Z]*)/g ;



// Called when there is a modeArg and the mode allow common mode arg
// CONVENTION: reserve upper-cased letters for common mode arg
function commonModeArg( str , modeArg ) {
	for ( let [ , k , v ] of modeArg.matchAll( COMMON_MODE_ARG_FORMAT_REGEX ) ) {
		if ( k === 'L' ) {
			let width = unicode.width( str ) ;
			v = + v || 1 ;

			if ( width > v ) {
				str = unicode.truncateWidth( str , v - 1 ).trim() + '…' ;
				width = unicode.width( str ) ;
			}

			if ( width < v ) { str = ' '.repeat( v - width ) + str ; }
		}
		else if ( k === 'R' ) {
			let width = unicode.width( str ) ;
			v = + v || 1 ;

			if ( width > v ) {
				str = unicode.truncateWidth( str , v - 1 ).trim() + '…' ;
				width = unicode.width( str ) ;
			}

			if ( width < v ) { str = str + ' '.repeat( v - width ) ; }
		}
	}

	return str ;
}



const FLOAT_MODES = {
	leftPadding: 1 ,
	rightPadding: 0 ,
	rightPaddingOnlyIfDecimal: false ,
	rounding: null ,
	precision: null ,
	groupSeparator: ''
} ;

// Generic number modes
function floatModeArg( modeArg ) {
	FLOAT_MODES.leftPadding = 1 ;
	FLOAT_MODES.rightPadding = 0 ;
	FLOAT_MODES.rightPaddingOnlyIfDecimal = false ;
	FLOAT_MODES.rounding = null ;
	FLOAT_MODES.precision = null ;
	FLOAT_MODES.groupSeparator = '' ;

	if ( modeArg ) {
		for ( let [ , k , v ] of modeArg.matchAll( MODE_ARG_FORMAT_REGEX ) ) {
			if ( k === 'z' ) {
				// Zero-left padding
				FLOAT_MODES.leftPadding = + v ;
			}
			else if ( k === 'g' ) {
				// Group separator
				FLOAT_MODES.groupSeparator = v || ' ' ;
			}
			else if ( ! k ) {
				if ( v[ 0 ] === '.' ) {
					// Rounding after the decimal
					let lv = v[ v.length - 1 ] ;

					// Zero-right padding?
					if ( lv === '!' ) {
						FLOAT_MODES.rounding = FLOAT_MODES.rightPadding = parseInt( v.slice( 1 , -1 ) , 10 ) || 0 ;
					}
					else if ( lv === '?' ) {
						FLOAT_MODES.rounding = FLOAT_MODES.rightPadding = parseInt( v.slice( 1 , -1 ) , 10 ) || 0 ;
						FLOAT_MODES.rightPaddingOnlyIfDecimal = true ;
					}
					else {
						FLOAT_MODES.rounding = parseInt( v.slice( 1 ) , 10 ) || 0 ;
					}
				}
				else if ( v[ v.length - 1 ] === '.' ) {
					// Rounding before the decimal
					FLOAT_MODES.rounding = -parseInt( v.slice( 0 , -1 ) , 10 ) || 0 ;
				}
				else {
					// Precision, but only if integer
					FLOAT_MODES.precision = parseInt( v , 10 ) || null ;
				}
			}
		}
	}

	return FLOAT_MODES ;
}



const DATE_TIME_MODES = {
	useAbbreviation: false ,
	rightPadding: 0 ,
	rightPaddingOnlyIfDecimal: false ,
	years: true ,
	months: true ,
	days: true ,
	hours: true ,
	minutes: true ,
	seconds: true
} ;

// Generic number modes
function dateTimeModeArg( modeArg ) {
	DATE_TIME_MODES.rightPadding = 0 ;
	DATE_TIME_MODES.rightPaddingOnlyIfDecimal = false ;
	DATE_TIME_MODES.rounding = 0 ;
	DATE_TIME_MODES.roundingType = -1 ;
	DATE_TIME_MODES.years = DATE_TIME_MODES.months = DATE_TIME_MODES.days = false ;
	DATE_TIME_MODES.hours = DATE_TIME_MODES.minutes = DATE_TIME_MODES.seconds = false ;
	DATE_TIME_MODES.useAbbreviation = false ;

	var hasSelector = false ;

	if ( modeArg ) {
		for ( let [ , k , v ] of modeArg.matchAll( MODE_ARG_FORMAT_REGEX ) ) {
			if ( k === 'T' ) {
				DATE_TIME_MODES.years = DATE_TIME_MODES.months = DATE_TIME_MODES.days = false ;
				DATE_TIME_MODES.hours = DATE_TIME_MODES.minutes = DATE_TIME_MODES.seconds = true ;
				hasSelector = true ;
			}
			else if ( k === 'D' ) {
				DATE_TIME_MODES.years = DATE_TIME_MODES.months = DATE_TIME_MODES.days = true ;
				DATE_TIME_MODES.hours = DATE_TIME_MODES.minutes = DATE_TIME_MODES.seconds = false ;
				hasSelector = true ;
			}
			else if ( k === 'Y' ) {
				DATE_TIME_MODES.years = true ;
				hasSelector = true ;
			}
			else if ( k === 'M' ) {
				DATE_TIME_MODES.months = true ;
				hasSelector = true ;
			}
			else if ( k === 'd' ) {
				DATE_TIME_MODES.days = true ;
				hasSelector = true ;
			}
			else if ( k === 'h' ) {
				DATE_TIME_MODES.hours = true ;
				hasSelector = true ;
			}
			else if ( k === 'm' ) {
				DATE_TIME_MODES.minutes = true ;
				hasSelector = true ;
			}
			else if ( k === 's' ) {
				DATE_TIME_MODES.seconds = true ;
				hasSelector = true ;
			}
			else if ( k === 'r' ) {
				DATE_TIME_MODES.roundingType = 0 ;
			}
			else if ( k === 'f' ) {
				DATE_TIME_MODES.roundingType = -1 ;
			}
			else if ( k === 'c' ) {
				DATE_TIME_MODES.roundingType = 1 ;
			}
			else if ( k === 'a' ) {
				DATE_TIME_MODES.useAbbreviation = true ;
			}
			else if ( ! k ) {
				if ( v[ 0 ] === '.' ) {
					// Rounding after the decimal
					let lv = v[ v.length - 1 ] ;

					// Zero-right padding?
					if ( lv === '!' ) {
						DATE_TIME_MODES.rounding = DATE_TIME_MODES.rightPadding = parseInt( v.slice( 1 , -1 ) , 10 ) || 0 ;
					}
					else if ( lv === '?' ) {
						DATE_TIME_MODES.rounding = DATE_TIME_MODES.rightPadding = parseInt( v.slice( 1 , -1 ) , 10 ) || 0 ;
						DATE_TIME_MODES.rightPaddingOnlyIfDecimal = true ;
					}
					else {
						DATE_TIME_MODES.rounding = parseInt( v.slice( 1 ) , 10 ) || 0 ;
					}
				}
			}
		}
	}

	if ( ! hasSelector ) {
		DATE_TIME_MODES.years = DATE_TIME_MODES.months = DATE_TIME_MODES.days = true ;
		DATE_TIME_MODES.hours = DATE_TIME_MODES.minutes = DATE_TIME_MODES.seconds = true ;
	}

	return DATE_TIME_MODES ;
}



const TIME_DURATION_MODES = {
	useAbbreviation: false ,
	rightPadding: 0 ,
	rightPaddingOnlyIfDecimal: false ,
	rounding: 0 ,
	roundingType: -1 ,	// -1: floor, 0: round, 1: ceil
	forceHours: false ,
	forceMinutes: false
} ;

// Generic number modes
function timeDurationModeArg( modeArg ) {
	TIME_DURATION_MODES.rightPadding = 0 ;
	TIME_DURATION_MODES.rightPaddingOnlyIfDecimal = false ;
	TIME_DURATION_MODES.rounding = 0 ;
	TIME_DURATION_MODES.roundingType = -1 ;
	TIME_DURATION_MODES.useAbbreviation = TIME_DURATION_MODES.forceHours = TIME_DURATION_MODES.forceMinutes = false ;

	if ( modeArg ) {
		for ( let [ , k , v ] of modeArg.matchAll( MODE_ARG_FORMAT_REGEX ) ) {
			if ( k === 'h' ) {
				TIME_DURATION_MODES.forceHours = TIME_DURATION_MODES.forceMinutes = true ;
			}
			else if ( k === 'm' ) {
				TIME_DURATION_MODES.forceMinutes = true ;
			}
			else if ( k === 'r' ) {
				TIME_DURATION_MODES.roundingType = 0 ;
			}
			else if ( k === 'f' ) {
				TIME_DURATION_MODES.roundingType = -1 ;
			}
			else if ( k === 'c' ) {
				TIME_DURATION_MODES.roundingType = 1 ;
			}
			else if ( k === 'a' ) {
				TIME_DURATION_MODES.useAbbreviation = true ;
			}
			else if ( ! k ) {
				if ( v[ 0 ] === '.' ) {
					// Rounding after the decimal
					let lv = v[ v.length - 1 ] ;

					// Zero-right padding?
					if ( lv === '!' ) {
						TIME_DURATION_MODES.rounding = TIME_DURATION_MODES.rightPadding = parseInt( v.slice( 1 , -1 ) , 10 ) || 0 ;
					}
					else if ( lv === '?' ) {
						TIME_DURATION_MODES.rounding = TIME_DURATION_MODES.rightPadding = parseInt( v.slice( 1 , -1 ) , 10 ) || 0 ;
						TIME_DURATION_MODES.rightPaddingOnlyIfDecimal = true ;
					}
					else {
						TIME_DURATION_MODES.rounding = parseInt( v.slice( 1 ) , 10 ) || 0 ;
					}
				}
			}
		}
	}

	return TIME_DURATION_MODES ;
}



// Generic Natural Mode
function genericNaturalMode( arg , modeArg , delimiters ) {
	var depthLimit = 2 ;

	if ( modeArg ) {
		for ( let [ , k , v ] of modeArg.matchAll( MODE_ARG_FORMAT_REGEX ) ) {
			if ( ! k ) {
				depthLimit = parseInt( v , 10 ) || 1 ;
			}
		}
	}

	return genericNaturalModeRecursive( arg , delimiters , depthLimit , 0 ) ;
}

function genericNaturalModeRecursive( arg , delimiters , depthLimit , depth ) {
	if ( typeof arg === 'string' ) { return arg ; }

	if ( arg === null || arg === undefined || arg === true || arg === false ) {
		return '' + arg ;
	}

	if ( typeof arg === 'number' ) {
		return modes.f( arg , '.3g ' ) ;
	}

	if ( Array.isArray( arg ) ) {
		if ( depth >= depthLimit ) { return '[...]' ; }

		arg = arg.map( e => genericNaturalModeRecursive( e , true , depthLimit , depth + 1 ) ) ;

		if ( delimiters ) { return '[' + arg.join( ',' ) + ']' ; }
		return arg.join( ', ' ) ;
	}

	if ( Buffer.isBuffer( arg ) ) {
		arg = [ ... arg ].map( e => {
			e = e.toString( 16 ) ;
			if ( e.length === 1 ) { e = '0' + e ; }
			return e ;
		} ) ;
		return '<' + arg.join( ' ' ) + '>' ;
	}

	var proto = Object.getPrototypeOf( arg ) ;

	if ( proto === null || proto === Object.prototype ) {
		// Plain objects
		if ( depth >= depthLimit ) { return '{...}' ; }

		arg = Object.entries( arg ).sort( naturalSort )
			.map( e => e[ 0 ] + ': ' + genericNaturalModeRecursive( e[ 1 ] , true , depthLimit , depth + 1 ) ) ;

		if ( delimiters ) { return '{' + arg.join( ', ' ) + '}' ; }
		return arg.join( ', ' ) ;
	}

	if ( typeof arg.inspect === 'function' ) { return arg.inspect() ; }
	if ( typeof arg.toString === 'function' ) { return arg.toString() ; }

	return '(' + arg + ')' ;
}



// Generic inspect
function genericInspectMode( arg , modeArg , options , modeOptions , isInspectError = false ) {
	var outputMaxLength ,
		maxLength ,
		depth = 3 ,
		style = options && options.color ? 'color' : 'none' ;

	if ( modeArg ) {
		for ( let [ , k , v ] of modeArg.matchAll( MODE_ARG_FORMAT_REGEX ) ) {
			if ( k === 'c' ) {
				if ( v === '+' ) { style = 'color' ; }
				else if ( v === '-' ) { style = 'none' ; }
			}
			else if ( k === 'i' ) {
				style = 'inline' ;
			}
			else if ( k === 'l' ) {
				// total output max length
				outputMaxLength = parseInt( v , 10 ) || undefined ;
			}
			else if ( k === 's' ) {
				// string max length
				maxLength = parseInt( v , 10 ) || undefined ;
			}
			else if ( ! k ) {
				depth = parseInt( v , 10 ) || 1 ;
			}
		}
	}

	if ( isInspectError ) {
		return inspectError( Object.assign( {
			depth , style , outputMaxLength , maxLength
		} , modeOptions ) , arg ) ;
	}

	return inspect( Object.assign( {
		depth , style , outputMaxLength , maxLength
	} , modeOptions ) , arg ) ;
}



// From math-kit module
// /!\ Should be updated with the new way the math-kit module do it!!! /!\
const EPSILON = 0.0000000001 ;
const INVERSE_EPSILON = Math.round( 1 / EPSILON ) ;

function epsilonRound( v ) {
	return Math.round( v * INVERSE_EPSILON ) / INVERSE_EPSILON ;
}

function epsilonFloor( v ) {
	return Math.floor( v + EPSILON ) ;
}

// Round with precision
function round( v , step ) {
	// use: v * ( 1 / step )
	// not: v / step
	// reason: epsilon rounding errors
	return epsilonRound( step * Math.round( v * ( 1 / step ) ) ) ;
}


}).call(this)}).call(this,require("buffer").Buffer)
},{"./StringNumber.js":116,"./ansi.js":117,"./escape.js":121,"./inspect.js":124,"./naturalSort.js":131,"./unicode.js":135,"buffer":159}],123:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;


const fuzzy = {} ;
module.exports = fuzzy ;



fuzzy.score = ( input , pattern ) => {
	if ( input === pattern ) { return 1 ; }
	if ( input.length === 0 || pattern.length === 0 ) { return 0 ; }
	//return 1 - fuzzy.levenshtein( input , pattern ) / ( pattern.length >= input.length ? pattern.length : input.length ) ;
	return Math.max( 0 , 1 - fuzzy.levenshtein( input , pattern ) / pattern.length ) ;
} ;



const DEFAULT_SCORE_LIMIT = 0 ;
const DEFAULT_TOKEN_DISPARITY_PENALTY = 0.88 ;
// deltaRate should be just above tokenDisparityPenalty
const DEFAULT_DELTA_RATE = 0.9 ;



fuzzy.bestMatch = ( input , patterns , options = {} ) => {
	var bestScore = options.scoreLimit || DEFAULT_SCORE_LIMIT ,
		i , iMax , currentScore , currentPattern ,
		bestIndex = -1 ,
		bestPattern = null ;

	for ( i = 0 , iMax = patterns.length ; i < iMax ; i ++ ) {
		currentPattern = patterns[ i ] ;
		currentScore = fuzzy.score( input , currentPattern ) ;
		if ( currentScore === 1 ) { return options.indexOf ? i : currentPattern ; }
		if ( currentScore > bestScore ) {
			bestScore = currentScore ;
			bestPattern = currentPattern ;
			bestIndex = i ;
		}
	}

	return options.indexOf ? bestIndex : bestPattern ;
} ;



fuzzy.topMatch = ( input , patterns , options = {} ) => {
	var scoreLimit = options.scoreLimit || DEFAULT_SCORE_LIMIT ,
		deltaRate = options.deltaRate || DEFAULT_DELTA_RATE ,
		i , iMax , patternScores ;

	patternScores = patterns.map( ( pattern , index ) => ( { pattern , index , score: fuzzy.score( input , pattern ) } ) ) ;
	patternScores.sort( ( a , b ) => b.score - a.score ) ;

	//console.log( patternScores ) ;

	if ( patternScores[ 0 ].score <= scoreLimit ) { return [] ; }
	scoreLimit = Math.max( scoreLimit , patternScores[ 0 ].score * deltaRate ) ;

	for ( i = 1 , iMax = patternScores.length ; i < iMax ; i ++ ) {
		if ( patternScores[ i ].score < scoreLimit ) {
			patternScores.length = i ;
			break ;
		}
	}

	return options.indexOf ?
		patternScores.map( e => e.index ) :
		patternScores.map( e => e.pattern ) ;
} ;



const englishBlackList = new Set( [
	'a' , 'an' , 'the' , 'this' , 'that' , 'those' , 'some' ,
	'of' , 'in' , 'on' , 'at' ,
	'my' , 'your' , 'her' , 'his' , 'its' , 'our' , 'their'
] ) ;

function tokenize( str , blackList = englishBlackList ) {
	return str.split( /[ '"/|,:_-]+/g ).filter( s => s && ! blackList.has( s ) ) ;
}



// This is almost the same code than .topTokenMatch(): both must be in sync
fuzzy.bestTokenMatch = ( input , patterns , options = {} ) => {
	var scoreLimit = options.scoreLimit || DEFAULT_SCORE_LIMIT ,
		tokenDisparityPenalty = options.tokenDisparityPenalty || DEFAULT_TOKEN_DISPARITY_PENALTY ,
		i , iMax , j , jMax , z , zMax ,
		currentPattern , currentPatternTokens , currentPatternToken , currentPatternScore ,
		bestPatternScore = scoreLimit ,
		//currentPatternScores = [] ,
		currentInputToken , currentScore ,
		inputTokens = tokenize( input ) ,
		bestScore ,
		bestIndex = -1 ,
		bestPattern = null ;

	//console.log( inputTokens ) ;
	if ( ! inputTokens.length || ! patterns.length ) { return options.indexOf ? bestIndex : bestPattern ; }

	for ( i = 0 , iMax = patterns.length ; i < iMax ; i ++ ) {
		currentPattern = patterns[ i ] ;
		currentPatternTokens = tokenize( currentPattern ) ;
		//currentPatternScores.length = 0 ;
		currentPatternScore = 0 ;

		for ( j = 0 , jMax = inputTokens.length ; j < jMax ; j ++ ) {
			currentInputToken = inputTokens[ j ] ;
			bestScore = 0 ;

			for ( z = 0 , zMax = currentPatternTokens.length ; z < zMax ; z ++ ) {
				currentPatternToken = currentPatternTokens[ z ] ;
				currentScore = fuzzy.score( currentInputToken , currentPatternToken ) ;

				if ( currentScore > bestScore ) {
					bestScore = currentScore ;
					if ( currentScore === 1 ) { break ; }
				}
			}

			//currentPatternScores[ j ] = bestScore ;
			currentPatternScore += bestScore ;
		}

		//currentPatternScore = Math.hypot( ... currentPatternScores ) ;
		currentPatternScore /= inputTokens.length ;

		// Apply a small penalty if there isn't enough tokens
		if ( inputTokens.length !== currentPatternTokens.length ) {
			currentPatternScore *= tokenDisparityPenalty ** Math.abs( currentPatternTokens.length - inputTokens.length ) ;
		}

		//console.log( currentPattern + ': ' + currentPatternScore ) ;
		if ( currentPatternScore > bestPatternScore ) {
			bestPatternScore = currentPatternScore ;
			bestPattern = currentPattern ;
			bestIndex = i ;
		}
	}

	return options.indexOf ? bestIndex : bestPattern ;
} ;



// This is almost the same code than .bestTokenMatch(): both must be in sync
// deltaRate should be just above tokenDisparityPenalty
fuzzy.topTokenMatch = ( input , patterns , options = {} ) => {
	var scoreLimit = options.scoreLimit || DEFAULT_SCORE_LIMIT ,
		tokenDisparityPenalty = options.tokenDisparityPenalty || DEFAULT_TOKEN_DISPARITY_PENALTY ,
		deltaRate = options.deltaRate || DEFAULT_DELTA_RATE ,
		i , iMax , j , jMax , z , zMax ,
		currentPattern , currentPatternTokens , currentPatternToken , currentPatternScore ,
		currentInputToken , currentScore ,
		inputTokens = tokenize( input ) ,
		bestScore ,
		patternScores = [] ;

	//console.log( inputTokens ) ;
	if ( ! inputTokens.length || ! patterns.length ) { return [] ; }

	for ( i = 0 , iMax = patterns.length ; i < iMax ; i ++ ) {
		currentPattern = patterns[ i ] ;
		currentPatternTokens = tokenize( currentPattern ) ;
		//currentPatternScores.length = 0 ;
		currentPatternScore = 0 ;

		for ( j = 0 , jMax = inputTokens.length ; j < jMax ; j ++ ) {
			currentInputToken = inputTokens[ j ] ;
			bestScore = 0 ;

			for ( z = 0 , zMax = currentPatternTokens.length ; z < zMax ; z ++ ) {
				currentPatternToken = currentPatternTokens[ z ] ;
				currentScore = fuzzy.score( currentInputToken , currentPatternToken ) ;

				if ( currentScore > bestScore ) {
					bestScore = currentScore ;
					if ( currentScore === 1 ) { break ; }
				}
			}

			//currentPatternScores[ j ] = bestScore ;
			currentPatternScore += bestScore ;
		}

		//currentPatternScore = Math.hypot( ... currentPatternScores ) ;
		currentPatternScore /= inputTokens.length ;

		// Apply a small penalty if there isn't enough tokens
		if ( inputTokens.length !== currentPatternTokens.length ) {
			currentPatternScore *= tokenDisparityPenalty ** Math.abs( currentPatternTokens.length - inputTokens.length ) ;
		}

		patternScores.push( { pattern: currentPattern , index: i , score: currentPatternScore } ) ;
	}

	patternScores.sort( ( a , b ) => b.score - a.score ) ;
	//console.log( "Before truncating:" , patternScores ) ;

	if ( patternScores[ 0 ].score <= scoreLimit ) { return [] ; }
	scoreLimit = Math.max( scoreLimit , patternScores[ 0 ].score * deltaRate ) ;

	for ( i = 1 , iMax = patternScores.length ; i < iMax ; i ++ ) {
		if ( patternScores[ i ].score < scoreLimit ) {
			patternScores.length = i ;
			break ;
		}
	}

	//console.log( "After truncating:" , patternScores ) ;

	return options.indexOf ?
		patternScores.map( e => e.index ) :
		patternScores.map( e => e.pattern ) ;
} ;



// The .levenshtein() function is derivated from https://github.com/sindresorhus/leven by Sindre Sorhus (MIT License)
const _tracker = [] ;
const _leftCharCodeCache = [] ;

fuzzy.levenshtein = ( left , right ) => {
	if ( left === right ) { return 0 ; }

	// Swapping the strings if `a` is longer than `b` so we know which one is the
	// shortest & which one is the longest
	if ( left.length > right.length ) {
		let swap = left ;
		left = right ;
		right = swap ;
	}

	let leftLength = left.length ;
	let rightLength = right.length ;

	// Performing suffix trimming:
	// We can linearly drop suffix common to both strings since they
	// don't increase distance at all
	while ( leftLength > 0 && ( left.charCodeAt( leftLength - 1 ) === right.charCodeAt( rightLength - 1 ) ) ) {
		leftLength -- ;
		rightLength -- ;
	}

	// Performing prefix trimming
	// We can linearly drop prefix common to both strings since they
	// don't increase distance at all
	let start = 0 ;

	while ( start < leftLength && ( left.charCodeAt( start ) === right.charCodeAt( start ) ) ) {
		start ++ ;
	}

	leftLength -= start ;
	rightLength -= start ;

	if ( leftLength === 0 ) { return rightLength ; }

	let rightCharCode ;
	let result ;
	let temp ;
	let temp2 ;
	let i = 0 ;
	let j = 0 ;

	while ( i < leftLength ) {
		_leftCharCodeCache[ i ] = left.charCodeAt( start + i ) ;
		_tracker[ i ] = ++ i ;
	}

	while ( j < rightLength ) {
		rightCharCode = right.charCodeAt( start + j ) ;
		temp = j ++ ;
		result = j ;

		for ( i = 0 ; i < leftLength ; i ++ ) {
			temp2 = rightCharCode === _leftCharCodeCache[ i ] ? temp : temp + 1 ;
			temp = _tracker[ i ] ;
			// eslint-disable-next-line no-nested-ternary
			result = _tracker[ i ] = temp > result   ?   temp2 > result ? result + 1 : temp2   :   temp2 > temp ? temp + 1 : temp2 ;
		}
	}

	return result ;
} ;


},{}],124:[function(require,module,exports){
(function (Buffer,process){(function (){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

/*
	Variable inspector.
*/

"use strict" ;



const escape = require( './escape.js' ) ;
const ansi = require( './ansi.js' ) ;

const EMPTY = {} ;
const TRIVIAL_CONSTRUCTOR = new Set( [ Object , Array ] ) ;



/*
	Inspect a variable, return a string ready to be displayed with console.log(), or even as an HTML output.

	Options:
		* style:
			* 'none': (default) normal output suitable for console.log() or writing in a file
			* 'inline': like 'none', but without newlines
			* 'color': colorful output suitable for terminal
			* 'html': html output
			* any object: full controle, inheriting from 'none'
		* tab: `string` override the tab of the style
		* depth: depth limit, default: 3
		* maxLength: length limit for strings, default: 250
		* outputMaxLength: length limit for the inspect output string, default: 5000
		* noFunc: do not display functions
		* noDescriptor: do not display descriptor information
		* noArrayProperty: do not display array properties
		* noIndex: do not display array indexes
		* bulletIndex: do not display array indexes, instead display a bullet: *
		* noType: do not display type and constructor
		* noTypeButConstructor: do not display type, display non-trivial constructor (not Object or Array, but all others)
		* enumOnly: only display enumerable properties
		* funcDetails: display function's details
		* proto: display object's prototype
		* sort: sort the keys
		* noMarkup: don't add Javascript/JSON markup: {}[],"
		* minimal: imply noFunc: true, noDescriptor: true, noType: true, noArrayProperty: true, enumOnly: true, proto: false and funcDetails: false.
		  Display a minimal JSON-like output
		* minimalPlusConstructor: like minimal, but output non-trivial constructor
		* protoBlackList: `Set` of blacklisted object prototype (will not recurse inside it)
		* propertyBlackList: `Set` of blacklisted property names (will not even display it)
		* useInspect: use .inspect() method when available on an object (default to false)
		* useInspectPropertyBlackList: if set and if the object to be inspected has an 'inspectPropertyBlackList' property which value is a `Set`,
		  use it like the 'propertyBlackList' option
*/

function inspect( options , variable ) {
	if ( arguments.length < 2 ) { variable = options ; options = {} ; }
	else if ( ! options || typeof options !== 'object' ) { options = {} ; }

	var runtime = { depth: 0 , ancestors: [] } ;

	if ( ! options.style ) { options.style = inspectStyle.none ; }
	else if ( typeof options.style === 'string' ) { options.style = inspectStyle[ options.style ] ; }
	// Too slow:
	//else { options.style = Object.assign( {} , inspectStyle.none , options.style ) ; }

	if ( options.depth === undefined ) { options.depth = 3 ; }
	if ( options.maxLength === undefined ) { options.maxLength = 250 ; }
	if ( options.outputMaxLength === undefined ) { options.outputMaxLength = 5000 ; }

	// /!\ nofunc is deprecated
	if ( options.nofunc ) { options.noFunc = true ; }

	if ( options.minimal ) {
		options.noFunc = true ;
		options.noDescriptor = true ;
		options.noType = true ;
		options.noArrayProperty = true ;
		options.enumOnly = true ;
		options.proto = false ;
		options.funcDetails = false ;
	}

	if ( options.minimalPlusConstructor ) {
		options.noFunc = true ;
		options.noDescriptor = true ;
		options.noTypeButConstructor = true ;
		options.noArrayProperty = true ;
		options.enumOnly = true ;
		options.proto = false ;
		options.funcDetails = false ;
	}

	var str = inspect_( runtime , options , variable ) ;

	if ( str.length > options.outputMaxLength ) {
		str = options.style.truncate( str , options.outputMaxLength ) ;
	}

	return str ;
}

exports.inspect = inspect ;



function inspect_( runtime , options , variable ) {
	var i , funcName , length , proto , propertyList , isTrivialConstructor , constructor , keyIsProperty ,
		type , pre , isArray , isFunc , specialObject ,
		str = '' , key = '' , descriptorStr = '' , indent = '' ,
		descriptor , nextAncestors ;

	// Prepare things (indentation, key, descriptor, ... )

	type = typeof variable ;

	if ( runtime.depth ) {
		indent = ( options.tab ?? options.style.tab ).repeat( options.noMarkup ? runtime.depth - 1 : runtime.depth ) ;
	}

	if ( type === 'function' && options.noFunc ) { return '' ; }

	if ( runtime.key !== undefined ) {
		if ( runtime.descriptor ) {
			descriptorStr = [] ;

			if ( runtime.descriptor.error ) {
				descriptorStr = '[' + runtime.descriptor.error + ']' ;
			}
			else {
				if ( ! runtime.descriptor.configurable ) { descriptorStr.push( '-conf' ) ; }
				if ( ! runtime.descriptor.enumerable ) { descriptorStr.push( '-enum' ) ; }

				// Already displayed by runtime.forceType
				//if ( runtime.descriptor.get || runtime.descriptor.set ) { descriptorStr.push( 'getter/setter' ) ; } else
				if ( ! runtime.descriptor.writable ) { descriptorStr.push( '-w' ) ; }

				//if ( descriptorStr.length ) { descriptorStr = '(' + descriptorStr.join( ' ' ) + ')' ; }
				if ( descriptorStr.length ) { descriptorStr = descriptorStr.join( ' ' ) ; }
				else { descriptorStr = '' ; }
			}
		}

		if ( runtime.keyIsProperty ) {
			if ( ! options.noMarkup && keyNeedingQuotes( runtime.key ) ) {
				key = '"' + options.style.key( runtime.key ) + '": ' ;
			}
			else {
				key = options.style.key( runtime.key ) + ': ' ;
			}
		}
		else if ( options.bulletIndex ) {
			key = ( typeof options.bulletIndex === 'string' ? options.bulletIndex : '*' ) + ' ' ;
		}
		else if ( ! options.noIndex ) {
			key = options.style.index( runtime.key ) ;
		}

		if ( descriptorStr ) { descriptorStr = ' ' + options.style.type( descriptorStr ) ; }
	}

	pre = runtime.noPre ? '' : indent + key ;


	// Describe the current variable

	if ( variable === undefined ) {
		str += pre + options.style.constant( 'undefined' ) + descriptorStr + options.style.newline ;
	}
	else if ( variable === EMPTY ) {
		str += pre + options.style.constant( '[empty]' ) + descriptorStr + options.style.newline ;
	}
	else if ( variable === null ) {
		str += pre + options.style.constant( 'null' ) + descriptorStr + options.style.newline ;
	}
	else if ( variable === false ) {
		str += pre + options.style.constant( 'false' ) + descriptorStr + options.style.newline ;
	}
	else if ( variable === true ) {
		str += pre + options.style.constant( 'true' ) + descriptorStr + options.style.newline ;
	}
	else if ( type === 'number' ) {
		str += pre + options.style.number( variable.toString() ) +
			( options.noType || options.noTypeButConstructor ? '' : ' ' + options.style.type( 'number' ) ) +
			descriptorStr + options.style.newline ;
	}
	else if ( type === 'string' ) {
		if ( variable.length > options.maxLength ) {
			str += pre + ( options.noMarkup ? '' : '"' ) + options.style.string( escape.control( variable.slice( 0 , options.maxLength - 1 ) ) ) + '…' + ( options.noMarkup ? '' : '"' ) +
				( options.noType || options.noTypeButConstructor ? '' : ' ' + options.style.type( 'string' ) + options.style.length( '(' + variable.length + ' - TRUNCATED)' ) ) +
				descriptorStr + options.style.newline ;
		}
		else {
			str += pre + ( options.noMarkup ? '' : '"' ) + options.style.string( escape.control( variable ) ) + ( options.noMarkup ? '' : '"' ) +
				( options.noType || options.noTypeButConstructor ? '' : ' ' + options.style.type( 'string' ) + options.style.length( '(' + variable.length + ')' ) ) +
				descriptorStr + options.style.newline ;
		}
	}
	else if ( Buffer.isBuffer( variable ) ) {
		str += pre + options.style.inspect( variable.inspect() ) +
			( options.noType ? '' : ' ' + options.style.type( 'Buffer' ) + options.style.length( '(' + variable.length + ')' ) ) +
			descriptorStr + options.style.newline ;
	}
	else if ( type === 'object' || type === 'function' ) {
		funcName = length = '' ;
		isFunc = false ;

		if ( type === 'function' ) {
			isFunc = true ;
			funcName = ' ' + options.style.funcName( ( variable.name ? variable.name : '(anonymous)' ) ) ;
			length = options.style.length( '(' + variable.length + ')' ) ;
		}

		isArray = false ;

		if ( Array.isArray( variable ) ) {
			isArray = true ;
			length = options.style.length( '(' + variable.length + ')' ) ;
		}

		if ( ! variable.constructor ) { constructor = '(no constructor)' ; }
		else if ( ! variable.constructor.name ) { constructor = '(anonymous)' ; }
		else { constructor = variable.constructor.name ; }

		isTrivialConstructor = ! variable.constructor || TRIVIAL_CONSTRUCTOR.has( variable.constructor ) ;

		constructor = options.style.constructorName( constructor ) ;
		proto = Object.getPrototypeOf( variable ) ;

		str += pre ;

		if ( ! options.noType && ( ! options.noTypeButConstructor || ! isTrivialConstructor ) ) {
			if ( runtime.forceType && ! options.noType && ! options.noTypeButConstructor ) {
				str += options.style.type( runtime.forceType ) ;
			}
			else if ( options.noTypeButConstructor ) {
				str += constructor ;
			}
			else {
				str += constructor + funcName + length + ' ' + options.style.type( type ) + descriptorStr ;
			}

			if ( ! isFunc || options.funcDetails ) { str += ' ' ; }	// if no funcDetails imply no space here
		}

		if ( isArray && options.noArrayProperty ) {
			propertyList = [ ... Array( variable.length ).keys() ] ;
		}
		else {
			propertyList = Object.getOwnPropertyNames( variable ) ;
		}

		if ( options.sort ) { propertyList.sort() ; }

		// Special Objects
		specialObject = specialObjectSubstitution( variable , runtime , options ) ;

		if ( options.protoBlackList && options.protoBlackList.has( proto ) ) {
			str += options.style.limit( '[skip]' ) + options.style.newline ;
		}
		else if ( specialObject !== undefined ) {
			if ( typeof specialObject === 'string' ) {
				str += '=> ' + specialObject + options.style.newline ;
			}
			else {
				str += '=> ' + inspect_(
					{
						depth: runtime.depth ,
						ancestors: runtime.ancestors ,
						noPre: true
					} ,
					options ,
					specialObject
				) ;
			}
		}
		else if ( isFunc && ! options.funcDetails ) {
			str += options.style.newline ;
		}
		else if ( ! propertyList.length && ! options.proto ) {
			str += ( options.noMarkup ? '' : isArray ? '[]' : '{}' ) + options.style.newline ;
		}
		else if ( runtime.depth >= options.depth ) {
			str += options.style.limit( '[depth limit]' ) + options.style.newline ;
		}
		else if ( runtime.ancestors.indexOf( variable ) !== -1 ) {
			str += options.style.limit( '[circular]' ) + options.style.newline ;
		}
		else {
			/*
			str +=
				options.noMarkup ? ( isArray && options.noIndex && ! runtime.keyIsProperty ? '' : options.style.newline ) :
				( isArray ? '[' : '{' ) + options.style.newline ;
			//*/
			//*
			str += ( options.noMarkup ? '' : isArray ? '[' : '{'  ) + options.style.newline ;
			//*/

			// Do not use .concat() here, it doesn't works as expected with arrays...
			nextAncestors = runtime.ancestors.slice() ;
			nextAncestors.push( variable ) ;

			for ( i = 0 ; i < propertyList.length && str.length < options.outputMaxLength ; i ++ ) {
				if ( ! isArray && (
					( options.propertyBlackList && options.propertyBlackList.has( propertyList[ i ] ) )
					|| ( options.useInspectPropertyBlackList && ( variable.inspectPropertyBlackList instanceof Set ) && variable.inspectPropertyBlackList.has( propertyList[ i ] ) )
				) ) {
					//str += options.style.limit( '[skip]' ) + options.style.newline ;
					continue ;
				}

				if ( isArray && options.noArrayProperty && ! ( propertyList[ i ] in variable ) ) {
					// Hole in the array (sparse array, item deleted, ...)
					str += inspect_(
						{
							depth: runtime.depth + 1 ,
							ancestors: nextAncestors ,
							key: propertyList[ i ] ,
							keyIsProperty: false
						} ,
						options ,
						EMPTY
					) ;
				}
				else {
					try {
						descriptor = Object.getOwnPropertyDescriptor( variable , propertyList[ i ] ) ;
						// Note: descriptor can be undefined, this happens when the object is a Proxy with a bad implementation:
						// it reports that key (Object.keys()) but doesn't give the descriptor for it.

						if ( descriptor && ! descriptor.enumerable && options.enumOnly ) { continue ; }
						keyIsProperty = ! isArray || ! descriptor.enumerable || isNaN( propertyList[ i ] ) ;

						if ( ! options.noDescriptor && descriptor && ( descriptor.get || descriptor.set ) ) {
							str += inspect_(
								{
									depth: runtime.depth + 1 ,
									ancestors: nextAncestors ,
									key: propertyList[ i ] ,
									keyIsProperty: keyIsProperty ,
									descriptor: descriptor ,
									forceType: 'getter/setter'
								} ,
								options ,
								{ get: descriptor.get , set: descriptor.set }
							) ;
						}
						else {
							str += inspect_(
								{
									depth: runtime.depth + 1 ,
									ancestors: nextAncestors ,
									key: propertyList[ i ] ,
									keyIsProperty: keyIsProperty ,
									descriptor: options.noDescriptor ? undefined : descriptor || { error: "Bad Proxy Descriptor" }
								} ,
								options ,
								variable[ propertyList[ i ] ]
							) ;
						}
					}
					catch ( error ) {
						str += inspect_(
							{
								depth: runtime.depth + 1 ,
								ancestors: nextAncestors ,
								key: propertyList[ i ] ,
								keyIsProperty: keyIsProperty ,
								descriptor: options.noDescriptor ? undefined : descriptor
							} ,
							options ,
							error
						) ;
					}
				}

				if ( i < propertyList.length - 1 ) { str += options.style.comma ; }
			}

			if ( options.proto ) {
				str += inspect_(
					{
						depth: runtime.depth + 1 ,
						ancestors: nextAncestors ,
						key: '__proto__' ,
						keyIsProperty: true
					} ,
					options ,
					proto
				) ;
			}

			str += options.noMarkup ? '' : indent + ( isArray ? ']' : '}' ) + options.style.newline ;
		}
	}


	// Finalizing


	if ( runtime.depth === 0 ) {
		if ( options.style.trim ) { str = str.trim() ; }
		if ( options.style === 'html' ) { str = escape.html( str ) ; }
	}

	return str ;
}



function keyNeedingQuotes( key ) {
	if ( ! key.length ) { return true ; }
	return false ;
}



var promiseStates = [ 'pending' , 'fulfilled' , 'rejected' ] ;



// Some special object are better written down when substituted by something else
function specialObjectSubstitution( object , runtime , options ) {
	if ( typeof object.constructor !== 'function' ) {
		// Some objects have no constructor, e.g.: Object.create(null)
		//console.error( object ) ;
		return ;
	}

	if ( object instanceof String ) {
		return object.toString() ;
	}

	if ( object instanceof RegExp ) {
		return object.toString() ;
	}

	if ( object instanceof Date ) {
		return object.toString() + ' [' + object.getTime() + ']' ;
	}

	if ( typeof Set === 'function' && object instanceof Set ) {
		// This is an ES6 'Set' Object
		return Array.from( object ) ;
	}

	if ( typeof Map === 'function' && object instanceof Map ) {
		// This is an ES6 'Map' Object
		return Array.from( object ) ;
	}

	if ( object instanceof Promise ) {
		if ( process && process.binding && process.binding( 'util' ) && process.binding( 'util' ).getPromiseDetails ) {
			let details = process.binding( 'util' ).getPromiseDetails( object ) ;
			let state =  promiseStates[ details[ 0 ] ] ;
			let str = 'Promise <' + state + '>' ;

			if ( state === 'fulfilled' ) {
				str += ' ' + inspect_(
					{
						depth: runtime.depth ,
						ancestors: runtime.ancestors ,
						noPre: true
					} ,
					options ,
					details[ 1 ]
				) ;
			}
			else if ( state === 'rejected' ) {
				if ( details[ 1 ] instanceof Error ) {
					str += ' ' + inspectError(
						{
							style: options.style ,
							noErrorStack: true
						} ,
						details[ 1 ]
					) ;
				}
				else {
					str += ' ' + inspect_(
						{
							depth: runtime.depth ,
							ancestors: runtime.ancestors ,
							noPre: true
						} ,
						options ,
						details[ 1 ]
					) ;
				}
			}

			return str ;
		}
	}

	if ( object._bsontype ) {
		// This is a MongoDB ObjectID, rather boring to display in its original form
		// due to esoteric characters that confuse both the user and the terminal displaying it.
		// Substitute it to its string representation
		return object.toString() ;
	}

	if ( options.useInspect && typeof object.inspect === 'function' ) {
		return object.inspect() ;
	}

	return ;
}



/*
	Options:
		noErrorStack: set to true if the stack should not be displayed
*/
function inspectError( options , error ) {
	var str = '' , stack , type , code ;

	if ( arguments.length < 2 ) { error = options ; options = {} ; }
	else if ( ! options || typeof options !== 'object' ) { options = {} ; }

	if ( ! options.style ) { options.style = inspectStyle.none ; }
	else if ( typeof options.style === 'string' ) { options.style = inspectStyle[ options.style ] ; }

	if ( ! ( error instanceof Error ) ) {
		str += '[not an Error] ' ;

		if ( typeof error === 'string' ) {
			let maxLength = 5000 ;

			if ( error.length > maxLength ) {
				str += options.style.errorMessage( escape.control( error.slice( 0 , maxLength - 1 ) , true ) ) + '…'
					+ options.style.length( '(' + error.length + ' - TRUNCATED)' )
					+ options.style.newline ;
			}
			else {
				str += options.style.errorMessage( escape.control( error , true ) )
					+ options.style.newline ;
			}

			return str ;
		}
		else if ( ! error || typeof error !== 'object' || ! error.name || typeof error.name !== 'string' || ! error.message || typeof error.message !== 'string' ) {
			str += inspect( options , error ) ;
			return str ;
		}

		// It's an object, but it's compatible with Error, so we can move on...
	}

	if ( error.stack && ! options.noErrorStack ) { stack = inspectStack( options , error.stack ) ; }

	type = error.type || error.constructor.name ;
	code = error.code || error.name || error.errno || error.number ;

	str += options.style.errorType( type ) +
		( code ? ' [' + options.style.errorType( code ) + ']' : '' ) + ': ' ;
	str += options.style.errorMessage( error.message ) + '\n' ;

	if ( stack ) { str += options.style.errorStack( stack ) + '\n' ; }

	if ( error.from ) {
		str += options.style.newline + options.style.errorFromMessage( 'From error:' ) + options.style.newline + inspectError( options , error.from ) ;
	}

	return str ;
}

exports.inspectError = inspectError ;



function inspectStack( options , stack ) {
	if ( arguments.length < 2 ) { stack = options ; options = {} ; }
	else if ( ! options || typeof options !== 'object' ) { options = {} ; }

	if ( ! options.style ) { options.style = inspectStyle.none ; }
	else if ( typeof options.style === 'string' ) { options.style = inspectStyle[ options.style ] ; }

	if ( ! stack ) { return ; }

	if ( ( options.browser || process.browser ) && stack.indexOf( '@' ) !== -1 ) {
		// Assume a Firefox-compatible stack-trace here...
		stack = stack
			.replace( /[</]*(?=@)/g , '' )	// Firefox output some WTF </</</</< stuff in its stack trace -- removing that
			.replace(
				/^\s*([^@]*)\s*@\s*([^\n]*)(?::([0-9]+):([0-9]+))?$/mg ,
				( matches , method , file , line , column ) => {
					return options.style.errorStack( '    at ' ) +
						( method ? options.style.errorStackMethod( method ) + ' ' : '' ) +
						options.style.errorStack( '(' ) +
						( file ? options.style.errorStackFile( file ) : options.style.errorStack( 'unknown' ) ) +
						( line ? options.style.errorStack( ':' ) + options.style.errorStackLine( line ) : '' ) +
						( column ? options.style.errorStack( ':' ) + options.style.errorStackColumn( column ) : '' ) +
						options.style.errorStack( ')' ) ;
				}
			) ;
	}
	else {
		stack = stack.replace( /^[^\n]*\n/ , '' ) ;
		stack = stack.replace(
			/^\s*(at)\s+(?:(?:(async|new)\s+)?([^\s:()[\]\n]+(?:\([^)]+\))?)\s)?(?:\[as ([^\s:()[\]\n]+)\]\s)?(?:\(?([^:()[\]\n]+):([0-9]+):([0-9]+)\)?)?$/mg ,
			( matches , at , keyword , method , as , file , line , column ) => {
				return options.style.errorStack( '    at ' ) +
					( keyword ? options.style.errorStackKeyword( keyword ) + ' ' : '' ) +
					( method ? options.style.errorStackMethod( method ) + ' ' : '' ) +
					( as ? options.style.errorStack( '[as ' ) + options.style.errorStackMethodAs( as ) + options.style.errorStack( '] ' ) : '' ) +
					options.style.errorStack( '(' ) +
					( file ? options.style.errorStackFile( file ) : options.style.errorStack( 'unknown' ) ) +
					( line ? options.style.errorStack( ':' ) + options.style.errorStackLine( line ) : '' ) +
					( column ? options.style.errorStack( ':' ) + options.style.errorStackColumn( column ) : '' ) +
					options.style.errorStack( ')' ) ;
			}
		) ;
	}

	return stack ;
}

exports.inspectStack = inspectStack ;



// Inspect's styles

var inspectStyle = {} ;

var inspectStyleNoop = str => str ;



inspectStyle.none = {
	trim: false ,
	tab: '    ' ,
	newline: '\n' ,
	comma: '' ,
	limit: inspectStyleNoop ,
	type: str => '<' + str + '>' ,
	constant: inspectStyleNoop ,
	funcName: inspectStyleNoop ,
	constructorName: str => '<' + str + '>' ,
	length: inspectStyleNoop ,
	key: inspectStyleNoop ,
	index: str => '[' + str + '] ' ,
	number: inspectStyleNoop ,
	inspect: inspectStyleNoop ,
	string: inspectStyleNoop ,
	errorType: inspectStyleNoop ,
	errorMessage: inspectStyleNoop ,
	errorStack: inspectStyleNoop ,
	errorStackKeyword: inspectStyleNoop ,
	errorStackMethod: inspectStyleNoop ,
	errorStackMethodAs: inspectStyleNoop ,
	errorStackFile: inspectStyleNoop ,
	errorStackLine: inspectStyleNoop ,
	errorStackColumn: inspectStyleNoop ,
	errorFromMessage: inspectStyleNoop ,
	truncate: ( str , maxLength ) => str.slice( 0 , maxLength - 1 ) + '…'
} ;



inspectStyle.inline = Object.assign( {} , inspectStyle.none , {
	trim: true ,
	tab: '' ,
	newline: ' ' ,
	comma: ', ' ,
	length: () => '' ,
	index: () => ''
	//type: () => '' ,
} ) ;



inspectStyle.color = Object.assign( {} , inspectStyle.none , {
	limit: str => ansi.bold + ansi.brightRed + str + ansi.reset ,
	type: str => ansi.italic + ansi.brightBlack + str + ansi.reset ,
	constant: str => ansi.cyan + str + ansi.reset ,
	funcName: str => ansi.italic + ansi.magenta + str + ansi.reset ,
	constructorName: str => ansi.magenta + str + ansi.reset ,
	length: str => ansi.italic + ansi.brightBlack + str + ansi.reset ,
	key: str => ansi.green + str + ansi.reset ,
	index: str => ansi.blue + '[' + str + ']' + ansi.reset + ' ' ,
	number: str => ansi.cyan + str + ansi.reset ,
	inspect: str => ansi.cyan + str + ansi.reset ,
	string: str => ansi.blue + str + ansi.reset ,
	errorType: str => ansi.red + ansi.bold + str + ansi.reset ,
	errorMessage: str => ansi.red + ansi.italic + str + ansi.reset ,
	errorStack: str => ansi.brightBlack + str + ansi.reset ,
	errorStackKeyword: str => ansi.italic + ansi.bold + str + ansi.reset ,
	errorStackMethod: str => ansi.brightYellow + str + ansi.reset ,
	errorStackMethodAs: str => ansi.yellow + str + ansi.reset ,
	errorStackFile: str => ansi.brightCyan + str + ansi.reset ,
	errorStackLine: str => ansi.blue + str + ansi.reset ,
	errorStackColumn: str => ansi.magenta + str + ansi.reset ,
	errorFromMessage: str => ansi.yellow + ansi.underline + str + ansi.reset ,
	truncate: ( str , maxLength ) => {
		var trail = ansi.gray + '…' + ansi.reset ;
		str = str.slice( 0 , maxLength - trail.length ) ;

		// Search for an ansi escape sequence at the end, that could be truncated.
		// The longest one is '\x1b[107m': 6 characters.
		var lastEscape = str.lastIndexOf( '\x1b' ) ;
		if ( lastEscape >= str.length - 6 ) { str = str.slice( 0 , lastEscape ) ; }

		return str + trail ;
	}
} ) ;



inspectStyle.html = Object.assign( {} , inspectStyle.none , {
	tab: '&nbsp;&nbsp;&nbsp;&nbsp;' ,
	newline: '<br />' ,
	limit: str => '<span style="color:red">' + str + '</span>' ,
	type: str => '<i style="color:gray">' + str + '</i>' ,
	constant: str => '<span style="color:cyan">' + str + '</span>' ,
	funcName: str => '<i style="color:magenta">' + str + '</i>' ,
	constructorName: str => '<span style="color:magenta">' + str + '</span>' ,
	length: str => '<i style="color:gray">' + str + '</i>' ,
	key: str => '<span style="color:green">' + str + '</span>' ,
	index: str => '<span style="color:blue">[' + str + ']</span> ' ,
	number: str => '<span style="color:cyan">' + str + '</span>' ,
	inspect: str => '<span style="color:cyan">' + str + '</span>' ,
	string: str => '<span style="color:blue">' + str + '</span>' ,
	errorType: str => '<span style="color:red">' + str + '</span>' ,
	errorMessage: str => '<span style="color:red">' + str + '</span>' ,
	errorStack: str => '<span style="color:gray">' + str + '</span>' ,
	errorStackKeyword: str => '<i>' + str + '</i>' ,
	errorStackMethod: str => '<span style="color:yellow">' + str + '</span>' ,
	errorStackMethodAs: str => '<span style="color:yellow">' + str + '</span>' ,
	errorStackFile: str => '<span style="color:cyan">' + str + '</span>' ,
	errorStackLine: str => '<span style="color:blue">' + str + '</span>' ,
	errorStackColumn: str => '<span style="color:gray">' + str + '</span>' ,
	errorFromMessage: str => '<span style="color:yellow">' + str + '</span>'
} ) ;


}).call(this)}).call(this,{"isBuffer":require("../../../../../../../../opt/node-v14.15.4/lib/node_modules/browserify/node_modules/is-buffer/index.js")},require('_process'))
},{"../../../../../../../../opt/node-v14.15.4/lib/node_modules/browserify/node_modules/is-buffer/index.js":174,"./ansi.js":117,"./escape.js":121,"_process":191}],125:[function(require,module,exports){
module.exports={"😀":"grinning-face","😃":"grinning-face-with-big-eyes","😄":"grinning-face-with-smiling-eyes","😁":"beaming-face-with-smiling-eyes","😆":"grinning-squinting-face","😅":"grinning-face-with-sweat","🤣":"rolling-on-the-floor-laughing","😂":"face-with-tears-of-joy","🙂":"slightly-smiling-face","🙃":"upside-down-face","🫠":"melting-face","😉":"winking-face","😊":"smiling-face-with-smiling-eyes","😇":"smiling-face-with-halo","🥰":"smiling-face-with-hearts","😍":"smiling-face-with-heart-eyes","🤩":"star-struck","😘":"face-blowing-a-kiss","😗":"kissing-face","☺️":"smiling-face","😚":"kissing-face-with-closed-eyes","😙":"kissing-face-with-smiling-eyes","🥲":"smiling-face-with-tear","😋":"face-savoring-food","😛":"face-with-tongue","😜":"winking-face-with-tongue","🤪":"zany-face","😝":"squinting-face-with-tongue","🤑":"money-mouth-face","🤗":"smiling-face-with-open-hands","🤭":"face-with-hand-over-mouth","🫢":"face-with-open-eyes-and-hand-over-mouth","🫣":"face-with-peeking-eye","🤫":"shushing-face","🤔":"thinking-face","🫡":"saluting-face","🤐":"zipper-mouth-face","🤨":"face-with-raised-eyebrow","😐":"neutral-face","😑":"expressionless-face","😶":"face-without-mouth","🫥":"dotted-line-face","😶‍🌫️":"face-in-clouds","😏":"smirking-face","😒":"unamused-face","🙄":"face-with-rolling-eyes","😬":"grimacing-face","😮‍💨":"face-exhaling","🤥":"lying-face","🫨":"shaking-face","😌":"relieved-face","😔":"pensive-face","😪":"sleepy-face","🤤":"drooling-face","😴":"sleeping-face","😷":"face-with-medical-mask","🤒":"face-with-thermometer","🤕":"face-with-head-bandage","🤢":"nauseated-face","🤮":"face-vomiting","🤧":"sneezing-face","🥵":"hot-face","🥶":"cold-face","🥴":"woozy-face","😵":"face-with-crossed-out-eyes","😵‍💫":"face-with-spiral-eyes","🤯":"exploding-head","🤠":"cowboy-hat-face","🥳":"partying-face","🥸":"disguised-face","😎":"smiling-face-with-sunglasses","🤓":"nerd-face","🧐":"face-with-monocle","😕":"confused-face","🫤":"face-with-diagonal-mouth","😟":"worried-face","🙁":"slightly-frowning-face","☹️":"frowning-face","😮":"face-with-open-mouth","😯":"hushed-face","😲":"astonished-face","😳":"flushed-face","🥺":"pleading-face","🥹":"face-holding-back-tears","😦":"frowning-face-with-open-mouth","😧":"anguished-face","😨":"fearful-face","😰":"anxious-face-with-sweat","😥":"sad-but-relieved-face","😢":"crying-face","😭":"loudly-crying-face","😱":"face-screaming-in-fear","😖":"confounded-face","😣":"persevering-face","😞":"disappointed-face","😓":"downcast-face-with-sweat","😩":"weary-face","😫":"tired-face","🥱":"yawning-face","😤":"face-with-steam-from-nose","😡":"enraged-face","😠":"angry-face","🤬":"face-with-symbols-on-mouth","😈":"smiling-face-with-horns","👿":"angry-face-with-horns","💀":"skull","☠️":"skull-and-crossbones","💩":"pile-of-poo","🤡":"clown-face","👹":"ogre","👺":"goblin","👻":"ghost","👽":"alien","👾":"alien-monster","🤖":"robot","😺":"grinning-cat","😸":"grinning-cat-with-smiling-eyes","😹":"cat-with-tears-of-joy","😻":"smiling-cat-with-heart-eyes","😼":"cat-with-wry-smile","😽":"kissing-cat","🙀":"weary-cat","😿":"crying-cat","😾":"pouting-cat","🙈":"see-no-evil-monkey","🙉":"hear-no-evil-monkey","🙊":"speak-no-evil-monkey","💌":"love-letter","💘":"heart-with-arrow","💝":"heart-with-ribbon","💖":"sparkling-heart","💗":"growing-heart","💓":"beating-heart","💞":"revolving-hearts","💕":"two-hearts","💟":"heart-decoration","❣️":"heart-exclamation","💔":"broken-heart","❤️‍🔥":"heart-on-fire","❤️‍🩹":"mending-heart","❤️":"red-heart","🩷":"pink-heart","🧡":"orange-heart","💛":"yellow-heart","💚":"green-heart","💙":"blue-heart","🩵":"light-blue-heart","💜":"purple-heart","🤎":"brown-heart","🖤":"black-heart","🩶":"grey-heart","🤍":"white-heart","💋":"kiss-mark","💯":"hundred-points","💢":"anger-symbol","💥":"collision","💫":"dizzy","💦":"sweat-droplets","💨":"dashing-away","🕳️":"hole","💬":"speech-balloon","👁️‍🗨️":"eye-in-speech-bubble","🗨️":"left-speech-bubble","🗯️":"right-anger-bubble","💭":"thought-balloon","💤":"zzz","👋":"waving-hand","🤚":"raised-back-of-hand","🖐️":"hand-with-fingers-splayed","✋":"raised-hand","🖖":"vulcan-salute","🫱":"rightwards-hand","🫲":"leftwards-hand","🫳":"palm-down-hand","🫴":"palm-up-hand","🫷":"leftwards-pushing-hand","🫸":"rightwards-pushing-hand","👌":"ok-hand","🤌":"pinched-fingers","🤏":"pinching-hand","✌️":"victory-hand","🤞":"crossed-fingers","🫰":"hand-with-index-finger-and-thumb-crossed","🤟":"love-you-gesture","🤘":"sign-of-the-horns","🤙":"call-me-hand","👈":"backhand-index-pointing-left","👉":"backhand-index-pointing-right","👆":"backhand-index-pointing-up","🖕":"middle-finger","👇":"backhand-index-pointing-down","☝️":"index-pointing-up","🫵":"index-pointing-at-the-viewer","👍":"thumbs-up","👎":"thumbs-down","✊":"raised-fist","👊":"oncoming-fist","🤛":"left-facing-fist","🤜":"right-facing-fist","👏":"clapping-hands","🙌":"raising-hands","🫶":"heart-hands","👐":"open-hands","🤲":"palms-up-together","🤝":"handshake","🙏":"folded-hands","✍️":"writing-hand","💅":"nail-polish","🤳":"selfie","💪":"flexed-biceps","🦾":"mechanical-arm","🦿":"mechanical-leg","🦵":"leg","🦶":"foot","👂":"ear","🦻":"ear-with-hearing-aid","👃":"nose","🧠":"brain","🫀":"anatomical-heart","🫁":"lungs","🦷":"tooth","🦴":"bone","👀":"eyes","👁️":"eye","👅":"tongue","👄":"mouth","🫦":"biting-lip","👶":"baby","🧒":"child","👦":"boy","👧":"girl","🧑":"person","👱":"person-blond-hair","👨":"man","🧔":"person-beard","🧔‍♂️":"man-beard","🧔‍♀️":"woman-beard","👨‍🦰":"man-red-hair","👨‍🦱":"man-curly-hair","👨‍🦳":"man-white-hair","👨‍🦲":"man-bald","👩":"woman","👩‍🦰":"woman-red-hair","🧑‍🦰":"person-red-hair","👩‍🦱":"woman-curly-hair","🧑‍🦱":"person-curly-hair","👩‍🦳":"woman-white-hair","🧑‍🦳":"person-white-hair","👩‍🦲":"woman-bald","🧑‍🦲":"person-bald","👱‍♀️":"woman-blond-hair","👱‍♂️":"man-blond-hair","🧓":"older-person","👴":"old-man","👵":"old-woman","🙍":"person-frowning","🙍‍♂️":"man-frowning","🙍‍♀️":"woman-frowning","🙎":"person-pouting","🙎‍♂️":"man-pouting","🙎‍♀️":"woman-pouting","🙅":"person-gesturing-no","🙅‍♂️":"man-gesturing-no","🙅‍♀️":"woman-gesturing-no","🙆":"person-gesturing-ok","🙆‍♂️":"man-gesturing-ok","🙆‍♀️":"woman-gesturing-ok","💁":"person-tipping-hand","💁‍♂️":"man-tipping-hand","💁‍♀️":"woman-tipping-hand","🙋":"person-raising-hand","🙋‍♂️":"man-raising-hand","🙋‍♀️":"woman-raising-hand","🧏":"deaf-person","🧏‍♂️":"deaf-man","🧏‍♀️":"deaf-woman","🙇":"person-bowing","🙇‍♂️":"man-bowing","🙇‍♀️":"woman-bowing","🤦":"person-facepalming","🤦‍♂️":"man-facepalming","🤦‍♀️":"woman-facepalming","🤷":"person-shrugging","🤷‍♂️":"man-shrugging","🤷‍♀️":"woman-shrugging","🧑‍⚕️":"health-worker","👨‍⚕️":"man-health-worker","👩‍⚕️":"woman-health-worker","🧑‍🎓":"student","👨‍🎓":"man-student","👩‍🎓":"woman-student","🧑‍🏫":"teacher","👨‍🏫":"man-teacher","👩‍🏫":"woman-teacher","🧑‍⚖️":"judge","👨‍⚖️":"man-judge","👩‍⚖️":"woman-judge","🧑‍🌾":"farmer","👨‍🌾":"man-farmer","👩‍🌾":"woman-farmer","🧑‍🍳":"cook","👨‍🍳":"man-cook","👩‍🍳":"woman-cook","🧑‍🔧":"mechanic","👨‍🔧":"man-mechanic","👩‍🔧":"woman-mechanic","🧑‍🏭":"factory-worker","👨‍🏭":"man-factory-worker","👩‍🏭":"woman-factory-worker","🧑‍💼":"office-worker","👨‍💼":"man-office-worker","👩‍💼":"woman-office-worker","🧑‍🔬":"scientist","👨‍🔬":"man-scientist","👩‍🔬":"woman-scientist","🧑‍💻":"technologist","👨‍💻":"man-technologist","👩‍💻":"woman-technologist","🧑‍🎤":"singer","👨‍🎤":"man-singer","👩‍🎤":"woman-singer","🧑‍🎨":"artist","👨‍🎨":"man-artist","👩‍🎨":"woman-artist","🧑‍✈️":"pilot","👨‍✈️":"man-pilot","👩‍✈️":"woman-pilot","🧑‍🚀":"astronaut","👨‍🚀":"man-astronaut","👩‍🚀":"woman-astronaut","🧑‍🚒":"firefighter","👨‍🚒":"man-firefighter","👩‍🚒":"woman-firefighter","👮":"police-officer","👮‍♂️":"man-police-officer","👮‍♀️":"woman-police-officer","🕵️":"detective","🕵️‍♂️":"man-detective","🕵️‍♀️":"woman-detective","💂":"guard","💂‍♂️":"man-guard","💂‍♀️":"woman-guard","🥷":"ninja","👷":"construction-worker","👷‍♂️":"man-construction-worker","👷‍♀️":"woman-construction-worker","🫅":"person-with-crown","🤴":"prince","👸":"princess","👳":"person-wearing-turban","👳‍♂️":"man-wearing-turban","👳‍♀️":"woman-wearing-turban","👲":"person-with-skullcap","🧕":"woman-with-headscarf","🤵":"person-in-tuxedo","🤵‍♂️":"man-in-tuxedo","🤵‍♀️":"woman-in-tuxedo","👰":"person-with-veil","👰‍♂️":"man-with-veil","👰‍♀️":"woman-with-veil","🤰":"pregnant-woman","🫃":"pregnant-man","🫄":"pregnant-person","🤱":"breast-feeding","👩‍🍼":"woman-feeding-baby","👨‍🍼":"man-feeding-baby","🧑‍🍼":"person-feeding-baby","👼":"baby-angel","🎅":"santa-claus","🤶":"mrs-claus","🧑‍🎄":"mx-claus","🦸":"superhero","🦸‍♂️":"man-superhero","🦸‍♀️":"woman-superhero","🦹":"supervillain","🦹‍♂️":"man-supervillain","🦹‍♀️":"woman-supervillain","🧙":"mage","🧙‍♂️":"man-mage","🧙‍♀️":"woman-mage","🧚":"fairy","🧚‍♂️":"man-fairy","🧚‍♀️":"woman-fairy","🧛":"vampire","🧛‍♂️":"man-vampire","🧛‍♀️":"woman-vampire","🧜":"merperson","🧜‍♂️":"merman","🧜‍♀️":"mermaid","🧝":"elf","🧝‍♂️":"man-elf","🧝‍♀️":"woman-elf","🧞":"genie","🧞‍♂️":"man-genie","🧞‍♀️":"woman-genie","🧟":"zombie","🧟‍♂️":"man-zombie","🧟‍♀️":"woman-zombie","🧌":"troll","💆":"person-getting-massage","💆‍♂️":"man-getting-massage","💆‍♀️":"woman-getting-massage","💇":"person-getting-haircut","💇‍♂️":"man-getting-haircut","💇‍♀️":"woman-getting-haircut","🚶":"person-walking","🚶‍♂️":"man-walking","🚶‍♀️":"woman-walking","🧍":"person-standing","🧍‍♂️":"man-standing","🧍‍♀️":"woman-standing","🧎":"person-kneeling","🧎‍♂️":"man-kneeling","🧎‍♀️":"woman-kneeling","🧑‍🦯":"person-with-white-cane","👨‍🦯":"man-with-white-cane","👩‍🦯":"woman-with-white-cane","🧑‍🦼":"person-in-motorized-wheelchair","👨‍🦼":"man-in-motorized-wheelchair","👩‍🦼":"woman-in-motorized-wheelchair","🧑‍🦽":"person-in-manual-wheelchair","👨‍🦽":"man-in-manual-wheelchair","👩‍🦽":"woman-in-manual-wheelchair","🏃":"person-running","🏃‍♂️":"man-running","🏃‍♀️":"woman-running","💃":"woman-dancing","🕺":"man-dancing","🕴️":"person-in-suit-levitating","👯":"people-with-bunny-ears","👯‍♂️":"men-with-bunny-ears","👯‍♀️":"women-with-bunny-ears","🧖":"person-in-steamy-room","🧖‍♂️":"man-in-steamy-room","🧖‍♀️":"woman-in-steamy-room","🧗":"person-climbing","🧗‍♂️":"man-climbing","🧗‍♀️":"woman-climbing","🤺":"person-fencing","🏇":"horse-racing","⛷️":"skier","🏂":"snowboarder","🏌️":"person-golfing","🏌️‍♂️":"man-golfing","🏌️‍♀️":"woman-golfing","🏄":"person-surfing","🏄‍♂️":"man-surfing","🏄‍♀️":"woman-surfing","🚣":"person-rowing-boat","🚣‍♂️":"man-rowing-boat","🚣‍♀️":"woman-rowing-boat","🏊":"person-swimming","🏊‍♂️":"man-swimming","🏊‍♀️":"woman-swimming","⛹️":"person-bouncing-ball","⛹️‍♂️":"man-bouncing-ball","⛹️‍♀️":"woman-bouncing-ball","🏋️":"person-lifting-weights","🏋️‍♂️":"man-lifting-weights","🏋️‍♀️":"woman-lifting-weights","🚴":"person-biking","🚴‍♂️":"man-biking","🚴‍♀️":"woman-biking","🚵":"person-mountain-biking","🚵‍♂️":"man-mountain-biking","🚵‍♀️":"woman-mountain-biking","🤸":"person-cartwheeling","🤸‍♂️":"man-cartwheeling","🤸‍♀️":"woman-cartwheeling","🤼":"people-wrestling","🤼‍♂️":"men-wrestling","🤼‍♀️":"women-wrestling","🤽":"person-playing-water-polo","🤽‍♂️":"man-playing-water-polo","🤽‍♀️":"woman-playing-water-polo","🤾":"person-playing-handball","🤾‍♂️":"man-playing-handball","🤾‍♀️":"woman-playing-handball","🤹":"person-juggling","🤹‍♂️":"man-juggling","🤹‍♀️":"woman-juggling","🧘":"person-in-lotus-position","🧘‍♂️":"man-in-lotus-position","🧘‍♀️":"woman-in-lotus-position","🛀":"person-taking-bath","🛌":"person-in-bed","🧑‍🤝‍🧑":"people-holding-hands","👭":"women-holding-hands","👫":"woman-and-man-holding-hands","👬":"men-holding-hands","💏":"kiss","👩‍❤️‍💋‍👨":"kiss-woman-man","👨‍❤️‍💋‍👨":"kiss-man-man","👩‍❤️‍💋‍👩":"kiss-woman-woman","💑":"couple-with-heart","👩‍❤️‍👨":"couple-with-heart-woman-man","👨‍❤️‍👨":"couple-with-heart-man-man","👩‍❤️‍👩":"couple-with-heart-woman-woman","👪":"family","👨‍👩‍👦":"family-man-woman-boy","👨‍👩‍👧":"family-man-woman-girl","👨‍👩‍👧‍👦":"family-man-woman-girl-boy","👨‍👩‍👦‍👦":"family-man-woman-boy-boy","👨‍👩‍👧‍👧":"family-man-woman-girl-girl","👨‍👨‍👦":"family-man-man-boy","👨‍👨‍👧":"family-man-man-girl","👨‍👨‍👧‍👦":"family-man-man-girl-boy","👨‍👨‍👦‍👦":"family-man-man-boy-boy","👨‍👨‍👧‍👧":"family-man-man-girl-girl","👩‍👩‍👦":"family-woman-woman-boy","👩‍👩‍👧":"family-woman-woman-girl","👩‍👩‍👧‍👦":"family-woman-woman-girl-boy","👩‍👩‍👦‍👦":"family-woman-woman-boy-boy","👩‍👩‍👧‍👧":"family-woman-woman-girl-girl","👨‍👦":"family-man-boy","👨‍👦‍👦":"family-man-boy-boy","👨‍👧":"family-man-girl","👨‍👧‍👦":"family-man-girl-boy","👨‍👧‍👧":"family-man-girl-girl","👩‍👦":"family-woman-boy","👩‍👦‍👦":"family-woman-boy-boy","👩‍👧":"family-woman-girl","👩‍👧‍👦":"family-woman-girl-boy","👩‍👧‍👧":"family-woman-girl-girl","🗣️":"speaking-head","👤":"bust-in-silhouette","👥":"busts-in-silhouette","🫂":"people-hugging","👣":"footprints","🐵":"monkey-face","🐒":"monkey","🦍":"gorilla","🦧":"orangutan","🐶":"dog-face","🐕":"dog","🦮":"guide-dog","🐕‍🦺":"service-dog","🐩":"poodle","🐺":"wolf","🦊":"fox","🦝":"raccoon","🐱":"cat-face","🐈":"cat","🐈‍⬛":"black-cat","🦁":"lion","🐯":"tiger-face","🐅":"tiger","🐆":"leopard","🐴":"horse-face","🫎":"moose","🫏":"donkey","🐎":"horse","🦄":"unicorn","🦓":"zebra","🦌":"deer","🦬":"bison","🐮":"cow-face","🐂":"ox","🐃":"water-buffalo","🐄":"cow","🐷":"pig-face","🐖":"pig","🐗":"boar","🐽":"pig-nose","🐏":"ram","🐑":"ewe","🐐":"goat","🐪":"camel","🐫":"two-hump-camel","🦙":"llama","🦒":"giraffe","🐘":"elephant","🦣":"mammoth","🦏":"rhinoceros","🦛":"hippopotamus","🐭":"mouse-face","🐁":"mouse","🐀":"rat","🐹":"hamster","🐰":"rabbit-face","🐇":"rabbit","🐿️":"chipmunk","🦫":"beaver","🦔":"hedgehog","🦇":"bat","🐻":"bear","🐻‍❄️":"polar-bear","🐨":"koala","🐼":"panda","🦥":"sloth","🦦":"otter","🦨":"skunk","🦘":"kangaroo","🦡":"badger","🐾":"paw-prints","🦃":"turkey","🐔":"chicken","🐓":"rooster","🐣":"hatching-chick","🐤":"baby-chick","🐥":"front-facing-baby-chick","🐦":"bird","🐧":"penguin","🕊️":"dove","🦅":"eagle","🦆":"duck","🦢":"swan","🦉":"owl","🦤":"dodo","🪶":"feather","🦩":"flamingo","🦚":"peacock","🦜":"parrot","🪽":"wing","🐦‍⬛":"black-bird","🪿":"goose","🐸":"frog","🐊":"crocodile","🐢":"turtle","🦎":"lizard","🐍":"snake","🐲":"dragon-face","🐉":"dragon","🦕":"sauropod","🦖":"t-rex","🐳":"spouting-whale","🐋":"whale","🐬":"dolphin","🦭":"seal","🐟":"fish","🐠":"tropical-fish","🐡":"blowfish","🦈":"shark","🐙":"octopus","🐚":"spiral-shell","🪸":"coral","🪼":"jellyfish","🐌":"snail","🦋":"butterfly","🐛":"bug","🐜":"ant","🐝":"honeybee","🪲":"beetle","🐞":"lady-beetle","🦗":"cricket","🪳":"cockroach","🕷️":"spider","🕸️":"spider-web","🦂":"scorpion","🦟":"mosquito","🪰":"fly","🪱":"worm","🦠":"microbe","💐":"bouquet","🌸":"cherry-blossom","💮":"white-flower","🪷":"lotus","🏵️":"rosette","🌹":"rose","🥀":"wilted-flower","🌺":"hibiscus","🌻":"sunflower","🌼":"blossom","🌷":"tulip","🪻":"hyacinth","🌱":"seedling","🪴":"potted-plant","🌲":"evergreen-tree","🌳":"deciduous-tree","🌴":"palm-tree","🌵":"cactus","🌾":"sheaf-of-rice","🌿":"herb","☘️":"shamrock","🍀":"four-leaf-clover","🍁":"maple-leaf","🍂":"fallen-leaf","🍃":"leaf-fluttering-in-wind","🪹":"empty-nest","🪺":"nest-with-eggs","🍄":"mushroom","🍇":"grapes","🍈":"melon","🍉":"watermelon","🍊":"tangerine","🍋":"lemon","🍌":"banana","🍍":"pineapple","🥭":"mango","🍎":"red-apple","🍏":"green-apple","🍐":"pear","🍑":"peach","🍒":"cherries","🍓":"strawberry","🫐":"blueberries","🥝":"kiwi-fruit","🍅":"tomato","🫒":"olive","🥥":"coconut","🥑":"avocado","🍆":"eggplant","🥔":"potato","🥕":"carrot","🌽":"ear-of-corn","🌶️":"hot-pepper","🫑":"bell-pepper","🥒":"cucumber","🥬":"leafy-green","🥦":"broccoli","🧄":"garlic","🧅":"onion","🥜":"peanuts","🫘":"beans","🌰":"chestnut","🫚":"ginger-root","🫛":"pea-pod","🍞":"bread","🥐":"croissant","🥖":"baguette-bread","🫓":"flatbread","🥨":"pretzel","🥯":"bagel","🥞":"pancakes","🧇":"waffle","🧀":"cheese-wedge","🍖":"meat-on-bone","🍗":"poultry-leg","🥩":"cut-of-meat","🥓":"bacon","🍔":"hamburger","🍟":"french-fries","🍕":"pizza","🌭":"hot-dog","🥪":"sandwich","🌮":"taco","🌯":"burrito","🫔":"tamale","🥙":"stuffed-flatbread","🧆":"falafel","🥚":"egg","🍳":"cooking","🥘":"shallow-pan-of-food","🍲":"pot-of-food","🫕":"fondue","🥣":"bowl-with-spoon","🥗":"green-salad","🍿":"popcorn","🧈":"butter","🧂":"salt","🥫":"canned-food","🍱":"bento-box","🍘":"rice-cracker","🍙":"rice-ball","🍚":"cooked-rice","🍛":"curry-rice","🍜":"steaming-bowl","🍝":"spaghetti","🍠":"roasted-sweet-potato","🍢":"oden","🍣":"sushi","🍤":"fried-shrimp","🍥":"fish-cake-with-swirl","🥮":"moon-cake","🍡":"dango","🥟":"dumpling","🥠":"fortune-cookie","🥡":"takeout-box","🦀":"crab","🦞":"lobster","🦐":"shrimp","🦑":"squid","🦪":"oyster","🍦":"soft-ice-cream","🍧":"shaved-ice","🍨":"ice-cream","🍩":"doughnut","🍪":"cookie","🎂":"birthday-cake","🍰":"shortcake","🧁":"cupcake","🥧":"pie","🍫":"chocolate-bar","🍬":"candy","🍭":"lollipop","🍮":"custard","🍯":"honey-pot","🍼":"baby-bottle","🥛":"glass-of-milk","☕":"hot-beverage","🫖":"teapot","🍵":"teacup-without-handle","🍶":"sake","🍾":"bottle-with-popping-cork","🍷":"wine-glass","🍸":"cocktail-glass","🍹":"tropical-drink","🍺":"beer-mug","🍻":"clinking-beer-mugs","🥂":"clinking-glasses","🥃":"tumbler-glass","🫗":"pouring-liquid","🥤":"cup-with-straw","🧋":"bubble-tea","🧃":"beverage-box","🧉":"mate","🧊":"ice","🥢":"chopsticks","🍽️":"fork-and-knife-with-plate","🍴":"fork-and-knife","🥄":"spoon","🔪":"kitchen-knife","🫙":"jar","🏺":"amphora","🌍":"globe-showing-europe-africa","🌎":"globe-showing-americas","🌏":"globe-showing-asia-australia","🌐":"globe-with-meridians","🗺️":"world-map","🗾":"map-of-japan","🧭":"compass","🏔️":"snow-capped-mountain","⛰️":"mountain","🌋":"volcano","🗻":"mount-fuji","🏕️":"camping","🏖️":"beach-with-umbrella","🏜️":"desert","🏝️":"desert-island","🏞️":"national-park","🏟️":"stadium","🏛️":"classical-building","🏗️":"building-construction","🧱":"brick","🪨":"rock","🪵":"wood","🛖":"hut","🏘️":"houses","🏚️":"derelict-house","🏠":"house","🏡":"house-with-garden","🏢":"office-building","🏣":"japanese-post-office","🏤":"post-office","🏥":"hospital","🏦":"bank","🏨":"hotel","🏩":"love-hotel","🏪":"convenience-store","🏫":"school","🏬":"department-store","🏭":"factory","🏯":"japanese-castle","🏰":"castle","💒":"wedding","🗼":"tokyo-tower","🗽":"statue-of-liberty","⛪":"church","🕌":"mosque","🛕":"hindu-temple","🕍":"synagogue","⛩️":"shinto-shrine","🕋":"kaaba","⛲":"fountain","⛺":"tent","🌁":"foggy","🌃":"night-with-stars","🏙️":"cityscape","🌄":"sunrise-over-mountains","🌅":"sunrise","🌆":"cityscape-at-dusk","🌇":"sunset","🌉":"bridge-at-night","♨️":"hot-springs","🎠":"carousel-horse","🛝":"playground-slide","🎡":"ferris-wheel","🎢":"roller-coaster","💈":"barber-pole","🎪":"circus-tent","🚂":"locomotive","🚃":"railway-car","🚄":"high-speed-train","🚅":"bullet-train","🚆":"train","🚇":"metro","🚈":"light-rail","🚉":"station","🚊":"tram","🚝":"monorail","🚞":"mountain-railway","🚋":"tram-car","🚌":"bus","🚍":"oncoming-bus","🚎":"trolleybus","🚐":"minibus","🚑":"ambulance","🚒":"fire-engine","🚓":"police-car","🚔":"oncoming-police-car","🚕":"taxi","🚖":"oncoming-taxi","🚗":"automobile","🚘":"oncoming-automobile","🚙":"sport-utility-vehicle","🛻":"pickup-truck","🚚":"delivery-truck","🚛":"articulated-lorry","🚜":"tractor","🏎️":"racing-car","🏍️":"motorcycle","🛵":"motor-scooter","🦽":"manual-wheelchair","🦼":"motorized-wheelchair","🛺":"auto-rickshaw","🚲":"bicycle","🛴":"kick-scooter","🛹":"skateboard","🛼":"roller-skate","🚏":"bus-stop","🛣️":"motorway","🛤️":"railway-track","🛢️":"oil-drum","⛽":"fuel-pump","🛞":"wheel","🚨":"police-car-light","🚥":"horizontal-traffic-light","🚦":"vertical-traffic-light","🛑":"stop-sign","🚧":"construction","⚓":"anchor","🛟":"ring-buoy","⛵":"sailboat","🛶":"canoe","🚤":"speedboat","🛳️":"passenger-ship","⛴️":"ferry","🛥️":"motor-boat","🚢":"ship","✈️":"airplane","🛩️":"small-airplane","🛫":"airplane-departure","🛬":"airplane-arrival","🪂":"parachute","💺":"seat","🚁":"helicopter","🚟":"suspension-railway","🚠":"mountain-cableway","🚡":"aerial-tramway","🛰️":"satellite","🚀":"rocket","🛸":"flying-saucer","🛎️":"bellhop-bell","🧳":"luggage","⌛":"hourglass-done","⏳":"hourglass-not-done","⌚":"watch","⏰":"alarm-clock","⏱️":"stopwatch","⏲️":"timer-clock","🕰️":"mantelpiece-clock","🕛":"twelve-o-clock","🕧":"twelve-thirty","🕐":"one-o-clock","🕜":"one-thirty","🕑":"two-o-clock","🕝":"two-thirty","🕒":"three-o-clock","🕞":"three-thirty","🕓":"four-o-clock","🕟":"four-thirty","🕔":"five-o-clock","🕠":"five-thirty","🕕":"six-o-clock","🕡":"six-thirty","🕖":"seven-o-clock","🕢":"seven-thirty","🕗":"eight-o-clock","🕣":"eight-thirty","🕘":"nine-o-clock","🕤":"nine-thirty","🕙":"ten-o-clock","🕥":"ten-thirty","🕚":"eleven-o-clock","🕦":"eleven-thirty","🌑":"new-moon","🌒":"waxing-crescent-moon","🌓":"first-quarter-moon","🌔":"waxing-gibbous-moon","🌕":"full-moon","🌖":"waning-gibbous-moon","🌗":"last-quarter-moon","🌘":"waning-crescent-moon","🌙":"crescent-moon","🌚":"new-moon-face","🌛":"first-quarter-moon-face","🌜":"last-quarter-moon-face","🌡️":"thermometer","☀️":"sun","🌝":"full-moon-face","🌞":"sun-with-face","🪐":"ringed-planet","⭐":"star","🌟":"glowing-star","🌠":"shooting-star","🌌":"milky-way","☁️":"cloud","⛅":"sun-behind-cloud","⛈️":"cloud-with-lightning-and-rain","🌤️":"sun-behind-small-cloud","🌥️":"sun-behind-large-cloud","🌦️":"sun-behind-rain-cloud","🌧️":"cloud-with-rain","🌨️":"cloud-with-snow","🌩️":"cloud-with-lightning","🌪️":"tornado","🌫️":"fog","🌬️":"wind-face","🌀":"cyclone","🌈":"rainbow","🌂":"closed-umbrella","☂️":"umbrella","☔":"umbrella-with-rain-drops","⛱️":"umbrella-on-ground","⚡":"high-voltage","❄️":"snowflake","☃️":"snowman","⛄":"snowman-without-snow","☄️":"comet","🔥":"fire","💧":"droplet","🌊":"water-wave","🎃":"jack-o-lantern","🎄":"christmas-tree","🎆":"fireworks","🎇":"sparkler","🧨":"firecracker","✨":"sparkles","🎈":"balloon","🎉":"party-popper","🎊":"confetti-ball","🎋":"tanabata-tree","🎍":"pine-decoration","🎎":"japanese-dolls","🎏":"carp-streamer","🎐":"wind-chime","🎑":"moon-viewing-ceremony","🧧":"red-envelope","🎀":"ribbon","🎁":"wrapped-gift","🎗️":"reminder-ribbon","🎟️":"admission-tickets","🎫":"ticket","🎖️":"military-medal","🏆":"trophy","🏅":"sports-medal","🥇":"1st-place-medal","🥈":"2nd-place-medal","🥉":"3rd-place-medal","⚽":"soccer-ball","⚾":"baseball","🥎":"softball","🏀":"basketball","🏐":"volleyball","🏈":"american-football","🏉":"rugby-football","🎾":"tennis","🥏":"flying-disc","🎳":"bowling","🏏":"cricket-game","🏑":"field-hockey","🏒":"ice-hockey","🥍":"lacrosse","🏓":"ping-pong","🏸":"badminton","🥊":"boxing-glove","🥋":"martial-arts-uniform","🥅":"goal-net","⛳":"flag-in-hole","⛸️":"ice-skate","🎣":"fishing-pole","🤿":"diving-mask","🎽":"running-shirt","🎿":"skis","🛷":"sled","🥌":"curling-stone","🎯":"bullseye","🪀":"yo-yo","🪁":"kite","🔫":"water-pistol","🎱":"pool-8-ball","🔮":"crystal-ball","🪄":"magic-wand","🎮":"video-game","🕹️":"joystick","🎰":"slot-machine","🎲":"game-die","🧩":"puzzle-piece","🧸":"teddy-bear","🪅":"pinata","🪩":"mirror-ball","🪆":"nesting-dolls","♠️":"spade-suit","♥️":"heart-suit","♦️":"diamond-suit","♣️":"club-suit","♟️":"chess-pawn","🃏":"joker","🀄":"mahjong-red-dragon","🎴":"flower-playing-cards","🎭":"performing-arts","🖼️":"framed-picture","🎨":"artist-palette","🧵":"thread","🪡":"sewing-needle","🧶":"yarn","🪢":"knot","👓":"glasses","🕶️":"sunglasses","🥽":"goggles","🥼":"lab-coat","🦺":"safety-vest","👔":"necktie","👕":"t-shirt","👖":"jeans","🧣":"scarf","🧤":"gloves","🧥":"coat","🧦":"socks","👗":"dress","👘":"kimono","🥻":"sari","🩱":"one-piece-swimsuit","🩲":"briefs","🩳":"shorts","👙":"bikini","👚":"woman-s-clothes","🪭":"folding-hand-fan","👛":"purse","👜":"handbag","👝":"clutch-bag","🛍️":"shopping-bags","🎒":"backpack","🩴":"thong-sandal","👞":"man-s-shoe","👟":"running-shoe","🥾":"hiking-boot","🥿":"flat-shoe","👠":"high-heeled-shoe","👡":"woman-s-sandal","🩰":"ballet-shoes","👢":"woman-s-boot","🪮":"hair-pick","👑":"crown","👒":"woman-s-hat","🎩":"top-hat","🎓":"graduation-cap","🧢":"billed-cap","🪖":"military-helmet","⛑️":"rescue-worker-s-helmet","📿":"prayer-beads","💄":"lipstick","💍":"ring","💎":"gem-stone","🔇":"muted-speaker","🔈":"speaker-low-volume","🔉":"speaker-medium-volume","🔊":"speaker-high-volume","📢":"loudspeaker","📣":"megaphone","📯":"postal-horn","🔔":"bell","🔕":"bell-with-slash","🎼":"musical-score","🎵":"musical-note","🎶":"musical-notes","🎙️":"studio-microphone","🎚️":"level-slider","🎛️":"control-knobs","🎤":"microphone","🎧":"headphone","📻":"radio","🎷":"saxophone","🪗":"accordion","🎸":"guitar","🎹":"musical-keyboard","🎺":"trumpet","🎻":"violin","🪕":"banjo","🥁":"drum","🪘":"long-drum","🪇":"maracas","🪈":"flute","📱":"mobile-phone","📲":"mobile-phone-with-arrow","☎️":"telephone","📞":"telephone-receiver","📟":"pager","📠":"fax-machine","🔋":"battery","🪫":"low-battery","🔌":"electric-plug","💻":"laptop","🖥️":"desktop-computer","🖨️":"printer","⌨️":"keyboard","🖱️":"computer-mouse","🖲️":"trackball","💽":"computer-disk","💾":"floppy-disk","💿":"optical-disk","📀":"dvd","🧮":"abacus","🎥":"movie-camera","🎞️":"film-frames","📽️":"film-projector","🎬":"clapper-board","📺":"television","📷":"camera","📸":"camera-with-flash","📹":"video-camera","📼":"videocassette","🔍":"magnifying-glass-tilted-left","🔎":"magnifying-glass-tilted-right","🕯️":"candle","💡":"light-bulb","🔦":"flashlight","🏮":"red-paper-lantern","🪔":"diya-lamp","📔":"notebook-with-decorative-cover","📕":"closed-book","📖":"open-book","📗":"green-book","📘":"blue-book","📙":"orange-book","📚":"books","📓":"notebook","📒":"ledger","📃":"page-with-curl","📜":"scroll","📄":"page-facing-up","📰":"newspaper","🗞️":"rolled-up-newspaper","📑":"bookmark-tabs","🔖":"bookmark","🏷️":"label","💰":"money-bag","🪙":"coin","💴":"yen-banknote","💵":"dollar-banknote","💶":"euro-banknote","💷":"pound-banknote","💸":"money-with-wings","💳":"credit-card","🧾":"receipt","💹":"chart-increasing-with-yen","✉️":"envelope","📧":"e-mail","📨":"incoming-envelope","📩":"envelope-with-arrow","📤":"outbox-tray","📥":"inbox-tray","📦":"package","📫":"closed-mailbox-with-raised-flag","📪":"closed-mailbox-with-lowered-flag","📬":"open-mailbox-with-raised-flag","📭":"open-mailbox-with-lowered-flag","📮":"postbox","🗳️":"ballot-box-with-ballot","✏️":"pencil","✒️":"black-nib","🖋️":"fountain-pen","🖊️":"pen","🖌️":"paintbrush","🖍️":"crayon","📝":"memo","💼":"briefcase","📁":"file-folder","📂":"open-file-folder","🗂️":"card-index-dividers","📅":"calendar","📆":"tear-off-calendar","🗒️":"spiral-notepad","🗓️":"spiral-calendar","📇":"card-index","📈":"chart-increasing","📉":"chart-decreasing","📊":"bar-chart","📋":"clipboard","📌":"pushpin","📍":"round-pushpin","📎":"paperclip","🖇️":"linked-paperclips","📏":"straight-ruler","📐":"triangular-ruler","✂️":"scissors","🗃️":"card-file-box","🗄️":"file-cabinet","🗑️":"wastebasket","🔒":"locked","🔓":"unlocked","🔏":"locked-with-pen","🔐":"locked-with-key","🔑":"key","🗝️":"old-key","🔨":"hammer","🪓":"axe","⛏️":"pick","⚒️":"hammer-and-pick","🛠️":"hammer-and-wrench","🗡️":"dagger","⚔️":"crossed-swords","💣":"bomb","🪃":"boomerang","🏹":"bow-and-arrow","🛡️":"shield","🪚":"carpentry-saw","🔧":"wrench","🪛":"screwdriver","🔩":"nut-and-bolt","⚙️":"gear","🗜️":"clamp","⚖️":"balance-scale","🦯":"white-cane","🔗":"link","⛓️":"chains","🪝":"hook","🧰":"toolbox","🧲":"magnet","🪜":"ladder","⚗️":"alembic","🧪":"test-tube","🧫":"petri-dish","🧬":"dna","🔬":"microscope","🔭":"telescope","📡":"satellite-antenna","💉":"syringe","🩸":"drop-of-blood","💊":"pill","🩹":"adhesive-bandage","🩼":"crutch","🩺":"stethoscope","🩻":"x-ray","🚪":"door","🛗":"elevator","🪞":"mirror","🪟":"window","🛏️":"bed","🛋️":"couch-and-lamp","🪑":"chair","🚽":"toilet","🪠":"plunger","🚿":"shower","🛁":"bathtub","🪤":"mouse-trap","🪒":"razor","🧴":"lotion-bottle","🧷":"safety-pin","🧹":"broom","🧺":"basket","🧻":"roll-of-paper","🪣":"bucket","🧼":"soap","🫧":"bubbles","🪥":"toothbrush","🧽":"sponge","🧯":"fire-extinguisher","🛒":"shopping-cart","🚬":"cigarette","⚰️":"coffin","🪦":"headstone","⚱️":"funeral-urn","🧿":"nazar-amulet","🪬":"hamsa","🗿":"moai","🪧":"placard","🪪":"identification-card","🏧":"atm-sign","🚮":"litter-in-bin-sign","🚰":"potable-water","♿":"wheelchair-symbol","🚹":"men-s-room","🚺":"women-s-room","🚻":"restroom","🚼":"baby-symbol","🚾":"water-closet","🛂":"passport-control","🛃":"customs","🛄":"baggage-claim","🛅":"left-luggage","⚠️":"warning","🚸":"children-crossing","⛔":"no-entry","🚫":"prohibited","🚳":"no-bicycles","🚭":"no-smoking","🚯":"no-littering","🚱":"non-potable-water","🚷":"no-pedestrians","📵":"no-mobile-phones","🔞":"no-one-under-eighteen","☢️":"radioactive","☣️":"biohazard","⬆️":"up-arrow","↗️":"up-right-arrow","➡️":"right-arrow","↘️":"down-right-arrow","⬇️":"down-arrow","↙️":"down-left-arrow","⬅️":"left-arrow","↖️":"up-left-arrow","↕️":"up-down-arrow","↔️":"left-right-arrow","↩️":"right-arrow-curving-left","↪️":"left-arrow-curving-right","⤴️":"right-arrow-curving-up","⤵️":"right-arrow-curving-down","🔃":"clockwise-vertical-arrows","🔄":"counterclockwise-arrows-button","🔙":"back-arrow","🔚":"end-arrow","🔛":"on-arrow","🔜":"soon-arrow","🔝":"top-arrow","🛐":"place-of-worship","⚛️":"atom-symbol","🕉️":"om","✡️":"star-of-david","☸️":"wheel-of-dharma","☯️":"yin-yang","✝️":"latin-cross","☦️":"orthodox-cross","☪️":"star-and-crescent","☮️":"peace-symbol","🕎":"menorah","🔯":"dotted-six-pointed-star","🪯":"khanda","♈":"aries","♉":"taurus","♊":"gemini","♋":"cancer","♌":"leo","♍":"virgo","♎":"libra","♏":"scorpio","♐":"sagittarius","♑":"capricorn","♒":"aquarius","♓":"pisces","⛎":"ophiuchus","🔀":"shuffle-tracks-button","🔁":"repeat-button","🔂":"repeat-single-button","▶️":"play-button","⏩":"fast-forward-button","⏭️":"next-track-button","⏯️":"play-or-pause-button","◀️":"reverse-button","⏪":"fast-reverse-button","⏮️":"last-track-button","🔼":"upwards-button","⏫":"fast-up-button","🔽":"downwards-button","⏬":"fast-down-button","⏸️":"pause-button","⏹️":"stop-button","⏺️":"record-button","⏏️":"eject-button","🎦":"cinema","🔅":"dim-button","🔆":"bright-button","📶":"antenna-bars","🛜":"wireless","📳":"vibration-mode","📴":"mobile-phone-off","♀️":"female-sign","♂️":"male-sign","⚧️":"transgender-symbol","✖️":"multiply","➕":"plus","➖":"minus","➗":"divide","🟰":"heavy-equals-sign","♾️":"infinity","‼️":"double-exclamation-mark","⁉️":"exclamation-question-mark","❓":"red-question-mark","❔":"white-question-mark","❕":"white-exclamation-mark","❗":"red-exclamation-mark","〰️":"wavy-dash","💱":"currency-exchange","💲":"heavy-dollar-sign","⚕️":"medical-symbol","♻️":"recycling-symbol","⚜️":"fleur-de-lis","🔱":"trident-emblem","📛":"name-badge","🔰":"japanese-symbol-for-beginner","⭕":"hollow-red-circle","✅":"check-mark-button","☑️":"check-box-with-check","✔️":"check-mark","❌":"cross-mark","❎":"cross-mark-button","➰":"curly-loop","➿":"double-curly-loop","〽️":"part-alternation-mark","✳️":"eight-spoked-asterisk","✴️":"eight-pointed-star","❇️":"sparkle","©️":"copyright","®️":"registered","™️":"trade-mark","#️⃣":"keycap-#","*️⃣":"keycap-*","0️⃣":"keycap-0","1️⃣":"keycap-1","2️⃣":"keycap-2","3️⃣":"keycap-3","4️⃣":"keycap-4","5️⃣":"keycap-5","6️⃣":"keycap-6","7️⃣":"keycap-7","8️⃣":"keycap-8","9️⃣":"keycap-9","🔟":"keycap-10","🔠":"input-latin-uppercase","🔡":"input-latin-lowercase","🔢":"input-numbers","🔣":"input-symbols","🔤":"input-latin-letters","🅰️":"a-button-blood-type","🆎":"ab-button-blood-type","🅱️":"b-button-blood-type","🆑":"cl-button","🆒":"cool-button","🆓":"free-button","ℹ️":"information","🆔":"id-button","Ⓜ️":"circled-m","🆕":"new-button","🆖":"ng-button","🅾️":"o-button-blood-type","🆗":"ok-button","🅿️":"p-button","🆘":"sos-button","🆙":"up-button","🆚":"vs-button","🈁":"japanese-here-button","🈂️":"japanese-service-charge-button","🈷️":"japanese-monthly-amount-button","🈶":"japanese-not-free-of-charge-button","🈯":"japanese-reserved-button","🉐":"japanese-bargain-button","🈹":"japanese-discount-button","🈚":"japanese-free-of-charge-button","🈲":"japanese-prohibited-button","🉑":"japanese-acceptable-button","🈸":"japanese-application-button","🈴":"japanese-passing-grade-button","🈳":"japanese-vacancy-button","㊗️":"japanese-congratulations-button","㊙️":"japanese-secret-button","🈺":"japanese-open-for-business-button","🈵":"japanese-no-vacancy-button","🔴":"red-circle","🟠":"orange-circle","🟡":"yellow-circle","🟢":"green-circle","🔵":"blue-circle","🟣":"purple-circle","🟤":"brown-circle","⚫":"black-circle","⚪":"white-circle","🟥":"red-square","🟧":"orange-square","🟨":"yellow-square","🟩":"green-square","🟦":"blue-square","🟪":"purple-square","🟫":"brown-square","⬛":"black-large-square","⬜":"white-large-square","◼️":"black-medium-square","◻️":"white-medium-square","◾":"black-medium-small-square","◽":"white-medium-small-square","▪️":"black-small-square","▫️":"white-small-square","🔶":"large-orange-diamond","🔷":"large-blue-diamond","🔸":"small-orange-diamond","🔹":"small-blue-diamond","🔺":"red-triangle-pointed-up","🔻":"red-triangle-pointed-down","💠":"diamond-with-a-dot","🔘":"radio-button","🔳":"white-square-button","🔲":"black-square-button","🏁":"chequered-flag","🚩":"triangular-flag","🎌":"crossed-flags","🏴":"black-flag","🏳️":"white-flag","🏳️‍🌈":"rainbow-flag","🏳️‍⚧️":"transgender-flag","🏴‍☠️":"pirate-flag","🇦🇨":"flag-ascension-island","🇦🇩":"flag-andorra","🇦🇪":"flag-united-arab-emirates","🇦🇫":"flag-afghanistan","🇦🇬":"flag-antigua-&-barbuda","🇦🇮":"flag-anguilla","🇦🇱":"flag-albania","🇦🇲":"flag-armenia","🇦🇴":"flag-angola","🇦🇶":"flag-antarctica","🇦🇷":"flag-argentina","🇦🇸":"flag-american-samoa","🇦🇹":"flag-austria","🇦🇺":"flag-australia","🇦🇼":"flag-aruba","🇦🇽":"flag-aland-islands","🇦🇿":"flag-azerbaijan","🇧🇦":"flag-bosnia-&-herzegovina","🇧🇧":"flag-barbados","🇧🇩":"flag-bangladesh","🇧🇪":"flag-belgium","🇧🇫":"flag-burkina-faso","🇧🇬":"flag-bulgaria","🇧🇭":"flag-bahrain","🇧🇮":"flag-burundi","🇧🇯":"flag-benin","🇧🇱":"flag-st-barthelemy","🇧🇲":"flag-bermuda","🇧🇳":"flag-brunei","🇧🇴":"flag-bolivia","🇧🇶":"flag-caribbean-netherlands","🇧🇷":"flag-brazil","🇧🇸":"flag-bahamas","🇧🇹":"flag-bhutan","🇧🇻":"flag-bouvet-island","🇧🇼":"flag-botswana","🇧🇾":"flag-belarus","🇧🇿":"flag-belize","🇨🇦":"flag-canada","🇨🇨":"flag-cocos-keeling-islands","🇨🇩":"flag-congo-kinshasa","🇨🇫":"flag-central-african-republic","🇨🇬":"flag-congo-brazzaville","🇨🇭":"flag-switzerland","🇨🇮":"flag-cote-d-ivoire","🇨🇰":"flag-cook-islands","🇨🇱":"flag-chile","🇨🇲":"flag-cameroon","🇨🇳":"flag-china","🇨🇴":"flag-colombia","🇨🇵":"flag-clipperton-island","🇨🇷":"flag-costa-rica","🇨🇺":"flag-cuba","🇨🇻":"flag-cape-verde","🇨🇼":"flag-curacao","🇨🇽":"flag-christmas-island","🇨🇾":"flag-cyprus","🇨🇿":"flag-czechia","🇩🇪":"flag-germany","🇩🇬":"flag-diego-garcia","🇩🇯":"flag-djibouti","🇩🇰":"flag-denmark","🇩🇲":"flag-dominica","🇩🇴":"flag-dominican-republic","🇩🇿":"flag-algeria","🇪🇦":"flag-ceuta-&-melilla","🇪🇨":"flag-ecuador","🇪🇪":"flag-estonia","🇪🇬":"flag-egypt","🇪🇭":"flag-western-sahara","🇪🇷":"flag-eritrea","🇪🇸":"flag-spain","🇪🇹":"flag-ethiopia","🇪🇺":"flag-european-union","🇫🇮":"flag-finland","🇫🇯":"flag-fiji","🇫🇰":"flag-falkland-islands","🇫🇲":"flag-micronesia","🇫🇴":"flag-faroe-islands","🇫🇷":"flag-france","🇬🇦":"flag-gabon","🇬🇧":"flag-united-kingdom","🇬🇩":"flag-grenada","🇬🇪":"flag-georgia","🇬🇫":"flag-french-guiana","🇬🇬":"flag-guernsey","🇬🇭":"flag-ghana","🇬🇮":"flag-gibraltar","🇬🇱":"flag-greenland","🇬🇲":"flag-gambia","🇬🇳":"flag-guinea","🇬🇵":"flag-guadeloupe","🇬🇶":"flag-equatorial-guinea","🇬🇷":"flag-greece","🇬🇸":"flag-south-georgia-&-south-sandwich-islands","🇬🇹":"flag-guatemala","🇬🇺":"flag-guam","🇬🇼":"flag-guinea-bissau","🇬🇾":"flag-guyana","🇭🇰":"flag-hong-kong-sar-china","🇭🇲":"flag-heard-&-mcdonald-islands","🇭🇳":"flag-honduras","🇭🇷":"flag-croatia","🇭🇹":"flag-haiti","🇭🇺":"flag-hungary","🇮🇨":"flag-canary-islands","🇮🇩":"flag-indonesia","🇮🇪":"flag-ireland","🇮🇱":"flag-israel","🇮🇲":"flag-isle-of-man","🇮🇳":"flag-india","🇮🇴":"flag-british-indian-ocean-territory","🇮🇶":"flag-iraq","🇮🇷":"flag-iran","🇮🇸":"flag-iceland","🇮🇹":"flag-italy","🇯🇪":"flag-jersey","🇯🇲":"flag-jamaica","🇯🇴":"flag-jordan","🇯🇵":"flag-japan","🇰🇪":"flag-kenya","🇰🇬":"flag-kyrgyzstan","🇰🇭":"flag-cambodia","🇰🇮":"flag-kiribati","🇰🇲":"flag-comoros","🇰🇳":"flag-st-kitts-&-nevis","🇰🇵":"flag-north-korea","🇰🇷":"flag-south-korea","🇰🇼":"flag-kuwait","🇰🇾":"flag-cayman-islands","🇰🇿":"flag-kazakhstan","🇱🇦":"flag-laos","🇱🇧":"flag-lebanon","🇱🇨":"flag-st-lucia","🇱🇮":"flag-liechtenstein","🇱🇰":"flag-sri-lanka","🇱🇷":"flag-liberia","🇱🇸":"flag-lesotho","🇱🇹":"flag-lithuania","🇱🇺":"flag-luxembourg","🇱🇻":"flag-latvia","🇱🇾":"flag-libya","🇲🇦":"flag-morocco","🇲🇨":"flag-monaco","🇲🇩":"flag-moldova","🇲🇪":"flag-montenegro","🇲🇫":"flag-st-martin","🇲🇬":"flag-madagascar","🇲🇭":"flag-marshall-islands","🇲🇰":"flag-north-macedonia","🇲🇱":"flag-mali","🇲🇲":"flag-myanmar-burma","🇲🇳":"flag-mongolia","🇲🇴":"flag-macao-sar-china","🇲🇵":"flag-northern-mariana-islands","🇲🇶":"flag-martinique","🇲🇷":"flag-mauritania","🇲🇸":"flag-montserrat","🇲🇹":"flag-malta","🇲🇺":"flag-mauritius","🇲🇻":"flag-maldives","🇲🇼":"flag-malawi","🇲🇽":"flag-mexico","🇲🇾":"flag-malaysia","🇲🇿":"flag-mozambique","🇳🇦":"flag-namibia","🇳🇨":"flag-new-caledonia","🇳🇪":"flag-niger","🇳🇫":"flag-norfolk-island","🇳🇬":"flag-nigeria","🇳🇮":"flag-nicaragua","🇳🇱":"flag-netherlands","🇳🇴":"flag-norway","🇳🇵":"flag-nepal","🇳🇷":"flag-nauru","🇳🇺":"flag-niue","🇳🇿":"flag-new-zealand","🇴🇲":"flag-oman","🇵🇦":"flag-panama","🇵🇪":"flag-peru","🇵🇫":"flag-french-polynesia","🇵🇬":"flag-papua-new-guinea","🇵🇭":"flag-philippines","🇵🇰":"flag-pakistan","🇵🇱":"flag-poland","🇵🇲":"flag-st-pierre-&-miquelon","🇵🇳":"flag-pitcairn-islands","🇵🇷":"flag-puerto-rico","🇵🇸":"flag-palestinian-territories","🇵🇹":"flag-portugal","🇵🇼":"flag-palau","🇵🇾":"flag-paraguay","🇶🇦":"flag-qatar","🇷🇪":"flag-reunion","🇷🇴":"flag-romania","🇷🇸":"flag-serbia","🇷🇺":"flag-russia","🇷🇼":"flag-rwanda","🇸🇦":"flag-saudi-arabia","🇸🇧":"flag-solomon-islands","🇸🇨":"flag-seychelles","🇸🇩":"flag-sudan","🇸🇪":"flag-sweden","🇸🇬":"flag-singapore","🇸🇭":"flag-st-helena","🇸🇮":"flag-slovenia","🇸🇯":"flag-svalbard-&-jan-mayen","🇸🇰":"flag-slovakia","🇸🇱":"flag-sierra-leone","🇸🇲":"flag-san-marino","🇸🇳":"flag-senegal","🇸🇴":"flag-somalia","🇸🇷":"flag-suriname","🇸🇸":"flag-south-sudan","🇸🇹":"flag-sao-tome-&-principe","🇸🇻":"flag-el-salvador","🇸🇽":"flag-sint-maarten","🇸🇾":"flag-syria","🇸🇿":"flag-eswatini","🇹🇦":"flag-tristan-da-cunha","🇹🇨":"flag-turks-&-caicos-islands","🇹🇩":"flag-chad","🇹🇫":"flag-french-southern-territories","🇹🇬":"flag-togo","🇹🇭":"flag-thailand","🇹🇯":"flag-tajikistan","🇹🇰":"flag-tokelau","🇹🇱":"flag-timor-leste","🇹🇲":"flag-turkmenistan","🇹🇳":"flag-tunisia","🇹🇴":"flag-tonga","🇹🇷":"flag-turkey","🇹🇹":"flag-trinidad-&-tobago","🇹🇻":"flag-tuvalu","🇹🇼":"flag-taiwan","🇹🇿":"flag-tanzania","🇺🇦":"flag-ukraine","🇺🇬":"flag-uganda","🇺🇲":"flag-us-outlying-islands","🇺🇳":"flag-united-nations","🇺🇸":"flag-united-states","🇺🇾":"flag-uruguay","🇺🇿":"flag-uzbekistan","🇻🇦":"flag-vatican-city","🇻🇨":"flag-st-vincent-&-grenadines","🇻🇪":"flag-venezuela","🇻🇬":"flag-british-virgin-islands","🇻🇮":"flag-us-virgin-islands","🇻🇳":"flag-vietnam","🇻🇺":"flag-vanuatu","🇼🇫":"flag-wallis-&-futuna","🇼🇸":"flag-samoa","🇽🇰":"flag-kosovo","🇾🇪":"flag-yemen","🇾🇹":"flag-mayotte","🇿🇦":"flag-south-africa","🇿🇲":"flag-zambia","🇿🇼":"flag-zimbabwe","🏴󠁧󠁢󠁥󠁮󠁧󠁿":"flag-england","🏴󠁧󠁢󠁳󠁣󠁴󠁿":"flag-scotland","🏴󠁧󠁢󠁷󠁬󠁳󠁿":"flag-wales"}
},{}],126:[function(require,module,exports){
module.exports={"10":["🔟"],"grin":["😀","😃","😄","😆","😅","😺","😸"],"face":["😀","😃","😄","😁","😆","😅","😂","🙂","🙃","🫠","😉","😊","😇","🥰","😍","😘","😗","☺️","😚","😙","🥲","😋","😛","😜","🤪","😝","🤑","🤗","🤭","🫢","🫣","🤫","🤔","🫡","🤐","🤨","😐","😑","😶","🫥","😶‍🌫️","😏","😒","🙄","😬","😮‍💨","🤥","🫨","😌","😔","😪","🤤","😴","😷","🤒","🤕","🤢","🤮","🤧","🥵","🥶","🥴","😵","😵‍💫","🤠","🥳","🥸","😎","🤓","🧐","😕","🫤","😟","🙁","☹️","😮","😯","😲","😳","🥺","🥹","😦","😧","😨","😰","😥","😢","😭","😱","😖","😣","😞","😓","😩","😫","🥱","😤","😡","😠","🤬","😈","👿","🤡","🤛","🤜","🐵","🐶","🐱","🐯","🐴","🐮","🐷","🐭","🐰","🐥","🐲","🌚","🌛","🌜","🌝","🌞","🌬️","📄"],"big":["😃"],"eyes":["😃","😄","😁","😊","😍","😚","😙","🫢","🙄","😵","😵‍💫","😸","😻","👀"],"smile":["😄","😁","🙂","😊","😇","🥰","😍","☺️","😙","🥲","🤗","😎","😈","😸","😻","😼"],"beam":["😁"],"squint":["😆","😝"],"sweat":["😅","😰","😓","💦"],"roll":["🤣","🙄","🧻"],"on":["🤣","🤬","❤️‍🔥","🍖","⛱️","🔛"],"the":["🤣","🤘","🫵"],"floor":["🤣"],"laugh":["🤣"],"tears":["😂","🥹","😹"],"joy":["😂","😹"],"slightly":["🙂","🙁"],"upside":["🙃"],"down":["🙃","🫳","👇","👎","↘️","⬇️","↙️","↕️","⤵️","⏬","🔻"],"melt":["🫠"],"wink":["😉","😜"],"halo":["😇"],"hearts":["🥰","💞","💕"],"heart":["😍","😻","💘","💝","💖","💗","💓","💟","❣️","💔","❤️‍🔥","❤️‍🩹","❤️","🩷","🧡","💛","💚","💙","🩵","💜","🤎","🖤","🩶","🤍","🫶","🫀","💑","👩‍❤️‍👨","👨‍❤️‍👨","👩‍❤️‍👩","♥️"],"star":["🤩","⭐","🌟","🌠","✡️","☪️","🔯","✴️"],"struck":["🤩"],"blow":["😘"],"kiss":["😘","😗","😚","😙","😽","💋","💏","👩‍❤️‍💋‍👨","👨‍❤️‍💋‍👨","👩‍❤️‍💋‍👩"],"closed":["😚","🌂","📕","📫","📪"],"tear":["🥲","📆"],"savore":["😋"],"food":["😋","🥘","🍲","🥫"],"tongue":["😛","😜","😝","👅"],"zany":["🤪"],"money":["🤑","💰","💸"],"mouth":["🤑","🤭","🫢","🤐","😶","🫤","😮","😦","🤬","👄"],"open":["🤗","🫢","😮","😦","👐","📖","📬","📭","📂","🈺"],"hands":["🤗","👏","🙌","🫶","👐","🙏","🧑‍🤝‍🧑","👭","👫","👬"],"hand":["🤭","🫢","👋","🤚","🖐️","✋","🫱","🫲","🫳","🫴","🫷","🫸","👌","🤏","✌️","🫰","🤙","✍️","💁","💁‍♂️","💁‍♀️","🙋","🙋‍♂️","🙋‍♀️","🪭"],"over":["🤭","🫢","🌄"],"and":["🫢","☠️","🫰","👫","🍽️","🍴","⛈️","⚒️","🛠️","🏹","🔩","🛋️","☪️"],"peek":["🫣"],"eye":["🫣","👁️‍🗨️","👁️"],"shush":["🤫"],"think":["🤔"],"salute":["🫡","🖖"],"zipper":["🤐"],"raised":["🤨","🤚","✋","✊","📫","📬"],"eyebrow":["🤨"],"neutral":["😐"],"expressionless":["😑"],"without":["😶","🍵","⛄"],"dotted":["🫥","🔯"],"line":["🫥"],"in":["😶‍🌫️","😱","👁️‍🗨️","🤵","🤵‍♂️","🤵‍♀️","🧑‍🦼","👨‍🦼","👩‍🦼","🧑‍🦽","👨‍🦽","👩‍🦽","🕴️","🧖","🧖‍♂️","🧖‍♀️","🧘","🧘‍♂️","🧘‍♀️","🛌","👤","👥","🍃","⛳","🚮"],"clouds":["😶‍🌫️"],"smirk":["😏"],"unamused":["😒"],"grimace":["😬"],"exhale":["😮‍💨"],"lying":["🤥"],"shake":["🫨"],"relieved":["😌","😥"],"pensive":["😔"],"sleepy":["😪"],"drool":["🤤"],"sleep":["😴"],"medical":["😷","⚕️"],"mask":["😷","🤿"],"thermometer":["🤒","🌡️"],"head":["🤕","🤯","🗣️"],"bandage":["🤕","🩹"],"nauseated":["🤢"],"vomite":["🤮"],"sneez":["🤧"],"hot":["🥵","🌶️","🌭","☕","♨️"],"cold":["🥶"],"woozy":["🥴"],"crossed":["😵","🤞","🫰","⚔️","🎌"],"out":["😵"],"spiral":["😵‍💫","🐚","🗒️","🗓️"],"explode":["🤯"],"cowboy":["🤠"],"hat":["🤠","👒","🎩"],"party":["🥳","🎉"],"disguised":["🥸"],"sunglasses":["😎","🕶️"],"nerd":["🤓"],"monocle":["🧐"],"confused":["😕"],"diagonal":["🫤"],"worried":["😟"],"frown":["🙁","☹️","😦","🙍","🙍‍♂️","🙍‍♀️"],"hushed":["😯"],"astonished":["😲"],"flushed":["😳"],"plead":["🥺"],"hold":["🥹","🧑‍🤝‍🧑","👭","👫","👬"],"back":["🥹","🤚","🔙"],"anguished":["😧"],"fearful":["😨"],"anxious":["😰"],"sad":["😥"],"but":["😥"],"cry":["😢","😭","😿"],"loudly":["😭"],"scream":["😱"],"fear":["😱"],"confounded":["😖"],"persever":["😣"],"disappointed":["😞"],"downcast":["😓"],"weary":["😩","🙀"],"tired":["😫"],"yawn":["🥱"],"steam":["😤","🍜"],"from":["😤"],"nose":["😤","👃","🐽"],"enraged":["😡"],"angry":["😠","👿"],"symbols":["🤬","🔣"],"horns":["😈","👿","🤘"],"skull":["💀","☠️"],"crossbones":["☠️"],"pile":["💩"],"poo":["💩"],"clown":["🤡"],"ogre":["👹"],"goblin":["👺"],"ghost":["👻"],"alien":["👽","👾"],"monster":["👾"],"robot":["🤖"],"cat":["😺","😸","😹","😻","😼","😽","🙀","😿","😾","🐱","🐈","🐈‍⬛"],"wry":["😼"],"pout":["😾","🙎","🙎‍♂️","🙎‍♀️"],"see":["🙈"],"no":["🙈","🙉","🙊","🙅","🙅‍♂️","🙅‍♀️","⛔","🚳","🚭","🚯","🚷","📵","🔞","🈵"],"evil":["🙈","🙉","🙊"],"monkey":["🙈","🙉","🙊","🐵","🐒"],"hear":["🙉","🦻"],"speak":["🙊","🗣️"],"love":["💌","🤟","🏩"],"letter":["💌"],"arrow":["💘","📲","📩","🏹","⬆️","↗️","➡️","↘️","⬇️","↙️","⬅️","↖️","↕️","↔️","↩️","↪️","⤴️","⤵️","🔙","🔚","🔛","🔜","🔝"],"ribbon":["💝","🎀","🎗️"],"sparkle":["💖","❇️"],"grow":["💗"],"beat":["💓"],"revolve":["💞"],"two":["💕","🐫","🕑","🕝"],"decoration":["💟","🎍"],"exclamation":["❣️","‼️","⁉️","❕","❗"],"broken":["💔"],"fire":["❤️‍🔥","🚒","🔥","🧯"],"mend":["❤️‍🩹"],"red":["❤️","👨‍🦰","👩‍🦰","🧑‍🦰","🍎","🧧","🀄","🏮","❓","❗","⭕","🔴","🟥","🔺","🔻"],"pink":["🩷"],"orange":["🧡","📙","🟠","🟧","🔶","🔸"],"yellow":["💛","🟡","🟨"],"green":["💚","🍏","🥬","🥗","📗","🟢","🟩"],"blue":["💙","🩵","📘","🔵","🟦","🔷","🔹"],"light":["🩵","🚈","🚨","🚥","🚦","💡"],"purple":["💜","🟣","🟪"],"brown":["🤎","🟤","🟫"],"black":["🖤","🐈‍⬛","🐦‍⬛","✒️","⚫","⬛","◼️","◾","▪️","🔲","🏴"],"grey":["🩶"],"white":["🤍","👨‍🦳","👩‍🦳","🧑‍🦳","🧑‍🦯","👨‍🦯","👩‍🦯","💮","🦯","❔","❕","⚪","⬜","◻️","◽","▫️","🔳","🏳️"],"mark":["💋","‼️","⁉️","❓","❔","❕","❗","✅","✔️","❌","❎","〽️","™️"],"hundred":["💯"],"points":["💯"],"anger":["💢","🗯️"],"symbol":["💢","♿","🚼","⚛️","☮️","⚧️","⚕️","♻️","🔰"],"collision":["💥"],"dizzy":["💫"],"droplets":["💦"],"dash":["💨","〰️"],"away":["💨"],"hole":["🕳️","⛳"],"speech":["💬","👁️‍🗨️","🗨️"],"balloon":["💬","💭","🎈"],"bubble":["👁️‍🗨️","🗨️","🗯️","🧋"],"left":["🗨️","👈","🤛","🔍","🛅","↙️","⬅️","↖️","↔️","↩️","↪️"],"right":["🗯️","👉","🤜","🔎","↗️","➡️","↘️","↔️","↩️","↪️","⤴️","⤵️"],"thought":["💭"],"zzz":["💤"],"wave":["👋","🌊"],"fingers":["🖐️","🤌","🤞"],"splayed":["🖐️"],"vulcan":["🖖"],"rightwards":["🫱","🫸"],"leftwards":["🫲","🫷"],"palm":["🫳","🫴","🌴"],"up":["🫴","👆","☝️","👍","🤲","📄","🗞️","⬆️","↗️","↖️","↕️","⤴️","⏫","🆙","🔺"],"push":["🫷","🫸"],"ok":["👌","🙆","🙆‍♂️","🙆‍♀️","🆗"],"pinched":["🤌"],"pinch":["🤏"],"victory":["✌️"],"index":["🫰","👈","👉","👆","👇","☝️","🫵","🗂️","📇"],"finger":["🫰","🖕"],"thumb":["🫰"],"you":["🤟"],"gesture":["🤟","🙅","🙅‍♂️","🙅‍♀️","🙆","🙆‍♂️","🙆‍♀️"],"sign":["🤘","🛑","🏧","🚮","♀️","♂️","🟰","💲"],"call":["🤙"],"me":["🤙"],"backhand":["👈","👉","👆","👇"],"point":["👈","👉","👆","👇","☝️","🫵"],"middle":["🖕"],"at":["🫵","🌆","🌉"],"viewer":["🫵"],"thumbs":["👍","👎"],"fist":["✊","👊","🤛","🤜"],"oncome":["👊","🚍","🚔","🚖","🚘"],"clap":["👏"],"raise":["🙌","🙋","🙋‍♂️","🙋‍♀️"],"palms":["🤲"],"together":["🤲"],"handshake":["🤝"],"folded":["🙏"],"write":["✍️"],"nail":["💅"],"polish":["💅"],"selfie":["🤳"],"flexed":["💪"],"biceps":["💪"],"mechanical":["🦾","🦿"],"arm":["🦾"],"leg":["🦿","🦵","🍗"],"foot":["🦶"],"ear":["👂","🦻","🌽"],"aid":["🦻"],"brain":["🧠"],"anatomical":["🫀"],"lungs":["🫁"],"tooth":["🦷"],"bone":["🦴","🍖"],"bite":["🫦"],"lip":["🫦"],"baby":["👶","👩‍🍼","👨‍🍼","🧑‍🍼","👼","🐤","🐥","🍼","🚼"],"child":["🧒"],"boy":["👦","👨‍👩‍👦","👨‍👩‍👧‍👦","👨‍👩‍👦‍👦","👨‍👨‍👦","👨‍👨‍👧‍👦","👨‍👨‍👦‍👦","👩‍👩‍👦","👩‍👩‍👧‍👦","👩‍👩‍👦‍👦","👨‍👦","👨‍👦‍👦","👨‍👧‍👦","👩‍👦","👩‍👦‍👦","👩‍👧‍👦"],"girl":["👧","👨‍👩‍👧","👨‍👩‍👧‍👦","👨‍👩‍👧‍👧","👨‍👨‍👧","👨‍👨‍👧‍👦","👨‍👨‍👧‍👧","👩‍👩‍👧","👩‍👩‍👧‍👦","👩‍👩‍👧‍👧","👨‍👧","👨‍👧‍👦","👨‍👧‍👧","👩‍👧","👩‍👧‍👦","👩‍👧‍👧"],"person":["🧑","👱","🧔","🧑‍🦰","🧑‍🦱","🧑‍🦳","🧑‍🦲","🧓","🙍","🙎","🙅","🙆","💁","🙋","🧏","🙇","🤦","🤷","🫅","👳","👲","🤵","👰","🫄","🧑‍🍼","💆","💇","🚶","🧍","🧎","🧑‍🦯","🧑‍🦼","🧑‍🦽","🏃","🕴️","🧖","🧗","🤺","🏌️","🏄","🚣","🏊","⛹️","🏋️","🚴","🚵","🤸","🤽","🤾","🤹","🧘","🛀","🛌"],"blond":["👱","👱‍♀️","👱‍♂️"],"hair":["👱","👨‍🦰","👨‍🦱","👨‍🦳","👩‍🦰","🧑‍🦰","👩‍🦱","🧑‍🦱","👩‍🦳","🧑‍🦳","👱‍♀️","👱‍♂️","🪮"],"man":["👨","🧔‍♂️","👨‍🦰","👨‍🦱","👨‍🦳","👨‍🦲","👱‍♂️","👴","🙍‍♂️","🙎‍♂️","🙅‍♂️","🙆‍♂️","💁‍♂️","🙋‍♂️","🧏‍♂️","🙇‍♂️","🤦‍♂️","🤷‍♂️","👨‍⚕️","👨‍🎓","👨‍🏫","👨‍⚖️","👨‍🌾","👨‍🍳","👨‍🔧","👨‍🏭","👨‍💼","👨‍🔬","👨‍💻","👨‍🎤","👨‍🎨","👨‍✈️","👨‍🚀","👨‍🚒","👮‍♂️","🕵️‍♂️","💂‍♂️","👷‍♂️","👳‍♂️","🤵‍♂️","👰‍♂️","🫃","👨‍🍼","🦸‍♂️","🦹‍♂️","🧙‍♂️","🧚‍♂️","🧛‍♂️","🧝‍♂️","🧞‍♂️","🧟‍♂️","💆‍♂️","💇‍♂️","🚶‍♂️","🧍‍♂️","🧎‍♂️","👨‍🦯","👨‍🦼","👨‍🦽","🏃‍♂️","🕺","🧖‍♂️","🧗‍♂️","🏌️‍♂️","🏄‍♂️","🚣‍♂️","🏊‍♂️","⛹️‍♂️","🏋️‍♂️","🚴‍♂️","🚵‍♂️","🤸‍♂️","🤽‍♂️","🤾‍♂️","🤹‍♂️","🧘‍♂️","👫","👩‍❤️‍💋‍👨","👨‍❤️‍💋‍👨","👩‍❤️‍👨","👨‍❤️‍👨","👨‍👩‍👦","👨‍👩‍👧","👨‍👩‍👧‍👦","👨‍👩‍👦‍👦","👨‍👩‍👧‍👧","👨‍👨‍👦","👨‍👨‍👧","👨‍👨‍👧‍👦","👨‍👨‍👦‍👦","👨‍👨‍👧‍👧","👨‍👦","👨‍👦‍👦","👨‍👧","👨‍👧‍👦","👨‍👧‍👧","👞","🇮🇲"],"beard":["🧔","🧔‍♂️","🧔‍♀️"],"woman":["🧔‍♀️","👩","👩‍🦰","👩‍🦱","👩‍🦳","👩‍🦲","👱‍♀️","👵","🙍‍♀️","🙎‍♀️","🙅‍♀️","🙆‍♀️","💁‍♀️","🙋‍♀️","🧏‍♀️","🙇‍♀️","🤦‍♀️","🤷‍♀️","👩‍⚕️","👩‍🎓","👩‍🏫","👩‍⚖️","👩‍🌾","👩‍🍳","👩‍🔧","👩‍🏭","👩‍💼","👩‍🔬","👩‍💻","👩‍🎤","👩‍🎨","👩‍✈️","👩‍🚀","👩‍🚒","👮‍♀️","🕵️‍♀️","💂‍♀️","👷‍♀️","👳‍♀️","🧕","🤵‍♀️","👰‍♀️","🤰","👩‍🍼","🦸‍♀️","🦹‍♀️","🧙‍♀️","🧚‍♀️","🧛‍♀️","🧝‍♀️","🧞‍♀️","🧟‍♀️","💆‍♀️","💇‍♀️","🚶‍♀️","🧍‍♀️","🧎‍♀️","👩‍🦯","👩‍🦼","👩‍🦽","🏃‍♀️","💃","🧖‍♀️","🧗‍♀️","🏌️‍♀️","🏄‍♀️","🚣‍♀️","🏊‍♀️","⛹️‍♀️","🏋️‍♀️","🚴‍♀️","🚵‍♀️","🤸‍♀️","🤽‍♀️","🤾‍♀️","🤹‍♀️","🧘‍♀️","👫","👩‍❤️‍💋‍👨","👩‍❤️‍💋‍👩","👩‍❤️‍👨","👩‍❤️‍👩","👨‍👩‍👦","👨‍👩‍👧","👨‍👩‍👧‍👦","👨‍👩‍👦‍👦","👨‍👩‍👧‍👧","👩‍👩‍👦","👩‍👩‍👧","👩‍👩‍👧‍👦","👩‍👩‍👦‍👦","👩‍👩‍👧‍👧","👩‍👦","👩‍👦‍👦","👩‍👧","👩‍👧‍👦","👩‍👧‍👧","👚","👡","👢","👒"],"curly":["👨‍🦱","👩‍🦱","🧑‍🦱","➰","➿"],"bald":["👨‍🦲","👩‍🦲","🧑‍🦲"],"older":["🧓"],"old":["👴","👵","🗝️"],"tip":["💁","💁‍♂️","💁‍♀️"],"deaf":["🧏","🧏‍♂️","🧏‍♀️"],"bow":["🙇","🙇‍♂️","🙇‍♀️","🏹"],"facepalm":["🤦","🤦‍♂️","🤦‍♀️"],"shrug":["🤷","🤷‍♂️","🤷‍♀️"],"health":["🧑‍⚕️","👨‍⚕️","👩‍⚕️"],"worker":["🧑‍⚕️","👨‍⚕️","👩‍⚕️","🧑‍🏭","👨‍🏭","👩‍🏭","🧑‍💼","👨‍💼","👩‍💼","👷","👷‍♂️","👷‍♀️","⛑️"],"student":["🧑‍🎓","👨‍🎓","👩‍🎓"],"teacher":["🧑‍🏫","👨‍🏫","👩‍🏫"],"judge":["🧑‍⚖️","👨‍⚖️","👩‍⚖️"],"farmer":["🧑‍🌾","👨‍🌾","👩‍🌾"],"cook":["🧑‍🍳","👨‍🍳","👩‍🍳","🍳","🇨🇰"],"mechanic":["🧑‍🔧","👨‍🔧","👩‍🔧"],"factory":["🧑‍🏭","👨‍🏭","👩‍🏭","🏭"],"office":["🧑‍💼","👨‍💼","👩‍💼","🏢","🏣","🏤"],"scientist":["🧑‍🔬","👨‍🔬","👩‍🔬"],"technologist":["🧑‍💻","👨‍💻","👩‍💻"],"singer":["🧑‍🎤","👨‍🎤","👩‍🎤"],"artist":["🧑‍🎨","👨‍🎨","👩‍🎨","🎨"],"pilot":["🧑‍✈️","👨‍✈️","👩‍✈️"],"astronaut":["🧑‍🚀","👨‍🚀","👩‍🚀"],"firefighter":["🧑‍🚒","👨‍🚒","👩‍🚒"],"police":["👮","👮‍♂️","👮‍♀️","🚓","🚔","🚨"],"officer":["👮","👮‍♂️","👮‍♀️"],"detective":["🕵️","🕵️‍♂️","🕵️‍♀️"],"guard":["💂","💂‍♂️","💂‍♀️"],"ninja":["🥷"],"construction":["👷","👷‍♂️","👷‍♀️","🏗️","🚧"],"crown":["🫅","👑"],"prince":["🤴"],"princess":["👸"],"wear":["👳","👳‍♂️","👳‍♀️"],"turban":["👳","👳‍♂️","👳‍♀️"],"skullcap":["👲"],"headscarf":["🧕"],"tuxedo":["🤵","🤵‍♂️","🤵‍♀️"],"veil":["👰","👰‍♂️","👰‍♀️"],"pregnant":["🤰","🫃","🫄"],"breast":["🤱"],"feed":["🤱","👩‍🍼","👨‍🍼","🧑‍🍼"],"angel":["👼"],"santa":["🎅"],"claus":["🎅","🤶","🧑‍🎄"],"mrs":["🤶"],"mx":["🧑‍🎄"],"superhero":["🦸","🦸‍♂️","🦸‍♀️"],"supervillain":["🦹","🦹‍♂️","🦹‍♀️"],"mage":["🧙","🧙‍♂️","🧙‍♀️"],"fairy":["🧚","🧚‍♂️","🧚‍♀️"],"vampire":["🧛","🧛‍♂️","🧛‍♀️"],"merperson":["🧜"],"merman":["🧜‍♂️"],"mermaid":["🧜‍♀️"],"elf":["🧝","🧝‍♂️","🧝‍♀️"],"genie":["🧞","🧞‍♂️","🧞‍♀️"],"zombie":["🧟","🧟‍♂️","🧟‍♀️"],"troll":["🧌"],"gett":["💆","💆‍♂️","💆‍♀️","💇","💇‍♂️","💇‍♀️"],"massage":["💆","💆‍♂️","💆‍♀️"],"haircut":["💇","💇‍♂️","💇‍♀️"],"walk":["🚶","🚶‍♂️","🚶‍♀️"],"stand":["🧍","🧍‍♂️","🧍‍♀️"],"kneel":["🧎","🧎‍♂️","🧎‍♀️"],"cane":["🧑‍🦯","👨‍🦯","👩‍🦯","🦯"],"motorized":["🧑‍🦼","👨‍🦼","👩‍🦼","🦼"],"wheelchair":["🧑‍🦼","👨‍🦼","👩‍🦼","🧑‍🦽","👨‍🦽","👩‍🦽","🦽","🦼","♿"],"manual":["🧑‍🦽","👨‍🦽","👩‍🦽","🦽"],"run":["🏃","🏃‍♂️","🏃‍♀️","🎽","👟"],"dance":["💃","🕺"],"suit":["🕴️","♠️","♥️","♦️","♣️"],"levitate":["🕴️"],"people":["👯","🤼","🧑‍🤝‍🧑","🫂"],"bunny":["👯","👯‍♂️","👯‍♀️"],"ears":["👯","👯‍♂️","👯‍♀️"],"men":["👯‍♂️","🤼‍♂️","👬","🚹"],"women":["👯‍♀️","🤼‍♀️","👭","🚺"],"steamy":["🧖","🧖‍♂️","🧖‍♀️"],"room":["🧖","🧖‍♂️","🧖‍♀️","🚹","🚺"],"climb":["🧗","🧗‍♂️","🧗‍♀️"],"fence":["🤺"],"horse":["🏇","🐴","🐎","🎠"],"race":["🏇","🏎️"],"skier":["⛷️"],"snowboarder":["🏂"],"golf":["🏌️","🏌️‍♂️","🏌️‍♀️"],"surf":["🏄","🏄‍♂️","🏄‍♀️"],"row":["🚣","🚣‍♂️","🚣‍♀️"],"boat":["🚣","🚣‍♂️","🚣‍♀️","🛥️"],"swim":["🏊","🏊‍♂️","🏊‍♀️"],"bounce":["⛹️","⛹️‍♂️","⛹️‍♀️"],"ball":["⛹️","⛹️‍♂️","⛹️‍♀️","🍙","🎊","⚽","🎱","🔮","🪩"],"lift":["🏋️","🏋️‍♂️","🏋️‍♀️"],"weights":["🏋️","🏋️‍♂️","🏋️‍♀️"],"bike":["🚴","🚴‍♂️","🚴‍♀️","🚵","🚵‍♂️","🚵‍♀️"],"mountain":["🚵","🚵‍♂️","🚵‍♀️","🏔️","⛰️","🚞","🚠"],"cartwheel":["🤸","🤸‍♂️","🤸‍♀️"],"wrestle":["🤼","🤼‍♂️","🤼‍♀️"],"play":["🤽","🤽‍♂️","🤽‍♀️","🤾","🤾‍♂️","🤾‍♀️","🎴","▶️","⏯️"],"water":["🤽","🤽‍♂️","🤽‍♀️","🐃","🌊","🔫","🚰","🚾","🚱"],"polo":["🤽","🤽‍♂️","🤽‍♀️"],"handball":["🤾","🤾‍♂️","🤾‍♀️"],"juggle":["🤹","🤹‍♂️","🤹‍♀️"],"lotus":["🧘","🧘‍♂️","🧘‍♀️","🪷"],"position":["🧘","🧘‍♂️","🧘‍♀️"],"take":["🛀"],"bath":["🛀"],"bed":["🛌","🛏️"],"couple":["💑","👩‍❤️‍👨","👨‍❤️‍👨","👩‍❤️‍👩"],"family":["👪","👨‍👩‍👦","👨‍👩‍👧","👨‍👩‍👧‍👦","👨‍👩‍👦‍👦","👨‍👩‍👧‍👧","👨‍👨‍👦","👨‍👨‍👧","👨‍👨‍👧‍👦","👨‍👨‍👦‍👦","👨‍👨‍👧‍👧","👩‍👩‍👦","👩‍👩‍👧","👩‍👩‍👧‍👦","👩‍👩‍👦‍👦","👩‍👩‍👧‍👧","👨‍👦","👨‍👦‍👦","👨‍👧","👨‍👧‍👦","👨‍👧‍👧","👩‍👦","👩‍👦‍👦","👩‍👧","👩‍👧‍👦","👩‍👧‍👧"],"bust":["👤"],"silhouette":["👤","👥"],"busts":["👥"],"hug":["🫂"],"footprints":["👣"],"gorilla":["🦍"],"orangutan":["🦧"],"dog":["🐶","🐕","🦮","🐕‍🦺","🌭"],"guide":["🦮"],"service":["🐕‍🦺","🈂️"],"poodle":["🐩"],"wolf":["🐺"],"fox":["🦊"],"raccoon":["🦝"],"lion":["🦁"],"tiger":["🐯","🐅"],"leopard":["🐆"],"moose":["🫎"],"donkey":["🫏"],"unicorn":["🦄"],"zebra":["🦓"],"deer":["🦌"],"bison":["🦬"],"cow":["🐮","🐄"],"ox":["🐂"],"buffalo":["🐃"],"pig":["🐷","🐖","🐽"],"boar":["🐗"],"ram":["🐏"],"ewe":["🐑"],"goat":["🐐"],"camel":["🐪","🐫"],"hump":["🐫"],"llama":["🦙"],"giraffe":["🦒"],"elephant":["🐘"],"mammoth":["🦣"],"rhinoceros":["🦏"],"hippopotamus":["🦛"],"mouse":["🐭","🐁","🖱️","🪤"],"rat":["🐀"],"hamster":["🐹"],"rabbit":["🐰","🐇"],"chipmunk":["🐿️"],"beaver":["🦫"],"hedgehog":["🦔"],"bat":["🦇"],"bear":["🐻","🐻‍❄️","🧸"],"polar":["🐻‍❄️"],"koala":["🐨"],"panda":["🐼"],"sloth":["🦥"],"otter":["🦦"],"skunk":["🦨"],"kangaroo":["🦘"],"badger":["🦡"],"paw":["🐾"],"prints":["🐾"],"turkey":["🦃","🇹🇷"],"chicken":["🐔"],"rooster":["🐓"],"hatch":["🐣"],"chick":["🐣","🐤","🐥"],"front":["🐥"],"bird":["🐦","🐦‍⬛"],"penguin":["🐧"],"dove":["🕊️"],"eagle":["🦅"],"duck":["🦆"],"swan":["🦢"],"owl":["🦉"],"dodo":["🦤"],"feather":["🪶"],"flamingo":["🦩"],"peacock":["🦚"],"parrot":["🦜"],"wing":["🪽"],"goose":["🪿"],"frog":["🐸"],"crocodile":["🐊"],"turtle":["🐢"],"lizard":["🦎"],"snake":["🐍"],"dragon":["🐲","🐉","🀄"],"sauropod":["🦕"],"rex":["🦖"],"spout":["🐳"],"whale":["🐳","🐋"],"dolphin":["🐬"],"seal":["🦭"],"fish":["🐟","🐠","🍥","🎣"],"tropical":["🐠","🍹"],"blowfish":["🐡"],"shark":["🦈"],"octopus":["🐙"],"shell":["🐚"],"coral":["🪸"],"jellyfish":["🪼"],"snail":["🐌"],"butterfly":["🦋"],"bug":["🐛"],"ant":["🐜"],"honeybee":["🐝"],"beetle":["🪲","🐞"],"lady":["🐞"],"cricket":["🦗","🏏"],"cockroach":["🪳"],"spider":["🕷️","🕸️"],"web":["🕸️"],"scorpion":["🦂"],"mosquito":["🦟"],"fly":["🪰","🛸","🥏"],"worm":["🪱"],"microbe":["🦠"],"bouquet":["💐"],"cherry":["🌸"],"blossom":["🌸","🌼"],"flower":["💮","🥀","🎴"],"rosette":["🏵️"],"rose":["🌹"],"wilted":["🥀"],"hibiscus":["🌺"],"sunflower":["🌻"],"tulip":["🌷"],"hyacinth":["🪻"],"seedle":["🌱"],"potted":["🪴"],"plant":["🪴"],"evergreen":["🌲"],"tree":["🌲","🌳","🌴","🎄","🎋"],"deciduous":["🌳"],"cactus":["🌵"],"sheaf":["🌾"],"rice":["🌾","🍘","🍙","🍚","🍛"],"herb":["🌿"],"shamrock":["☘️"],"four":["🍀","🕓","🕟"],"leaf":["🍀","🍁","🍂","🍃"],"clover":["🍀"],"maple":["🍁"],"fallen":["🍂"],"flutter":["🍃"],"wind":["🍃","🌬️","🎐"],"empty":["🪹"],"nest":["🪹","🪺","🪆"],"eggs":["🪺"],"mushroom":["🍄"],"grapes":["🍇"],"melon":["🍈"],"watermelon":["🍉"],"tangerine":["🍊"],"lemon":["🍋"],"banana":["🍌"],"pineapple":["🍍"],"mango":["🥭"],"apple":["🍎","🍏"],"pear":["🍐"],"peach":["🍑"],"cherries":["🍒"],"strawberry":["🍓"],"blueberries":["🫐"],"kiwi":["🥝"],"fruit":["🥝"],"tomato":["🍅"],"olive":["🫒"],"coconut":["🥥"],"avocado":["🥑"],"eggplant":["🍆"],"potato":["🥔","🍠"],"carrot":["🥕"],"corn":["🌽"],"pepper":["🌶️","🫑"],"bell":["🫑","🛎️","🔔","🔕"],"cucumber":["🥒"],"leafy":["🥬"],"broccoli":["🥦"],"garlic":["🧄"],"onion":["🧅"],"peanuts":["🥜"],"beans":["🫘"],"chestnut":["🌰"],"ginger":["🫚"],"root":["🫚"],"pea":["🫛"],"pod":["🫛"],"bread":["🍞","🥖"],"croissant":["🥐"],"baguette":["🥖"],"flatbread":["🫓","🥙"],"pretzel":["🥨"],"bagel":["🥯"],"pancakes":["🥞"],"waffle":["🧇"],"cheese":["🧀"],"wedge":["🧀"],"meat":["🍖","🥩"],"poultry":["🍗"],"cut":["🥩"],"bacon":["🥓"],"hamburger":["🍔"],"french":["🍟","🇬🇫","🇵🇫","🇹🇫"],"fries":["🍟"],"pizza":["🍕"],"sandwich":["🥪","🇬🇸"],"taco":["🌮"],"burrito":["🌯"],"tamale":["🫔"],"stuffed":["🥙"],"falafel":["🧆"],"egg":["🥚"],"shallow":["🥘"],"pan":["🥘"],"pot":["🍲","🍯"],"fondue":["🫕"],"bowl":["🥣","🍜","🎳"],"spoon":["🥣","🥄"],"salad":["🥗"],"popcorn":["🍿"],"butter":["🧈"],"salt":["🧂"],"canned":["🥫"],"bento":["🍱"],"box":["🍱","🥡","🧃","🥊","🗳️","🗃️","☑️"],"cracker":["🍘"],"cooked":["🍚"],"curry":["🍛"],"spaghetti":["🍝"],"roasted":["🍠"],"sweet":["🍠"],"oden":["🍢"],"sushi":["🍣"],"fried":["🍤"],"shrimp":["🍤","🦐"],"cake":["🍥","🥮","🎂"],"swirl":["🍥"],"moon":["🥮","🌑","🌒","🌓","🌔","🌕","🌖","🌗","🌘","🌙","🌚","🌛","🌜","🌝","🎑"],"dango":["🍡"],"dumple":["🥟"],"fortune":["🥠"],"cookie":["🥠","🍪"],"takeout":["🥡"],"crab":["🦀"],"lobster":["🦞"],"squid":["🦑"],"oyster":["🦪"],"soft":["🍦"],"ice":["🍦","🍧","🍨","🧊","🏒","⛸️"],"cream":["🍦","🍨"],"shaved":["🍧"],"doughnut":["🍩"],"birthday":["🎂"],"shortcake":["🍰"],"cupcake":["🧁"],"pie":["🥧"],"chocolate":["🍫"],"bar":["🍫","📊"],"candy":["🍬"],"lollipop":["🍭"],"custard":["🍮"],"honey":["🍯"],"bottle":["🍼","🍾","🧴"],"glass":["🥛","🍷","🍸","🥃","🔍","🔎"],"milk":["🥛"],"beverage":["☕","🧃"],"teapot":["🫖"],"teacup":["🍵"],"handle":["🍵"],"sake":["🍶"],"pop":["🍾"],"cork":["🍾"],"wine":["🍷"],"cocktail":["🍸"],"drink":["🍹"],"beer":["🍺","🍻"],"mug":["🍺"],"clink":["🍻","🥂"],"mugs":["🍻"],"glasses":["🥂","👓"],"tumbler":["🥃"],"pour":["🫗"],"liquid":["🫗"],"cup":["🥤"],"straw":["🥤"],"tea":["🧋"],"mate":["🧉"],"chopsticks":["🥢"],"fork":["🍽️","🍴"],"knife":["🍽️","🍴","🔪"],"plate":["🍽️"],"kitchen":["🔪"],"jar":["🫙"],"amphora":["🏺"],"globe":["🌍","🌎","🌏","🌐"],"show":["🌍","🌎","🌏"],"europe":["🌍"],"africa":["🌍","🇿🇦"],"americas":["🌎"],"asia":["🌏"],"australia":["🌏","🇦🇺"],"meridians":["🌐"],"world":["🗺️"],"map":["🗺️","🗾"],"japan":["🗾","🇯🇵"],"compass":["🧭"],"snow":["🏔️","🌨️","⛄"],"capped":["🏔️"],"volcano":["🌋"],"mount":["🗻"],"fuji":["🗻"],"camp":["🏕️"],"beach":["🏖️"],"umbrella":["🏖️","🌂","☂️","☔","⛱️"],"desert":["🏜️","🏝️"],"island":["🏝️","🇦🇨","🇧🇻","🇨🇵","🇨🇽","🇳🇫"],"national":["🏞️"],"park":["🏞️"],"stadium":["🏟️"],"classical":["🏛️"],"build":["🏛️","🏗️","🏢"],"brick":["🧱"],"rock":["🪨"],"wood":["🪵"],"hut":["🛖"],"houses":["🏘️"],"derelict":["🏚️"],"house":["🏚️","🏠","🏡"],"garden":["🏡"],"japanese":["🏣","🏯","🎎","🔰","🈁","🈂️","🈷️","🈶","🈯","🉐","🈹","🈚","🈲","🉑","🈸","🈴","🈳","㊗️","㊙️","🈺","🈵"],"post":["🏣","🏤"],"hospital":["🏥"],"bank":["🏦"],"hotel":["🏨","🏩"],"convenience":["🏪"],"store":["🏪","🏬"],"school":["🏫"],"department":["🏬"],"castle":["🏯","🏰"],"wed":["💒"],"tokyo":["🗼"],"tower":["🗼"],"statue":["🗽"],"liberty":["🗽"],"church":["⛪"],"mosque":["🕌"],"hindu":["🛕"],"temple":["🛕"],"synagogue":["🕍"],"shinto":["⛩️"],"shrine":["⛩️"],"kaaba":["🕋"],"fountain":["⛲","🖋️"],"tent":["⛺","🎪"],"foggy":["🌁"],"night":["🌃","🌉"],"stars":["🌃"],"cityscape":["🏙️","🌆"],"sunrise":["🌄","🌅"],"mountains":["🌄"],"dusk":["🌆"],"sunset":["🌇"],"bridge":["🌉"],"springs":["♨️"],"carousel":["🎠"],"playground":["🛝"],"slide":["🛝"],"ferris":["🎡"],"wheel":["🎡","🛞","☸️"],"roller":["🎢","🛼"],"coaster":["🎢"],"barber":["💈"],"pole":["💈","🎣"],"circus":["🎪"],"locomotive":["🚂"],"railway":["🚃","🚞","🛤️","🚟"],"car":["🚃","🚋","🚓","🚔","🏎️","🚨"],"high":["🚄","⚡","👠","🔊"],"speed":["🚄"],"train":["🚄","🚅","🚆"],"bullet":["🚅"],"metro":["🚇"],"rail":["🚈"],"station":["🚉"],"tram":["🚊","🚋"],"monorail":["🚝"],"bus":["🚌","🚍","🚏"],"trolleybus":["🚎"],"minibus":["🚐"],"ambulance":["🚑"],"engine":["🚒"],"taxi":["🚕","🚖"],"automobile":["🚗","🚘"],"sport":["🚙"],"utility":["🚙"],"vehicle":["🚙"],"pickup":["🛻"],"truck":["🛻","🚚"],"delivery":["🚚"],"articulated":["🚛"],"lorry":["🚛"],"tractor":["🚜"],"motorcycle":["🏍️"],"motor":["🛵","🛥️"],"scooter":["🛵","🛴"],"auto":["🛺"],"rickshaw":["🛺"],"bicycle":["🚲"],"kick":["🛴"],"skateboard":["🛹"],"skate":["🛼","⛸️"],"stop":["🚏","🛑","⏹️"],"motorway":["🛣️"],"track":["🛤️","⏭️","⏮️"],"oil":["🛢️"],"drum":["🛢️","🥁","🪘"],"fuel":["⛽"],"pump":["⛽"],"horizontal":["🚥"],"traffic":["🚥","🚦"],"vertical":["🚦","🔃"],"anchor":["⚓"],"ring":["🛟","💍"],"buoy":["🛟"],"sailboat":["⛵"],"canoe":["🛶"],"speedboat":["🚤"],"passenger":["🛳️"],"ship":["🛳️","🚢"],"ferry":["⛴️"],"airplane":["✈️","🛩️","🛫","🛬"],"small":["🛩️","🌤️","◾","◽","▪️","▫️","🔸","🔹"],"departure":["🛫"],"arrival":["🛬"],"parachute":["🪂"],"seat":["💺"],"helicopter":["🚁"],"suspension":["🚟"],"cableway":["🚠"],"aerial":["🚡"],"tramway":["🚡"],"satellite":["🛰️","📡"],"rocket":["🚀"],"saucer":["🛸"],"bellhop":["🛎️"],"luggage":["🧳","🛅"],"hourglass":["⌛","⏳"],"done":["⌛","⏳"],"not":["⏳","🈶"],"watch":["⌚"],"alarm":["⏰"],"clock":["⏰","⏲️","🕰️","🕛","🕐","🕑","🕒","🕓","🕔","🕕","🕖","🕗","🕘","🕙","🕚"],"stopwatch":["⏱️"],"timer":["⏲️"],"mantelpiece":["🕰️"],"twelve":["🕛","🕧"],"thirty":["🕧","🕜","🕝","🕞","🕟","🕠","🕡","🕢","🕣","🕤","🕥","🕦"],"one":["🕐","🕜","🩱","🔞"],"three":["🕒","🕞"],"five":["🕔","🕠"],"six":["🕕","🕡","🔯"],"seven":["🕖","🕢"],"eight":["🕗","🕣","✳️","✴️"],"nine":["🕘","🕤"],"ten":["🕙","🕥"],"eleven":["🕚","🕦"],"new":["🌑","🌚","🆕","🇳🇨","🇳🇿","🇵🇬"],"wax":["🌒","🌔"],"crescent":["🌒","🌘","🌙","☪️"],"first":["🌓","🌛"],"quarter":["🌓","🌗","🌛","🌜"],"gibbous":["🌔","🌖"],"full":["🌕","🌝"],"wane":["🌖","🌘"],"last":["🌗","🌜","⏮️"],"sun":["☀️","🌞","⛅","🌤️","🌥️","🌦️"],"ringed":["🪐"],"planet":["🪐"],"glow":["🌟"],"shoot":["🌠"],"milky":["🌌"],"way":["🌌"],"cloud":["☁️","⛅","⛈️","🌤️","🌥️","🌦️","🌧️","🌨️","🌩️"],"behind":["⛅","🌤️","🌥️","🌦️"],"lightning":["⛈️","🌩️"],"rain":["⛈️","🌦️","🌧️","☔"],"large":["🌥️","⬛","⬜","🔶","🔷"],"tornado":["🌪️"],"fog":["🌫️"],"cyclone":["🌀"],"rainbow":["🌈","🏳️‍🌈"],"drops":["☔"],"ground":["⛱️"],"voltage":["⚡"],"snowflake":["❄️"],"snowman":["☃️","⛄"],"comet":["☄️"],"droplet":["💧"],"jack":["🎃"],"lantern":["🎃","🏮"],"christmas":["🎄","🇨🇽"],"fireworks":["🎆"],"sparkler":["🎇"],"firecracker":["🧨"],"sparkles":["✨"],"popper":["🎉"],"confetti":["🎊"],"tanabata":["🎋"],"pine":["🎍"],"dolls":["🎎","🪆"],"carp":["🎏"],"streamer":["🎏"],"chime":["🎐"],"view":["🎑"],"ceremony":["🎑"],"envelope":["🧧","✉️","📨","📩"],"wrapped":["🎁"],"gift":["🎁"],"reminder":["🎗️"],"admission":["🎟️"],"tickets":["🎟️"],"ticket":["🎫"],"military":["🎖️","🪖"],"medal":["🎖️","🏅","🥇","🥈","🥉"],"trophy":["🏆"],"sports":["🏅"],"1st":["🥇"],"place":["🥇","🥈","🥉","🛐"],"2nd":["🥈"],"3rd":["🥉"],"soccer":["⚽"],"baseball":["⚾"],"softball":["🥎"],"basketball":["🏀"],"volleyball":["🏐"],"american":["🏈","🇦🇸"],"football":["🏈","🏉"],"rugby":["🏉"],"tennis":["🎾"],"disc":["🥏"],"game":["🏏","🎮","🎲"],"field":["🏑"],"hockey":["🏑","🏒"],"lacrosse":["🥍"],"ping":["🏓"],"pong":["🏓"],"badminton":["🏸"],"glove":["🥊"],"martial":["🥋"],"arts":["🥋","🎭"],"uniform":["🥋"],"goal":["🥅"],"net":["🥅"],"flag":["⛳","📫","📪","📬","📭","🏁","🚩","🏴","🏳️","🏳️‍🌈","🏳️‍⚧️","🏴‍☠️","🇦🇨","🇦🇩","🇦🇪","🇦🇫","🇦🇬","🇦🇮","🇦🇱","🇦🇲","🇦🇴","🇦🇶","🇦🇷","🇦🇸","🇦🇹","🇦🇺","🇦🇼","🇦🇽","🇦🇿","🇧🇦","🇧🇧","🇧🇩","🇧🇪","🇧🇫","🇧🇬","🇧🇭","🇧🇮","🇧🇯","🇧🇱","🇧🇲","🇧🇳","🇧🇴","🇧🇶","🇧🇷","🇧🇸","🇧🇹","🇧🇻","🇧🇼","🇧🇾","🇧🇿","🇨🇦","🇨🇨","🇨🇩","🇨🇫","🇨🇬","🇨🇭","🇨🇮","🇨🇰","🇨🇱","🇨🇲","🇨🇳","🇨🇴","🇨🇵","🇨🇷","🇨🇺","🇨🇻","🇨🇼","🇨🇽","🇨🇾","🇨🇿","🇩🇪","🇩🇬","🇩🇯","🇩🇰","🇩🇲","🇩🇴","🇩🇿","🇪🇦","🇪🇨","🇪🇪","🇪🇬","🇪🇭","🇪🇷","🇪🇸","🇪🇹","🇪🇺","🇫🇮","🇫🇯","🇫🇰","🇫🇲","🇫🇴","🇫🇷","🇬🇦","🇬🇧","🇬🇩","🇬🇪","🇬🇫","🇬🇬","🇬🇭","🇬🇮","🇬🇱","🇬🇲","🇬🇳","🇬🇵","🇬🇶","🇬🇷","🇬🇸","🇬🇹","🇬🇺","🇬🇼","🇬🇾","🇭🇰","🇭🇲","🇭🇳","🇭🇷","🇭🇹","🇭🇺","🇮🇨","🇮🇩","🇮🇪","🇮🇱","🇮🇲","🇮🇳","🇮🇴","🇮🇶","🇮🇷","🇮🇸","🇮🇹","🇯🇪","🇯🇲","🇯🇴","🇯🇵","🇰🇪","🇰🇬","🇰🇭","🇰🇮","🇰🇲","🇰🇳","🇰🇵","🇰🇷","🇰🇼","🇰🇾","🇰🇿","🇱🇦","🇱🇧","🇱🇨","🇱🇮","🇱🇰","🇱🇷","🇱🇸","🇱🇹","🇱🇺","🇱🇻","🇱🇾","🇲🇦","🇲🇨","🇲🇩","🇲🇪","🇲🇫","🇲🇬","🇲🇭","🇲🇰","🇲🇱","🇲🇲","🇲🇳","🇲🇴","🇲🇵","🇲🇶","🇲🇷","🇲🇸","🇲🇹","🇲🇺","🇲🇻","🇲🇼","🇲🇽","🇲🇾","🇲🇿","🇳🇦","🇳🇨","🇳🇪","🇳🇫","🇳🇬","🇳🇮","🇳🇱","🇳🇴","🇳🇵","🇳🇷","🇳🇺","🇳🇿","🇴🇲","🇵🇦","🇵🇪","🇵🇫","🇵🇬","🇵🇭","🇵🇰","🇵🇱","🇵🇲","🇵🇳","🇵🇷","🇵🇸","🇵🇹","🇵🇼","🇵🇾","🇶🇦","🇷🇪","🇷🇴","🇷🇸","🇷🇺","🇷🇼","🇸🇦","🇸🇧","🇸🇨","🇸🇩","🇸🇪","🇸🇬","🇸🇭","🇸🇮","🇸🇯","🇸🇰","🇸🇱","🇸🇲","🇸🇳","🇸🇴","🇸🇷","🇸🇸","🇸🇹","🇸🇻","🇸🇽","🇸🇾","🇸🇿","🇹🇦","🇹🇨","🇹🇩","🇹🇫","🇹🇬","🇹🇭","🇹🇯","🇹🇰","🇹🇱","🇹🇲","🇹🇳","🇹🇴","🇹🇷","🇹🇹","🇹🇻","🇹🇼","🇹🇿","🇺🇦","🇺🇬","🇺🇲","🇺🇳","🇺🇸","🇺🇾","🇺🇿","🇻🇦","🇻🇨","🇻🇪","🇻🇬","🇻🇮","🇻🇳","🇻🇺","🇼🇫","🇼🇸","🇽🇰","🇾🇪","🇾🇹","🇿🇦","🇿🇲","🇿🇼","🏴󠁧󠁢󠁥󠁮󠁧󠁿","🏴󠁧󠁢󠁳󠁣󠁴󠁿","🏴󠁧󠁢󠁷󠁬󠁳󠁿"],"dive":["🤿"],"shirt":["🎽","👕"],"skis":["🎿"],"sled":["🛷"],"curl":["🥌","📃"],"stone":["🥌","💎"],"bullseye":["🎯"],"yo":["🪀"],"kite":["🪁"],"pistol":["🔫"],"pool":["🎱"],"crystal":["🔮"],"magic":["🪄"],"wand":["🪄"],"video":["🎮","📹"],"joystick":["🕹️"],"slot":["🎰"],"machine":["🎰","📠"],"die":["🎲"],"puzzle":["🧩"],"piece":["🧩","🩱"],"teddy":["🧸"],"pinata":["🪅"],"mirror":["🪩","🪞"],"spade":["♠️"],"diamond":["♦️","🔶","🔷","🔸","🔹","💠"],"club":["♣️"],"chess":["♟️"],"pawn":["♟️"],"joker":["🃏"],"mahjong":["🀄"],"cards":["🎴"],"perform":["🎭"],"framed":["🖼️"],"picture":["🖼️"],"palette":["🎨"],"thread":["🧵"],"sew":["🪡"],"needle":["🪡"],"yarn":["🧶"],"knot":["🪢"],"goggles":["🥽"],"lab":["🥼"],"coat":["🥼","🧥"],"safety":["🦺","🧷"],"vest":["🦺"],"necktie":["👔"],"jeans":["👖"],"scarf":["🧣"],"gloves":["🧤"],"socks":["🧦"],"dress":["👗"],"kimono":["👘"],"sari":["🥻"],"swimsuit":["🩱"],"briefs":["🩲"],"shorts":["🩳"],"bikini":["👙"],"clothes":["👚"],"fold":["🪭"],"fan":["🪭"],"purse":["👛"],"handbag":["👜"],"clutch":["👝"],"bag":["👝","💰"],"shop":["🛍️","🛒"],"bags":["🛍️"],"backpack":["🎒"],"thong":["🩴"],"sandal":["🩴","👡"],"shoe":["👞","👟","🥿","👠"],"hike":["🥾"],"boot":["🥾","👢"],"flat":["🥿"],"heeled":["👠"],"ballet":["🩰"],"shoes":["🩰"],"pick":["🪮","⛏️","⚒️"],"top":["🎩","🔝"],"graduation":["🎓"],"cap":["🎓","🧢"],"billed":["🧢"],"helmet":["🪖","⛑️"],"rescue":["⛑️"],"prayer":["📿"],"beads":["📿"],"lipstick":["💄"],"gem":["💎"],"muted":["🔇"],"speaker":["🔇","🔈","🔉","🔊"],"low":["🔈","🪫"],"volume":["🔈","🔉","🔊"],"medium":["🔉","◼️","◻️","◾","◽"],"loudspeaker":["📢"],"megaphone":["📣"],"postal":["📯"],"horn":["📯"],"slash":["🔕"],"musical":["🎼","🎵","🎶","🎹"],"score":["🎼"],"note":["🎵"],"notes":["🎶"],"studio":["🎙️"],"microphone":["🎙️","🎤"],"level":["🎚️"],"slider":["🎚️"],"control":["🎛️","🛂"],"knobs":["🎛️"],"headphone":["🎧"],"radio":["📻","🔘"],"saxophone":["🎷"],"accordion":["🪗"],"guitar":["🎸"],"keyboard":["🎹","⌨️"],"trumpet":["🎺"],"violin":["🎻"],"banjo":["🪕"],"long":["🪘"],"maracas":["🪇"],"flute":["🪈"],"mobile":["📱","📲","📵","📴"],"phone":["📱","📲","📴"],"telephone":["☎️","📞"],"receiver":["📞"],"pager":["📟"],"fax":["📠"],"battery":["🔋","🪫"],"electric":["🔌"],"plug":["🔌"],"laptop":["💻"],"desktop":["🖥️"],"computer":["🖥️","🖱️","💽"],"printer":["🖨️"],"trackball":["🖲️"],"disk":["💽","💾","💿"],"floppy":["💾"],"optical":["💿"],"dvd":["📀"],"abacus":["🧮"],"movie":["🎥"],"camera":["🎥","📷","📸","📹"],"film":["🎞️","📽️"],"frames":["🎞️"],"projector":["📽️"],"clapper":["🎬"],"board":["🎬"],"television":["📺"],"flash":["📸"],"videocassette":["📼"],"magnify":["🔍","🔎"],"tilted":["🔍","🔎"],"candle":["🕯️"],"bulb":["💡"],"flashlight":["🔦"],"paper":["🏮","🧻"],"diya":["🪔"],"lamp":["🪔","🛋️"],"notebook":["📔","📓"],"decorative":["📔"],"cover":["📔"],"book":["📕","📖","📗","📘","📙"],"books":["📚"],"ledger":["📒"],"page":["📃","📄"],"scroll":["📜"],"newspaper":["📰","🗞️"],"rolled":["🗞️"],"bookmark":["📑","🔖"],"tabs":["📑"],"label":["🏷️"],"coin":["🪙"],"yen":["💴","💹"],"banknote":["💴","💵","💶","💷"],"dollar":["💵","💲"],"euro":["💶"],"pound":["💷"],"wings":["💸"],"credit":["💳"],"card":["💳","🗂️","📇","🗃️","🪪"],"receipt":["🧾"],"chart":["💹","📈","📉","📊"],"increase":["💹","📈"],"mail":["📧"],"income":["📨"],"outbox":["📤"],"tray":["📤","📥"],"inbox":["📥"],"package":["📦"],"mailbox":["📫","📪","📬","📭"],"lowered":["📪","📭"],"postbox":["📮"],"ballot":["🗳️"],"pencil":["✏️"],"nib":["✒️"],"pen":["🖋️","🖊️","🔏"],"paintbrush":["🖌️"],"crayon":["🖍️"],"memo":["📝"],"briefcase":["💼"],"file":["📁","📂","🗃️","🗄️"],"folder":["📁","📂"],"dividers":["🗂️"],"calendar":["📅","📆","🗓️"],"off":["📆","📴"],"notepad":["🗒️"],"decrease":["📉"],"clipboard":["📋"],"pushpin":["📌","📍"],"round":["📍"],"paperclip":["📎"],"linked":["🖇️"],"paperclips":["🖇️"],"straight":["📏"],"ruler":["📏","📐"],"triangular":["📐","🚩"],"scissors":["✂️"],"cabinet":["🗄️"],"wastebasket":["🗑️"],"locked":["🔒","🔏","🔐"],"unlocked":["🔓"],"key":["🔐","🔑","🗝️"],"hammer":["🔨","⚒️","🛠️"],"axe":["🪓"],"wrench":["🛠️","🔧"],"dagger":["🗡️"],"swords":["⚔️"],"bomb":["💣"],"boomerang":["🪃"],"shield":["🛡️"],"carpentry":["🪚"],"saw":["🪚"],"screwdriver":["🪛"],"nut":["🔩"],"bolt":["🔩"],"gear":["⚙️"],"clamp":["🗜️"],"balance":["⚖️"],"scale":["⚖️"],"link":["🔗"],"chains":["⛓️"],"hook":["🪝"],"toolbox":["🧰"],"magnet":["🧲"],"ladder":["🪜"],"alembic":["⚗️"],"test":["🧪"],"tube":["🧪"],"petri":["🧫"],"dish":["🧫"],"dna":["🧬"],"microscope":["🔬"],"telescope":["🔭"],"antenna":["📡","📶"],"syringe":["💉"],"drop":["🩸"],"blood":["🩸","🅰️","🆎","🅱️","🅾️"],"pill":["💊"],"adhesive":["🩹"],"crutch":["🩼"],"stethoscope":["🩺"],"ray":["🩻"],"door":["🚪"],"elevator":["🛗"],"window":["🪟"],"couch":["🛋️"],"chair":["🪑"],"toilet":["🚽"],"plunger":["🪠"],"shower":["🚿"],"bathtub":["🛁"],"trap":["🪤"],"razor":["🪒"],"lotion":["🧴"],"pin":["🧷"],"broom":["🧹"],"basket":["🧺"],"bucket":["🪣"],"soap":["🧼"],"bubbles":["🫧"],"toothbrush":["🪥"],"sponge":["🧽"],"extinguisher":["🧯"],"cart":["🛒"],"cigarette":["🚬"],"coffin":["⚰️"],"headstone":["🪦"],"funeral":["⚱️"],"urn":["⚱️"],"nazar":["🧿"],"amulet":["🧿"],"hamsa":["🪬"],"moai":["🗿"],"placard":["🪧"],"identification":["🪪"],"atm":["🏧"],"litter":["🚮","🚯"],"bin":["🚮"],"potable":["🚰","🚱"],"restroom":["🚻"],"closet":["🚾"],"passport":["🛂"],"customs":["🛃"],"baggage":["🛄"],"claim":["🛄"],"warn":["⚠️"],"children":["🚸"],"cross":["🚸","✝️","☦️","❌","❎"],"entry":["⛔"],"prohibited":["🚫","🈲"],"bicycles":["🚳"],"smoke":["🚭"],"non":["🚱"],"pedestrians":["🚷"],"phones":["📵"],"under":["🔞"],"eighteen":["🔞"],"radioactive":["☢️"],"biohazard":["☣️"],"curve":["↩️","↪️","⤴️","⤵️"],"clockwise":["🔃"],"arrows":["🔃","🔄"],"counterclockwise":["🔄"],"button":["🔄","🔀","🔁","🔂","▶️","⏩","⏭️","⏯️","◀️","⏪","⏮️","🔼","⏫","🔽","⏬","⏸️","⏹️","⏺️","⏏️","🔅","🔆","✅","❎","🅰️","🆎","🅱️","🆑","🆒","🆓","🆔","🆕","🆖","🅾️","🆗","🅿️","🆘","🆙","🆚","🈁","🈂️","🈷️","🈶","🈯","🉐","🈹","🈚","🈲","🉑","🈸","🈴","🈳","㊗️","㊙️","🈺","🈵","🔘","🔳","🔲"],"end":["🔚"],"soon":["🔜"],"worship":["🛐"],"atom":["⚛️"],"om":["🕉️"],"david":["✡️"],"dharma":["☸️"],"yin":["☯️"],"yang":["☯️"],"latin":["✝️","🔠","🔡","🔤"],"orthodox":["☦️"],"peace":["☮️"],"menorah":["🕎"],"pointed":["🔯","✴️","🔺","🔻"],"khanda":["🪯"],"aries":["♈"],"taurus":["♉"],"gemini":["♊"],"cancer":["♋"],"leo":["♌"],"virgo":["♍"],"libra":["♎"],"scorpio":["♏"],"sagittarius":["♐"],"capricorn":["♑"],"aquarius":["♒"],"pisces":["♓"],"ophiuchus":["⛎"],"shuffle":["🔀"],"tracks":["🔀"],"repeat":["🔁","🔂"],"single":["🔂"],"fast":["⏩","⏪","⏫","⏬"],"forward":["⏩"],"next":["⏭️"],"or":["⏯️"],"pause":["⏯️","⏸️"],"reverse":["◀️","⏪"],"upwards":["🔼"],"downwards":["🔽"],"record":["⏺️"],"eject":["⏏️"],"cinema":["🎦"],"dim":["🔅"],"bright":["🔆"],"bars":["📶"],"wireless":["🛜"],"vibration":["📳"],"mode":["📳"],"female":["♀️"],"male":["♂️"],"transgender":["⚧️","🏳️‍⚧️"],"multiply":["✖️"],"plus":["➕"],"minus":["➖"],"divide":["➗"],"heavy":["🟰","💲"],"equals":["🟰"],"infinity":["♾️"],"double":["‼️","➿"],"question":["⁉️","❓","❔"],"wavy":["〰️"],"currency":["💱"],"exchange":["💱"],"recycle":["♻️"],"fleur":["⚜️"],"de":["⚜️"],"lis":["⚜️"],"trident":["🔱"],"emblem":["🔱"],"name":["📛"],"badge":["📛"],"for":["🔰","🈺"],"beginner":["🔰"],"hollow":["⭕"],"circle":["⭕","🔴","🟠","🟡","🟢","🔵","🟣","🟤","⚫","⚪"],"check":["✅","☑️","✔️"],"loop":["➰","➿"],"part":["〽️"],"alternation":["〽️"],"spoked":["✳️"],"asterisk":["✳️"],"copyright":["©️"],"registered":["®️"],"trade":["™️"],"keycap":["#️⃣","*️⃣","0️⃣","1️⃣","2️⃣","3️⃣","4️⃣","5️⃣","6️⃣","7️⃣","8️⃣","9️⃣","🔟"],"input":["🔠","🔡","🔢","🔣","🔤"],"uppercase":["🔠"],"lowercase":["🔡"],"numbers":["🔢"],"letters":["🔤"],"type":["🅰️","🆎","🅱️","🅾️"],"ab":["🆎"],"cl":["🆑"],"cool":["🆒"],"free":["🆓","🈶","🈚"],"information":["ℹ️"],"id":["🆔"],"circled":["Ⓜ️"],"ng":["🆖"],"sos":["🆘"],"vs":["🆚"],"here":["🈁"],"charge":["🈂️","🈶","🈚"],"monthly":["🈷️"],"amount":["🈷️"],"reserved":["🈯"],"bargain":["🉐"],"discount":["🈹"],"acceptable":["🉑"],"application":["🈸"],"pass":["🈴"],"grade":["🈴"],"vacancy":["🈳","🈵"],"congratulations":["㊗️"],"secret":["㊙️"],"business":["🈺"],"square":["🟥","🟧","🟨","🟩","🟦","🟪","🟫","⬛","⬜","◼️","◻️","◾","◽","▪️","▫️","🔳","🔲"],"triangle":["🔺","🔻"],"dot":["💠"],"chequered":["🏁"],"flags":["🎌"],"pirate":["🏴‍☠️"],"ascension":["🇦🇨"],"andorra":["🇦🇩"],"united":["🇦🇪","🇬🇧","🇺🇳","🇺🇸"],"arab":["🇦🇪"],"emirates":["🇦🇪"],"afghanistan":["🇦🇫"],"antigua":["🇦🇬"],"barbuda":["🇦🇬"],"anguilla":["🇦🇮"],"albania":["🇦🇱"],"armenia":["🇦🇲"],"angola":["🇦🇴"],"antarctica":["🇦🇶"],"argentina":["🇦🇷"],"samoa":["🇦🇸","🇼🇸"],"austria":["🇦🇹"],"aruba":["🇦🇼"],"aland":["🇦🇽"],"islands":["🇦🇽","🇨🇨","🇨🇰","🇫🇰","🇫🇴","🇬🇸","🇭🇲","🇮🇨","🇰🇾","🇲🇭","🇲🇵","🇵🇳","🇸🇧","🇹🇨","🇺🇲","🇻🇬","🇻🇮"],"azerbaijan":["🇦🇿"],"bosnia":["🇧🇦"],"herzegovina":["🇧🇦"],"barbados":["🇧🇧"],"bangladesh":["🇧🇩"],"belgium":["🇧🇪"],"burkina":["🇧🇫"],"faso":["🇧🇫"],"bulgaria":["🇧🇬"],"bahrain":["🇧🇭"],"burundi":["🇧🇮"],"benin":["🇧🇯"],"st":["🇧🇱","🇰🇳","🇱🇨","🇲🇫","🇵🇲","🇸🇭","🇻🇨"],"barthelemy":["🇧🇱"],"bermuda":["🇧🇲"],"brunei":["🇧🇳"],"bolivia":["🇧🇴"],"caribbean":["🇧🇶"],"netherlands":["🇧🇶","🇳🇱"],"brazil":["🇧🇷"],"bahamas":["🇧🇸"],"bhutan":["🇧🇹"],"bouvet":["🇧🇻"],"botswana":["🇧🇼"],"belarus":["🇧🇾"],"belize":["🇧🇿"],"canada":["🇨🇦"],"cocos":["🇨🇨"],"keel":["🇨🇨"],"congo":["🇨🇩","🇨🇬"],"kinshasa":["🇨🇩"],"central":["🇨🇫"],"african":["🇨🇫"],"republic":["🇨🇫","🇩🇴"],"brazzaville":["🇨🇬"],"switzerland":["🇨🇭"],"cote":["🇨🇮"],"ivoire":["🇨🇮"],"chile":["🇨🇱"],"cameroon":["🇨🇲"],"china":["🇨🇳","🇭🇰","🇲🇴"],"colombia":["🇨🇴"],"clipperton":["🇨🇵"],"costa":["🇨🇷"],"rica":["🇨🇷"],"cuba":["🇨🇺"],"cape":["🇨🇻"],"verde":["🇨🇻"],"curacao":["🇨🇼"],"cyprus":["🇨🇾"],"czechia":["🇨🇿"],"germany":["🇩🇪"],"diego":["🇩🇬"],"garcia":["🇩🇬"],"djibouti":["🇩🇯"],"denmark":["🇩🇰"],"dominica":["🇩🇲"],"dominican":["🇩🇴"],"algeria":["🇩🇿"],"ceuta":["🇪🇦"],"melilla":["🇪🇦"],"ecuador":["🇪🇨"],"estonia":["🇪🇪"],"egypt":["🇪🇬"],"western":["🇪🇭"],"sahara":["🇪🇭"],"eritrea":["🇪🇷"],"spain":["🇪🇸"],"ethiopia":["🇪🇹"],"european":["🇪🇺"],"union":["🇪🇺"],"finland":["🇫🇮"],"fiji":["🇫🇯"],"falkland":["🇫🇰"],"micronesia":["🇫🇲"],"faroe":["🇫🇴"],"france":["🇫🇷"],"gabon":["🇬🇦"],"kingdom":["🇬🇧"],"grenada":["🇬🇩"],"georgia":["🇬🇪","🇬🇸"],"guiana":["🇬🇫"],"guernsey":["🇬🇬"],"ghana":["🇬🇭"],"gibraltar":["🇬🇮"],"greenland":["🇬🇱"],"gambia":["🇬🇲"],"guinea":["🇬🇳","🇬🇶","🇬🇼","🇵🇬"],"guadeloupe":["🇬🇵"],"equatorial":["🇬🇶"],"greece":["🇬🇷"],"south":["🇬🇸","🇰🇷","🇸🇸","🇿🇦"],"guatemala":["🇬🇹"],"guam":["🇬🇺"],"bissau":["🇬🇼"],"guyana":["🇬🇾"],"hong":["🇭🇰"],"kong":["🇭🇰"],"sar":["🇭🇰","🇲🇴"],"heard":["🇭🇲"],"mcdonald":["🇭🇲"],"honduras":["🇭🇳"],"croatia":["🇭🇷"],"haiti":["🇭🇹"],"hungary":["🇭🇺"],"canary":["🇮🇨"],"indonesia":["🇮🇩"],"ireland":["🇮🇪"],"israel":["🇮🇱"],"isle":["🇮🇲"],"india":["🇮🇳"],"british":["🇮🇴","🇻🇬"],"indian":["🇮🇴"],"ocean":["🇮🇴"],"territory":["🇮🇴"],"iraq":["🇮🇶"],"iran":["🇮🇷"],"iceland":["🇮🇸"],"italy":["🇮🇹"],"jersey":["🇯🇪"],"jamaica":["🇯🇲"],"jordan":["🇯🇴"],"kenya":["🇰🇪"],"kyrgyzstan":["🇰🇬"],"cambodia":["🇰🇭"],"kiribati":["🇰🇮"],"comoros":["🇰🇲"],"kitts":["🇰🇳"],"nevis":["🇰🇳"],"north":["🇰🇵","🇲🇰"],"korea":["🇰🇵","🇰🇷"],"kuwait":["🇰🇼"],"cayman":["🇰🇾"],"kazakhstan":["🇰🇿"],"laos":["🇱🇦"],"lebanon":["🇱🇧"],"lucia":["🇱🇨"],"liechtenstein":["🇱🇮"],"sri":["🇱🇰"],"lanka":["🇱🇰"],"liberia":["🇱🇷"],"lesotho":["🇱🇸"],"lithuania":["🇱🇹"],"luxembourg":["🇱🇺"],"latvia":["🇱🇻"],"libya":["🇱🇾"],"morocco":["🇲🇦"],"monaco":["🇲🇨"],"moldova":["🇲🇩"],"montenegro":["🇲🇪"],"martin":["🇲🇫"],"madagascar":["🇲🇬"],"marshall":["🇲🇭"],"macedonia":["🇲🇰"],"mali":["🇲🇱"],"myanmar":["🇲🇲"],"burma":["🇲🇲"],"mongolia":["🇲🇳"],"macao":["🇲🇴"],"northern":["🇲🇵"],"mariana":["🇲🇵"],"martinique":["🇲🇶"],"mauritania":["🇲🇷"],"montserrat":["🇲🇸"],"malta":["🇲🇹"],"mauritius":["🇲🇺"],"maldives":["🇲🇻"],"malawi":["🇲🇼"],"mexico":["🇲🇽"],"malaysia":["🇲🇾"],"mozambique":["🇲🇿"],"namibia":["🇳🇦"],"caledonia":["🇳🇨"],"niger":["🇳🇪"],"norfolk":["🇳🇫"],"nigeria":["🇳🇬"],"nicaragua":["🇳🇮"],"norway":["🇳🇴"],"nepal":["🇳🇵"],"nauru":["🇳🇷"],"niue":["🇳🇺"],"zealand":["🇳🇿"],"oman":["🇴🇲"],"panama":["🇵🇦"],"peru":["🇵🇪"],"polynesia":["🇵🇫"],"papua":["🇵🇬"],"philippines":["🇵🇭"],"pakistan":["🇵🇰"],"poland":["🇵🇱"],"pierre":["🇵🇲"],"miquelon":["🇵🇲"],"pitcairn":["🇵🇳"],"puerto":["🇵🇷"],"rico":["🇵🇷"],"palestinian":["🇵🇸"],"territories":["🇵🇸","🇹🇫"],"portugal":["🇵🇹"],"palau":["🇵🇼"],"paraguay":["🇵🇾"],"qatar":["🇶🇦"],"reunion":["🇷🇪"],"romania":["🇷🇴"],"serbia":["🇷🇸"],"russia":["🇷🇺"],"rwanda":["🇷🇼"],"saudi":["🇸🇦"],"arabia":["🇸🇦"],"solomon":["🇸🇧"],"seychelles":["🇸🇨"],"sudan":["🇸🇩","🇸🇸"],"sweden":["🇸🇪"],"singapore":["🇸🇬"],"helena":["🇸🇭"],"slovenia":["🇸🇮"],"svalbard":["🇸🇯"],"jan":["🇸🇯"],"mayen":["🇸🇯"],"slovakia":["🇸🇰"],"sierra":["🇸🇱"],"leone":["🇸🇱"],"san":["🇸🇲"],"marino":["🇸🇲"],"senegal":["🇸🇳"],"somalia":["🇸🇴"],"suriname":["🇸🇷"],"sao":["🇸🇹"],"tome":["🇸🇹"],"principe":["🇸🇹"],"el":["🇸🇻"],"salvador":["🇸🇻"],"sint":["🇸🇽"],"maarten":["🇸🇽"],"syria":["🇸🇾"],"eswatini":["🇸🇿"],"tristan":["🇹🇦"],"da":["🇹🇦"],"cunha":["🇹🇦"],"turks":["🇹🇨"],"caicos":["🇹🇨"],"chad":["🇹🇩"],"southern":["🇹🇫"],"togo":["🇹🇬"],"thailand":["🇹🇭"],"tajikistan":["🇹🇯"],"tokelau":["🇹🇰"],"timor":["🇹🇱"],"leste":["🇹🇱"],"turkmenistan":["🇹🇲"],"tunisia":["🇹🇳"],"tonga":["🇹🇴"],"trinidad":["🇹🇹"],"tobago":["🇹🇹"],"tuvalu":["🇹🇻"],"taiwan":["🇹🇼"],"tanzania":["🇹🇿"],"ukraine":["🇺🇦"],"uganda":["🇺🇬"],"us":["🇺🇲","🇻🇮"],"outly":["🇺🇲"],"nations":["🇺🇳"],"states":["🇺🇸"],"uruguay":["🇺🇾"],"uzbekistan":["🇺🇿"],"vatican":["🇻🇦"],"city":["🇻🇦"],"vincent":["🇻🇨"],"grenadines":["🇻🇨"],"venezuela":["🇻🇪"],"virgin":["🇻🇬","🇻🇮"],"vietnam":["🇻🇳"],"vanuatu":["🇻🇺"],"wallis":["🇼🇫"],"futuna":["🇼🇫"],"kosovo":["🇽🇰"],"yemen":["🇾🇪"],"mayotte":["🇾🇹"],"zambia":["🇿🇲"],"zimbabwe":["🇿🇼"],"england":["🏴󠁧󠁢󠁥󠁮󠁧󠁿"],"scotland":["🏴󠁧󠁢󠁳󠁣󠁴󠁿"],"wales":["🏴󠁧󠁢󠁷󠁬󠁳󠁿"]}
},{}],127:[function(require,module,exports){
module.exports={"߀":"0","́":""," ":" ","Ⓐ":"A","Ａ":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","Ｂ":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ɓ":"B","ｃ":"C","Ⓒ":"C","Ｃ":"C","Ꜿ":"C","Ḉ":"C","Ç":"C","Ⓓ":"D","Ｄ":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ɗ":"D","Ɖ":"D","ᴅ":"D","Ꝺ":"D","Ð":"Dh","Ǳ":"DZ","Ǆ":"DZ","ǲ":"Dz","ǅ":"Dz","ɛ":"E","Ⓔ":"E","Ｅ":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","ᴇ":"E","ꝼ":"F","Ⓕ":"F","Ｆ":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","Ｇ":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","ɢ":"G","Ⓗ":"H","Ｈ":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","Ｉ":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","Ｊ":"J","Ĵ":"J","Ɉ":"J","ȷ":"J","Ⓚ":"K","Ｋ":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","Ｌ":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","Ǉ":"LJ","ǈ":"Lj","Ⓜ":"M","Ｍ":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","ϻ":"M","Ꞥ":"N","Ƞ":"N","Ⓝ":"N","Ｎ":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ɲ":"N","Ꞑ":"N","ᴎ":"N","Ǌ":"NJ","ǋ":"Nj","Ⓞ":"O","Ｏ":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Œ":"OE","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","Ｐ":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Ｑ":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","Ｒ":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","Ｓ":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","Ｔ":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Þ":"Th","Ꜩ":"TZ","Ⓤ":"U","Ｕ":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","Ｖ":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","Ｗ":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","Ｘ":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Ｙ":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Ｚ":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","ａ":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ɑ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","ｂ":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","Ƃ":"b","ⓒ":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","C":"c","Ć":"c","Ĉ":"c","Ċ":"c","Č":"c","Ƈ":"c","Ȼ":"c","ⓓ":"d","ｄ":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","Ƌ":"d","Ꮷ":"d","ԁ":"d","Ɦ":"d","ð":"dh","ǳ":"dz","ǆ":"dz","ⓔ":"e","ｅ":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ǝ":"e","ⓕ":"f","ｆ":"f","ḟ":"f","ƒ":"f","ﬀ":"ff","ﬁ":"fi","ﬂ":"fl","ﬃ":"ffi","ﬄ":"ffl","ⓖ":"g","ｇ":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ꝿ":"g","ᵹ":"g","ⓗ":"h","ｈ":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","ｉ":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","ｊ":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","ｋ":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","ｌ":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","ɭ":"l","ǉ":"lj","ⓜ":"m","ｍ":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","ｎ":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ŉ":"n","ꞑ":"n","ꞥ":"n","ԉ":"n","ǌ":"nj","ⓞ":"o","ｏ":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ɔ":"o","ᴑ":"o","œ":"oe","ƣ":"oi","ꝏ":"oo","ȣ":"ou","ⓟ":"p","ｐ":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ρ":"p","ⓠ":"q","ｑ":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","ｒ":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","ｓ":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ʂ":"s","ß":"ss","ⓣ":"t","ｔ":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","þ":"th","ꜩ":"tz","ⓤ":"u","ｕ":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","ｖ":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","ｗ":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","ｘ":"x","ẋ":"x","ẍ":"x","ⓨ":"y","ｙ":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","ｚ":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z"}
},{}],128:[function(require,module,exports){
module.exports=[{"s":9728,"e":9747,"w":1},{"s":9748,"e":9749,"w":2},{"s":9750,"e":9799,"w":1},{"s":9800,"e":9811,"w":2},{"s":9812,"e":9854,"w":1},{"s":9855,"e":9855,"w":2},{"s":9856,"e":9874,"w":1},{"s":9875,"e":9875,"w":2},{"s":9876,"e":9888,"w":1},{"s":9889,"e":9889,"w":2},{"s":9890,"e":9897,"w":1},{"s":9898,"e":9899,"w":2},{"s":9900,"e":9916,"w":1},{"s":9917,"e":9918,"w":2},{"s":9919,"e":9923,"w":1},{"s":9924,"e":9925,"w":2},{"s":9926,"e":9933,"w":1},{"s":9934,"e":9934,"w":2},{"s":9935,"e":9939,"w":1},{"s":9940,"e":9940,"w":2},{"s":9941,"e":9961,"w":1},{"s":9962,"e":9962,"w":2},{"s":9963,"e":9969,"w":1},{"s":9970,"e":9971,"w":2},{"s":9972,"e":9972,"w":1},{"s":9973,"e":9973,"w":2},{"s":9974,"e":9977,"w":1},{"s":9978,"e":9978,"w":2},{"s":9979,"e":9980,"w":1},{"s":9981,"e":9981,"w":2},{"s":9982,"e":9983,"w":1},{"s":9984,"e":9988,"w":1},{"s":9989,"e":9989,"w":2},{"s":9990,"e":9993,"w":1},{"s":9994,"e":9995,"w":2},{"s":9996,"e":10023,"w":1},{"s":10024,"e":10024,"w":2},{"s":10025,"e":10059,"w":1},{"s":10060,"e":10060,"w":2},{"s":10061,"e":10061,"w":1},{"s":10062,"e":10062,"w":2},{"s":10063,"e":10066,"w":1},{"s":10067,"e":10069,"w":2},{"s":10070,"e":10070,"w":1},{"s":10071,"e":10071,"w":2},{"s":10072,"e":10132,"w":1},{"s":10133,"e":10135,"w":2},{"s":10136,"e":10159,"w":1},{"s":10160,"e":10160,"w":2},{"s":10161,"e":10174,"w":1},{"s":10175,"e":10175,"w":2},{"s":126976,"e":126979,"w":1},{"s":126980,"e":126980,"w":2},{"s":126981,"e":127182,"w":1},{"s":127183,"e":127183,"w":2},{"s":127184,"e":127373,"w":1},{"s":127374,"e":127374,"w":2},{"s":127375,"e":127376,"w":1},{"s":127377,"e":127386,"w":2},{"s":127387,"e":127487,"w":1},{"s":127744,"e":127776,"w":2},{"s":127777,"e":127788,"w":1},{"s":127789,"e":127797,"w":2},{"s":127798,"e":127798,"w":1},{"s":127799,"e":127868,"w":2},{"s":127869,"e":127869,"w":1},{"s":127870,"e":127891,"w":2},{"s":127892,"e":127903,"w":1},{"s":127904,"e":127946,"w":2},{"s":127947,"e":127950,"w":1},{"s":127951,"e":127955,"w":2},{"s":127956,"e":127967,"w":1},{"s":127968,"e":127984,"w":2},{"s":127985,"e":127987,"w":1},{"s":127988,"e":127988,"w":2},{"s":127989,"e":127991,"w":1},{"s":127992,"e":127994,"w":2},{"s":128000,"e":128062,"w":2},{"s":128063,"e":128063,"w":1},{"s":128064,"e":128064,"w":2},{"s":128065,"e":128065,"w":1},{"s":128066,"e":128252,"w":2},{"s":128253,"e":128254,"w":1},{"s":128255,"e":128317,"w":2},{"s":128318,"e":128330,"w":1},{"s":128331,"e":128334,"w":2},{"s":128335,"e":128335,"w":1},{"s":128336,"e":128359,"w":2},{"s":128360,"e":128377,"w":1},{"s":128378,"e":128378,"w":2},{"s":128379,"e":128404,"w":1},{"s":128405,"e":128406,"w":2},{"s":128407,"e":128419,"w":1},{"s":128420,"e":128420,"w":2},{"s":128421,"e":128506,"w":1},{"s":128507,"e":128591,"w":2},{"s":128592,"e":128639,"w":1},{"s":128640,"e":128709,"w":2},{"s":128710,"e":128715,"w":1},{"s":128716,"e":128716,"w":2},{"s":128717,"e":128719,"w":1},{"s":128720,"e":128722,"w":2},{"s":128723,"e":128724,"w":1},{"s":128725,"e":128727,"w":2},{"s":128728,"e":128746,"w":1},{"s":128747,"e":128748,"w":2},{"s":128749,"e":128755,"w":1},{"s":128756,"e":128764,"w":2},{"s":128765,"e":128991,"w":1},{"s":128992,"e":129003,"w":2},{"s":129004,"e":129291,"w":1},{"s":129292,"e":129338,"w":2},{"s":129339,"e":129339,"w":1},{"s":129340,"e":129349,"w":2},{"s":129350,"e":129350,"w":1},{"s":129351,"e":129400,"w":2},{"s":129401,"e":129401,"w":1},{"s":129402,"e":129483,"w":2},{"s":129484,"e":129484,"w":1},{"s":129485,"e":129535,"w":2},{"s":129536,"e":129647,"w":1},{"s":129648,"e":129652,"w":2},{"s":129653,"e":129655,"w":1},{"s":129656,"e":129658,"w":2},{"s":129659,"e":129663,"w":1},{"s":129664,"e":129670,"w":2},{"s":129671,"e":129679,"w":1},{"s":129680,"e":129704,"w":2},{"s":129705,"e":129711,"w":1},{"s":129712,"e":129718,"w":2},{"s":129719,"e":129727,"w":1},{"s":129728,"e":129730,"w":2},{"s":129731,"e":129743,"w":1},{"s":129744,"e":129750,"w":2},{"s":129751,"e":129791,"w":1}]

},{}],129:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const latinizeMap = require( './json-data/latinize-map.json' ) ;

module.exports = function( str ) {
	return str.replace( /[^\u0000-\u007e]/g , ( c ) => { return latinizeMap[ c ] || c ; } ) ;
} ;



},{"./json-data/latinize-map.json":127}],130:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



exports.resize = function( str , length ) {
	if ( str.length === length ) {
		return str ;
	}
	else if ( str.length > length ) {
		return str.slice( 0 , length ) ;
	}

	return str + ' '.repeat( length - str.length ) ;

} ;



exports.occurrenceCount = function( str , subStr , overlap = false ) {
	if ( ! str || ! subStr ) { return 0 ; }

	var count = 0 , index = 0 ,
		inc = overlap ? 1 : subStr.length ;

	while ( ( index = str.indexOf( subStr , index ) ) !== -1 ) {
		count ++ ;
		index += inc ;
	}

	return count ;
} ;


},{}],131:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const CONTROL_CLASS = 1 ;
const WORD_SEPARATOR_CLASS = 2 ;
const LETTER_CLASS = 3 ;
const NUMBER_CLASS = 4 ;
const SYMBOL_CLASS = 5 ;



function getCharacterClass( char , code ) {
	if ( isWordSeparator( code ) ) { return WORD_SEPARATOR_CLASS ; }
	if ( code <= 0x1f || code === 0x7f ) { return CONTROL_CLASS ; }
	if ( isNumber( code ) ) { return NUMBER_CLASS ; }
	// Here we assume that a letter is a char with a “case”
	if ( char.toUpperCase() !== char.toLowerCase() ) { return LETTER_CLASS ; }
	return SYMBOL_CLASS ;
}



function isWordSeparator( code ) {
	if (
		// space, tab, no-break space
		code === 0x20 || code === 0x09 || code === 0xa0 ||
		// hyphen, underscore
		code === 0x2d || code === 0x5f
	) {
		return true ;
	}

	return false ;
}



function isNumber( code ) {
	if ( code >= 0x30 && code <= 0x39 ) { return true ; }
	return false ;
}



function naturalSort( a , b ) {
	a = '' + a ;
	b = '' + b ;

	var aIndex , aEndIndex , aChar , aCode , aClass , aCharLc , aNumber ,
		aTrim = a.trim() ,
		aLength = aTrim.length ,
		bIndex , bEndIndex , bChar , bCode , bClass , bCharLc , bNumber ,
		bTrim = b.trim() ,
		bLength = bTrim.length ,
		advantage = 0 ;

	for ( aIndex = bIndex = 0 ; aIndex < aLength && bIndex < bLength ; aIndex ++ , bIndex ++ ) {
		aChar = aTrim[ aIndex ] ;
		bChar = bTrim[ bIndex ] ;
		aCode = aTrim.charCodeAt( aIndex ) ;
		bCode = bTrim.charCodeAt( bIndex ) ;
		aClass = getCharacterClass( aChar , aCode ) ;
		bClass = getCharacterClass( bChar , bCode ) ;
		if ( aClass !== bClass ) { return aClass - bClass ; }

		switch ( aClass ) {
			case WORD_SEPARATOR_CLASS :
				// Eat all white chars and continue
				while ( isWordSeparator( aTrim.charCodeAt( aIndex + 1 ) ) ) { aIndex ++ ; }
				while ( isWordSeparator( bTrim.charCodeAt( bIndex + 1 ) ) ) { bIndex ++ ; }
				break ;

			case CONTROL_CLASS :
			case SYMBOL_CLASS :
				if ( aCode !== bCode ) { return aCode - bCode ; }
				break ;

			case LETTER_CLASS :
				aCharLc = aChar.toLowerCase() ;
				bCharLc = bChar.toLowerCase() ;
				if ( aCharLc !== bCharLc ) { return aCharLc > bCharLc ? 1 : -1 ; }

				// As a last resort, we would sort uppercase first
				if ( ! advantage && aChar !== bChar ) { advantage = aChar !== aCharLc ? -1 : 1 ; }

				break ;

			case NUMBER_CLASS :
				// Lookup for a whole number and parse it
				aEndIndex = aIndex + 1 ;
				while ( isNumber( aTrim.charCodeAt( aEndIndex ) ) ) { aEndIndex ++ ; }
				aNumber = parseFloat( aTrim.slice( aIndex , aEndIndex ) ) ;

				bEndIndex = bIndex + 1 ;
				while ( isNumber( bTrim.charCodeAt( bEndIndex ) ) ) { bEndIndex ++ ; }
				bNumber = parseFloat( bTrim.slice( bIndex , bEndIndex ) ) ;

				if ( aNumber !== bNumber ) { return aNumber - bNumber ; }

				// As a last resort, we would sort the number with the less char first
				if ( ! advantage && aEndIndex - aIndex !== bEndIndex - bIndex ) { advantage = ( aEndIndex - aIndex ) - ( bEndIndex - bIndex ) ; }

				// Advance the index at the end of the number area
				aIndex = aEndIndex - 1 ;
				bIndex = bEndIndex - 1 ;
				break ;
		}
	}

	// If there was an “advantage”, use it now
	if ( advantage ) { return advantage ; }

	// Finally, sort by remaining char, or by trimmed length or by full length
	return ( aLength - aIndex ) - ( bLength - bIndex ) || aLength - bLength || a.length - b.length ;
}

module.exports = naturalSort ;


},{}],132:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



var escape = require( './escape.js' ) ;



exports.regexp = {} ;



exports.regexp.array2alternatives = function array2alternatives( array ) {
	var i , sorted = array.slice() ;

	// Sort descending by string length
	sorted.sort( ( a , b ) => {
		return b.length - a.length ;
	} ) ;

	// Then escape what should be
	for ( i = 0 ; i < sorted.length ; i ++ ) {
		sorted[ i ] = escape.regExpPattern( sorted[ i ] ) ;
	}

	return sorted.join( '|' ) ;
} ;



},{"./escape.js":121}],133:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const stringKit = {} ;
module.exports = stringKit ;



/*
// Tier 0: add polyfills to stringKit
const polyfill = require( './polyfill.js' ) ;

for ( let fn_ in polyfill ) {
	stringKit[ fn ] = function( str , ... args ) {
		return polyfill[ fn ].call( str , ... args ) ;
	} ;
}
//*/



Object.assign( stringKit ,

	// Tier 1
	{ escape: require( './escape.js' ) } ,
	{ ansi: require( './ansi.js' ) } ,
	{ unicode: require( './unicode.js' ) }
) ;



Object.assign( stringKit ,

	// Tier 2
	require( './format.js' ) ,

	// Tier 3
	require( './misc.js' ) ,
	require( './inspect.js' ) ,
	require( './regexp.js' ) ,
	require( './camel.js' ) ,
	{
		latinize: require( './latinize.js' ) ,
		toTitleCase: require( './toTitleCase.js' ) ,
		wordwrap: require( './wordwrap.js' ) ,
		naturalSort: require( './naturalSort.js' ) ,
		fuzzy: require( './fuzzy.js' ) ,
		StringNumber: require( './StringNumber.js' ) ,
		english: require( './english.js' ) ,
		emoji: require( './emoji.js' )
	}
) ;



/*
// Install all polyfill into String.prototype
stringKit.installPolyfills = function installPolyfills() {
	for ( let fn in polyfill ) {
		if ( ! String.prototype[ fn ] ) {
			String.prototype[ fn ] = polyfill[ fn ] ;
		}
	}
} ;
//*/


},{"./StringNumber.js":116,"./ansi.js":117,"./camel.js":118,"./emoji.js":119,"./english.js":120,"./escape.js":121,"./format.js":122,"./fuzzy.js":123,"./inspect.js":124,"./latinize.js":129,"./misc.js":130,"./naturalSort.js":131,"./regexp.js":132,"./toTitleCase.js":134,"./unicode.js":135,"./wordwrap.js":136}],134:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const DEFAULT_OPTIONS = {
	underscoreToSpace: true ,
	lowerCaseWords: new Set( [
		// Articles
		'a' , 'an' , 'the' ,
		// Conjunctions (only coordinating conjunctions, maybe we will have to add subordinating and correlative conjunctions)
		'for' , 'and' , 'nor' , 'but' , 'or' , 'yet' , 'so' ,
		// Prepositions (there are more, but usually only preposition with 2 or 3 letters are lower-cased)
		'of' , 'on' , 'off' , 'in' , 'into' , 'by' , 'with' , 'to' , 'at' , 'up' , 'down' , 'as'
	] )
} ;



module.exports = ( str , options = DEFAULT_OPTIONS ) => {
	if ( ! str || typeof str !== 'string' ) { return '' ; }

	// Manage options
	var dashToSpace = options.dashToSpace ?? DEFAULT_OPTIONS.dashToSpace ,
		underscoreToSpace = options.underscoreToSpace ?? DEFAULT_OPTIONS.underscoreToSpace ,
		zealous = options.zealous ?? DEFAULT_OPTIONS.zealous ,
		preserveAllCaps = options.preserveAllCaps ?? DEFAULT_OPTIONS.preserveAllCaps ,
		lowerCaseWords = options.lowerCaseWords ?? DEFAULT_OPTIONS.lowerCaseWords ;

	lowerCaseWords =
		lowerCaseWords instanceof Set ? lowerCaseWords :
		Array.isArray( lowerCaseWords ) ? new Set( lowerCaseWords ) :
		null ;


	if ( dashToSpace ) { str = str.replace( /-+/g , ' ' ) ; }
	if ( underscoreToSpace ) { str = str.replace( /_+/g , ' ' ) ; }

	// Squash multiple spaces into only one, and trim
	str = str.replace( / +/g , ' ' ).trim() ;


	return str.replace( /[^\s_-]+/g , ( part , position ) => {
		// Check word that must be lower-cased (excluding the first and the last word)
		if ( lowerCaseWords && position && position + part.length < str.length ) {
			let lowerCased = part.toLowerCase() ;
			if ( lowerCaseWords.has( lowerCased ) ) { return lowerCased ; }
		}

		if ( zealous ) {
			if ( preserveAllCaps && part === part.toUpperCase() ) {
				// This is a ALLCAPS word
				return part ;
			}

			return part[ 0 ].toUpperCase() + part.slice( 1 ).toLowerCase() ;
		}

		return part[ 0 ].toUpperCase() + part.slice( 1 ) ;
	} ) ;
} ;


},{}],135:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



/*
	Javascript does not use UTF-8 but UCS-2.
	The purpose of this module is to process correctly strings containing UTF-8 characters that take more than 2 bytes.

	Since the punycode module is deprecated in Node.js v8.x, this is an adaptation of punycode.ucs2.x
	as found on Aug 16th 2017 at: https://github.com/bestiejs/punycode.js/blob/master/punycode.js.

	2021 note -- Modern Javascript is way more unicode friendly since many years, e.g. `Array.from( string )` and `for ( char of string )` are unicode aware.
	Some methods here are now useless, but have been modernized to use the correct ES features.
*/



// Create the module and export it
const unicode = {} ;
module.exports = unicode ;



unicode.encode = array => String.fromCodePoint( ... array ) ;

// Decode a string into an array of unicode codepoints.
// The 2nd argument of Array.from() is a map function, it avoids creating intermediate array.
unicode.decode = str => Array.from( str , c => c.codePointAt( 0 ) ) ;

// DEPRECATED: This function is totally useless now, with modern JS.
unicode.firstCodePoint = str => str.codePointAt( 0 ) ;

// Extract only the first char.
unicode.firstChar = str => str.length ? String.fromCodePoint( str.codePointAt( 0 ) ) : undefined ;

// DEPRECATED: This function is totally useless now, with modern JS.
unicode.toArray = str => Array.from( str ) ;



// Decode a string into an array of Cell (used by Terminal-kit).
// Wide chars have an additionnal filler cell, so position is correct
unicode.toCells = ( Cell , str , tabWidth = 4 , linePosition = 0 , ... extraCellArgs ) => {
	var char , code , fillSize , width ,
		output = [] ;

	for ( char of str ) {
		code = char.codePointAt( 0 ) ;

		if ( code === 0x0a ) {	// New line
			linePosition = 0 ;
		}
		else if ( code === 0x09 ) {	// Tab
			// Depends upon the next tab-stop
			fillSize = tabWidth - ( linePosition % tabWidth ) - 1 ;
			//output.push( new Cell( '\t' , ... extraCellArgs ) ) ;
			output.push( new Cell( '\t' , 1 , ... extraCellArgs ) ) ;
			linePosition += 1 + fillSize ;

			// Add a filler cell
			while ( fillSize -- ) { output.push( new Cell( ' ' , -2 , ... extraCellArgs ) ) ; }
		}
		else {
			width = unicode.codePointWidth( code ) ,
			output.push( new Cell( char , width , ... extraCellArgs ) ) ;
			linePosition += width ;

			// Add an anti-filler cell (a cell with 0 width, following a wide char)
			while ( -- width > 0 ) { output.push( new Cell( ' ' , -1 , ... extraCellArgs ) ) ; }
		}
	}

	return output ;
} ;



unicode.fromCells = ( cells ) => {
	var cell , str = '' ;

	for ( cell of cells ) {
		if ( ! cell.filler ) { str += cell.char ; }
	}

	return str ;
} ;



// Get the length of an unicode string
// Mostly an adaptation of .decode(), not factorized for performance's sake (used by Terminal-kit)
// /!\ Use Array.from().length instead??? Not using it is potentially faster, but it needs benchmark to be sure.
unicode.length = str => {
	// for ... of is unicode-aware
	var char , length = 0 ;
	for ( char of str ) { length ++ ; }		/* eslint-disable-line no-unused-vars */
	return length ;
} ;



// Return a string that does not exceed the character limit
unicode.truncateLength = unicode.truncate = ( str , limit ) => {
	var position = 0 , length = 0 ;

	for ( let char of str ) {
		if ( length === limit ) { return str.slice( 0 , position ) ; }
		length ++ ;
		position += char.length ;
	}

	// The string remains unchanged
	return str ;
} ;



// Return the width of a string in a terminal/monospace font
unicode.width = str => {
	// for ... of is unicode-aware
	var char , count = 0 ;
	for ( char of str ) { count += unicode.codePointWidth( char.codePointAt( 0 ) ) ; }
	return count ;
} ;



// Return the width of an array of string in a terminal/monospace font
unicode.arrayWidth = ( array , limit ) => {
	var index , count = 0 ;

	if ( limit === undefined ) { limit = array.length ; }

	for ( index = 0 ; index < limit ; index ++ ) {
		count += unicode.isFullWidth( array[ index ] ) ? 2 : 1 ;
	}

	return count ;
} ;



// Userland may use this, it is more efficient than .truncateWidth() + .width(),
// and BTW even more than testing .width() then .truncateWidth() + .width()
var lastTruncateWidth = 0 ;
unicode.getLastTruncateWidth = () => lastTruncateWidth ;

// Return a string that does not exceed the width limit (taking wide-char into considerations)
unicode.widthLimit =	// DEPRECATED
unicode.truncateWidth = ( str , limit ) => {
	var char , charWidth , position = 0 ;

	// Module global:
	lastTruncateWidth = 0 ;

	for ( char of str ) {
		charWidth = unicode.codePointWidth( char.codePointAt( 0 ) ) ;

		if ( lastTruncateWidth + charWidth > limit ) {
			return str.slice( 0 , position ) ;
		}

		lastTruncateWidth += charWidth ;
		position += char.length ;
	}

	// The string remains unchanged
	return str ;
} ;



/*
	** PROBABLY DEPRECATED **

	Check if a UCS2 char is a surrogate pair.

	Returns:
		0: single char
		1: leading surrogate
		-1: trailing surrogate

	Note: it does not check input, to gain perfs.
*/
unicode.surrogatePair = char => {
	var code = char.charCodeAt( 0 ) ;

	if ( code < 0xd800 || code >= 0xe000 ) { return 0 ; }
	else if ( code < 0xdc00 ) { return 1 ; }
	return -1 ;
} ;



// Check if a character is a full-width char or not
unicode.isFullWidth = char => unicode.isFullWidthCodePoint( char.codePointAt( 0 ) ) ;

// Return the width of a char, leaner than .width() for one char
unicode.charWidth = char => unicode.codePointWidth( char.codePointAt( 0 ) ) ;



/*
	Build the Emoji width lookup.
	The ranges file (./lib/unicode-emoji-width-ranges.json) is produced by a Terminal-Kit script ([terminal-kit]/utilities/build-emoji-width-lookup.js),
	that writes each emoji and check the cursor location.
*/
const emojiWidthLookup = new Map() ;

( function() {
	var ranges = require( './json-data/unicode-emoji-width-ranges.json' ) ;
	for ( let range of ranges ) {
		for ( let i = range.s ; i <= range.e ; i ++ ) {
			emojiWidthLookup.set( i , range.w ) ;
		}
	}
} )() ;

/*
	Check if a codepoint represent a full-width char or not.
*/
unicode.codePointWidth = code => {
	// Assuming all emoji are wide here
	if ( unicode.isEmojiCodePoint( code ) ) {
		return emojiWidthLookup.get( code ) ?? 2 ;
	}

	// Code points are derived from:
	// http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt
	if ( code >= 0x1100 && (
		code <= 0x115f ||	// Hangul Jamo
		code === 0x2329 || // LEFT-POINTING ANGLE BRACKET
		code === 0x232a || // RIGHT-POINTING ANGLE BRACKET
		// CJK Radicals Supplement .. Enclosed CJK Letters and Months
		( 0x2e80 <= code && code <= 0x3247 && code !== 0x303f ) ||
		// Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A
		( 0x3250 <= code && code <= 0x4dbf ) ||
		// CJK Unified Ideographs .. Yi Radicals
		( 0x4e00 <= code && code <= 0xa4c6 ) ||
		// Hangul Jamo Extended-A
		( 0xa960 <= code && code <= 0xa97c ) ||
		// Hangul Syllables
		( 0xac00 <= code && code <= 0xd7a3 ) ||
		// CJK Compatibility Ideographs
		( 0xf900 <= code && code <= 0xfaff ) ||
		// Vertical Forms
		( 0xfe10 <= code && code <= 0xfe19 ) ||
		// CJK Compatibility Forms .. Small Form Variants
		( 0xfe30 <= code && code <= 0xfe6b ) ||
		// Halfwidth and Fullwidth Forms
		( 0xff01 <= code && code <= 0xff60 ) ||
		( 0xffe0 <= code && code <= 0xffe6 ) ||
		// Kana Supplement
		( 0x1b000 <= code && code <= 0x1b001 ) ||
		// Enclosed Ideographic Supplement
		( 0x1f200 <= code && code <= 0x1f251 ) ||
		// CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
		( 0x20000 <= code && code <= 0x3fffd )
	) ) {
		return 2 ;
	}

	if (
		unicode.isEmojiModifierCodePoint( code ) ||
		unicode.isZeroWidthDiacriticCodePoint( code )
	) {
		return 0 ;
	}

	return 1 ;
} ;

// For a true/false type of result
unicode.isFullWidthCodePoint = code => unicode.codePointWidth( code ) === 2 ;



// Convert normal ASCII chars to their full-width counterpart
unicode.toFullWidth = str => {
	return String.fromCodePoint( ... Array.from( str , char => {
		var code = char.codePointAt( 0 ) ;
		return code >= 33 && code <= 126  ?  0xff00 + code - 0x20  :  code ;
	} ) ) ;
} ;



// Check if a character is a diacritic with zero-width or not
unicode.isZeroWidthDiacritic = char => unicode.isZeroWidthDiacriticCodePoint( char.codePointAt( 0 ) ) ;

// Some doc found here: https://en.wikipedia.org/wiki/Combining_character
// Diacritics and other characters that combines with previous one (zero-width)
unicode.isZeroWidthDiacriticCodePoint = code =>
	// Combining Diacritical Marks
	( 0x300 <= code && code <= 0x36f ) ||
	// Combining Diacritical Marks Extended
	( 0x1ab0 <= code && code <= 0x1aff ) ||
	// Combining Diacritical Marks Supplement
	( 0x1dc0 <= code && code <= 0x1dff ) ||
	// Combining Diacritical Marks for Symbols
	( 0x20d0 <= code && code <= 0x20ff ) ||
	// Combining Half Marks
	( 0xfe20 <= code && code <= 0xfe2f ) ||
	// Dakuten and handakuten (japanese)
	code === 0x3099 || code === 0x309a ||
	// Devanagari
	( 0x900 <= code && code <= 0x903 ) ||
	( 0x93a <= code && code <= 0x957 && code !== 0x93d && code !== 0x950 ) ||
	code === 0x962 || code === 0x963 ||
	// Thai
	code === 0xe31 ||
	( 0xe34 <= code && code <= 0xe3a ) ||
	( 0xe47 <= code && code <= 0xe4e ) ;

// Check if a character is an emoji or not
unicode.isEmoji = char => unicode.isEmojiCodePoint( char.codePointAt( 0 ) ) ;

// Some doc found here: https://stackoverflow.com/questions/30470079/emoji-value-range
unicode.isEmojiCodePoint = code =>
	// Miscellaneous symbols
	( 0x2600 <= code && code <= 0x26ff ) ||
	// Dingbats
	( 0x2700 <= code && code <= 0x27bf ) ||
	// Emoji
	( 0x1f000 <= code && code <= 0x1f1ff ) ||
	( 0x1f300 <= code && code <= 0x1f3fa ) ||
	( 0x1f400 <= code && code <= 0x1faff ) ;

// Emoji modifier
unicode.isEmojiModifier = char => unicode.isEmojiModifierCodePoint( char.codePointAt( 0 ) ) ;
unicode.isEmojiModifierCodePoint = code =>
	( 0x1f3fb <= code && code <= 0x1f3ff ) ||	// (Fitzpatrick): https://en.wikipedia.org/wiki/Miscellaneous_Symbols_and_Pictographs#Emoji_modifiers
	code === 0xfe0f ;	// VARIATION SELECTOR-16 [VS16] {emoji variation selector}


},{"./json-data/unicode-emoji-width-ranges.json":128}],136:[function(require,module,exports){
/*
	String Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const unicode = require( './unicode.js' ) ;



// French typography rules with '!', '?', ':' and ';'
const FRENCH_DOUBLE_GRAPH_TYPO = {
	'!': true ,
	'?': true ,
	':': true ,
	';': true
} ;



/*
	.wordwrap( str , width )
	.wordwrap( str , options )

	str: the string to process
	width: the max width (default to 80)
	options: object, where:
		width: the max width (default to 80)
		glue: (optional) the char used to join lines, by default: lines are joined with '\n',
		noJoin: (optional) if set: don't join, instead return an array of lines
		offset: (optional) offset of the first-line
		updateOffset: (optional) if set, options.offset is updated with the last line width
		noTrim: (optional) if set, don't right-trim lines, if not set, right-trim all lines except the last
		fill: (optional) if set, fill the remaining width with space (it forces noTrim)
		skipFn: (optional) a function used to skip a whole sequence, useful for special sequences
			like ANSI escape sequence, and so on...
		charWidthFn: (optional) a function used to compute the width of one char/group of chars
		regroupFn: (optional) a function used to regroup chars together
*/
module.exports = function wordwrap( str , options ) {
	var start = 0 , end , skipEnd , lineWidth , currentLine , currentWidth , length ,
		lastEnd , lastWidth , lastWasSpace , charWidthFn ,
		explicitNewLine = true ,
		strArray = unicode.toArray( str ) ,
		trimNewLine = false ,
		line , lines = [] ;

	if ( typeof options !== 'object' ) {
		options = { width: options } ;
	}

	// Catch NaN, zero or negative and non-number
	if ( ! options.width || typeof options.width !== 'number' || options.width <= 0 ) { options.width = 80 ; }

	lineWidth = options.offset ? options.width - options.offset : options.width ;

	if ( typeof options.glue !== 'string' ) { options.glue = '\n' ; }

	if ( options.regroupFn ) {
		strArray = options.regroupFn( strArray ) ;
		// If char are grouped, use unicode.width() as a default
		charWidthFn = options.charWidthFn || unicode.width ;
	}
	else {
		// If char are not grouped, use unicode.charWidth() as a default
		charWidthFn = options.charWidthFn || unicode.charWidth ;
	}

	length = strArray.length ;

	var getNextLine = () => {
		//originStart = start ;

		if ( ! explicitNewLine || trimNewLine ) {
			// Find the first non-space char
			while ( strArray[ start ] === ' ' ) { start ++ ; }

			if ( trimNewLine && strArray[ start ] === '\n' ) {
				explicitNewLine = true ;
				start ++ ;
				/*
				originStart = start ;
				while ( strArray[ start ] === ' ' ) { start ++ ; }
				*/
			}
		}

		if ( start >= length ) { return null ; }

		explicitNewLine = false ;
		trimNewLine = false ;
		lastWasSpace = false ;
		end = lastEnd = start ;
		currentWidth = lastWidth = 0 ;

		for ( ;; ) {
			if ( end >= length ) {
				return strArray.slice( start , end ).join( '' ) ;
			}

			if ( strArray[ end ] === '\n' ) {
				explicitNewLine = true ;
				currentLine = strArray.slice( start , end ++ ).join( '' ) ;

				if ( options.fill ) {
					currentLine += ' '.repeat( lineWidth - currentWidth ) ;
				}

				return currentLine ;
			}

			if ( options.skipFn ) {
				skipEnd = options.skipFn( strArray , end ) ;
				if ( skipEnd !== end ) {
					end = skipEnd ;
					continue ;
				}
			}

			if ( strArray[ end ] === ' ' && ! lastWasSpace && ! FRENCH_DOUBLE_GRAPH_TYPO[ strArray[ end + 1 ] ] ) {
				// This is the first space of a group of space
				lastEnd = end ;
				lastWidth = currentWidth ;
			}
			else {
				lastWasSpace = false ;
			}

			currentWidth += charWidthFn( strArray[ end ] ) ;

			if ( currentWidth > lineWidth ) {
				// If lastEnd === start, this is a word that takes the whole line: cut it
				// If not, use the lastEnd
				trimNewLine = true ;

				if ( lastEnd !== start ) {
					end = lastEnd ;
				}
				else if ( lineWidth < options.width ) {
					// This is the first line with an offset, so just start over in line two
					end = start ;
					return '' ;
				}

				currentLine = strArray.slice( start , end ).join( '' ) ;

				if ( options.fill ) {
					currentLine += ' '.repeat( lineWidth - lastWidth ) ;
				}

				return currentLine ;
			}

			// Do not move that inside the for(;;), some part are using a continue statement and manage the end value by themself
			end ++ ;
		}
	} ;

	while ( start < length && ( line = getNextLine() ) !== null ) {
		lines.push( line ) ;
		start = end ;
		lineWidth = options.width ;
	}

	// If it ends with an explicit newline, we have to reproduce it now!
	if ( explicitNewLine ) { lines.push( '' ) ; }

	if ( ! options.noTrim && ! options.fill ) {
		lines = lines.map( ( line_ , index ) => index === lines.length - 1 ? line_ : line_.trimRight() ) ;
	}

	if ( ! options.noJoin ) { lines = lines.join( options.glue ) ; }

	if ( options.updateOffset ) { options.offset = currentWidth ; }

	return lines ;
} ;


},{"./unicode.js":135}],137:[function(require,module,exports){
/*
	Tree Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



// Mimic Array's prototype methods, like Array#map(), Array#filter(), and so on...

exports.map = function( object , fn ) {
	if ( ! object || typeof object !== 'object' ) { throw new Error( "Expecting an object" ) ; }
	return Object.fromEntries( Object.entries( object ).map(   entry => [ entry[ 0 ] , fn( entry[ 1 ] ) ]   ) ) ;
} ;

exports.filter = function( object , fn ) {
	if ( ! object || typeof object !== 'object' ) { throw new Error( "Expecting an object" ) ; }
	return Object.fromEntries( Object.entries( object ).filter(   entry => fn( entry[ 1 ] )   ) ) ;
} ;


},{}],138:[function(require,module,exports){
/*
	Tree Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



/*
	Stand-alone fork of extend.js, without options.
*/

function clone( originalObject , circular ) {
	// First create an empty object with
	// same prototype of our original source

	var originalProto = Object.getPrototypeOf( originalObject ) ;

	// Opaque objects, like Date
	if ( clone.opaque.has( originalProto ) ) { return clone.opaque.get( originalProto )( originalObject ) ; }

	var propertyIndex , descriptor , keys , current , nextSource , proto ,
		copies = [ {
			source: originalObject ,
			target: Array.isArray( originalObject ) ? [] : Object.create( originalProto )
		} ] ,
		cloneObject = copies[ 0 ].target ,
		refMap = new Map() ;

	refMap.set( originalObject , cloneObject ) ;


	// First in, first out
	while ( ( current = copies.shift() ) ) {
		keys = Object.getOwnPropertyNames( current.source ) ;

		for ( propertyIndex = 0 ; propertyIndex < keys.length ; propertyIndex ++ ) {
			// Save the source's descriptor
			descriptor = Object.getOwnPropertyDescriptor( current.source , keys[ propertyIndex ] ) ;


			if ( ! descriptor.value || typeof descriptor.value !== 'object' ) {
				Object.defineProperty( current.target , keys[ propertyIndex ] , descriptor ) ;
				continue ;
			}

			nextSource = descriptor.value ;

			if ( circular ) {
				if ( refMap.has( nextSource ) ) {
					// The source is already referenced, just assign reference
					descriptor.value = refMap.get( nextSource ) ;
					Object.defineProperty( current.target , keys[ propertyIndex ] , descriptor ) ;
					continue ;
				}
			}

			proto = Object.getPrototypeOf( descriptor.value ) ;

			// Opaque objects, like Date, not recursivity for them
			if ( clone.opaque.has( proto ) ) {
				descriptor.value = clone.opaque.get( proto )( descriptor.value ) ;
				Object.defineProperty( current.target , keys[ propertyIndex ] , descriptor ) ;
				continue ;
			}

			descriptor.value = Array.isArray( nextSource ) ? [] : Object.create( proto ) ;

			if ( circular ) { refMap.set( nextSource , descriptor.value ) ; }

			Object.defineProperty( current.target , keys[ propertyIndex ] , descriptor ) ;
			copies.push( { source: nextSource , target: descriptor.value } ) ;
		}
	}

	return cloneObject ;
}

module.exports = clone ;



clone.opaque = new Map() ;
clone.opaque.set( Date.prototype , src => new Date( src ) ) ;


},{}],139:[function(require,module,exports){
/*
	Tree Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



/*
	== Diff function ==
*/

function diff( left , right , options ) {
	var i , key , keyPath ,
		leftKeys , rightKeys , leftTypeof , rightTypeof ,
		depth , diffObject , length , arrayMode ;

	leftTypeof = typeof left ;
	rightTypeof = typeof right ;

	if (
		! left || ( leftTypeof !== 'object' && leftTypeof !== 'function' ) ||
		! right || ( rightTypeof !== 'object' && rightTypeof !== 'function' )
	) {
		throw new Error( '[tree] diff() needs objects as argument #0 and #1' ) ;
	}

	if ( ! options || typeof options !== 'object' ) { options = {} ; }

	depth = options.depth || 0 ;

	// Things applied only for the root, not for recursive call
	if ( ! depth ) {
		options.diffObject = {} ;
		if ( ! options.path ) { options.path = '' ; }
		if ( ! options.pathSeparator ) { options.pathSeparator = '.' ; }
	}

	diffObject = options.diffObject ;


	// Left part
	if ( Array.isArray( left ) ) {
		arrayMode = true ;
		length = left.length ;
	}
	else {
		arrayMode = false ;
		leftKeys = Object.keys( left ) ;
		length = leftKeys.length ;
	}

	for ( i = 0 ; i < length ; i ++ ) {
		key = arrayMode ? i : leftKeys[ i ] ;
		keyPath = options.path + options.pathSeparator + key ;
		//console.log( 'L keyPath:' , keyPath ) ;

		if ( ! Object.hasOwn( right , key ) ) {
			diffObject[ keyPath ] = { path: keyPath , message: 'does not exist in right-hand side' } ;
			continue ;
		}

		leftTypeof = typeof left[ key ] ;
		rightTypeof = typeof right[ key ] ;

		if ( leftTypeof !== rightTypeof ) {
			diffObject[ keyPath ] = { path: keyPath , message: 'different typeof: ' + leftTypeof + ' - ' + rightTypeof } ;
			continue ;
		}

		if ( leftTypeof === 'object' || leftTypeof === 'function' ) {
			// Cleanup the 'null is an object' mess
			if ( ! left[ key ] ) {
				if ( right[ key ] ) { diffObject[ keyPath ] = { path: keyPath , message: 'different type: null - Object' } ; }
				continue ;
			}

			if ( ! right[ key ] ) {
				diffObject[ keyPath ] = { path: keyPath , message: 'different type: Object - null' } ;
				continue ;
			}

			if ( Array.isArray( left[ key ] ) && ! Array.isArray( right[ key ] ) ) {
				diffObject[ keyPath ] = { path: keyPath , message: 'different type: Array - Object' } ;
				continue ;
			}

			if ( ! Array.isArray( left[ key ] ) && Array.isArray( right[ key ] ) ) {
				diffObject[ keyPath ] = { path: keyPath , message: 'different type: Object - Array' } ;
				continue ;
			}

			diff( left[ key ] , right[ key ] , {
				path: keyPath , pathSeparator: options.pathSeparator , depth: depth + 1 , diffObject: diffObject
			} ) ;
			continue ;
		}

		if ( left[ key ] !== right[ key ] ) {
			diffObject[ keyPath ] = { path: keyPath , message: 'different value: ' + left[ key ] + ' - ' + right[ key ] } ;
			continue ;
		}
	}


	// Right part
	if ( Array.isArray( right ) ) {
		arrayMode = true ;
		length = right.length ;
	}
	else {
		arrayMode = false ;
		rightKeys = Object.keys( right ) ;
		length = rightKeys.length ;
	}

	for ( i = 0 ; i < length ; i ++ ) {
		key = arrayMode ? i : rightKeys[ i ] ;
		keyPath = options.path + options.pathSeparator + key ;
		//console.log( 'R keyPath:' , keyPath ) ;

		if ( ! Object.hasOwn( left , key ) ) {
			diffObject[ keyPath ] = { path: keyPath , message: 'does not exist in left-hand side' } ;
			continue ;
		}
	}

	return Object.keys( diffObject ).length ? diffObject : null ;
}

exports.diff = diff ;


},{}],140:[function(require,module,exports){
/*
	Tree Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const dotPath = {} ;
module.exports = dotPath ;



const EMPTY_PATH = [] ;
const PROTO_POLLUTION_MESSAGE = 'This would cause prototype pollution' ;



function toPathArray( path ) {
	if ( Array.isArray( path ) ) {
		/*
		let i , iMax = path.length ;
		for ( i = 0 ; i < iMax ; i ++ ) {
			if ( typeof path[ i ] !== 'string' || typeof path[ i ] !== 'number' ) { path[ i ] = '' + path[ i ] ; }
		}
		//*/
		return path ;
	}

	if ( ! path ) { return EMPTY_PATH ; }
	if ( typeof path === 'string' ) {
		return path[ path.length - 1 ] === '.' ? path.slice( 0 , - 1 ).split( '.' ) : path.split( '.' ) ;
	}

	throw new TypeError( '[tree.dotPath]: the path argument should be a string or an array' ) ;
}

// Expose toPathArray()
dotPath.toPathArray = toPathArray ;



// Walk the tree using the path array.
function walk( object , pathArray , maxOffset = 0 ) {
	var index , indexMax , key ,
		pointer = object ;

	for ( index = 0 , indexMax = pathArray.length + maxOffset ; index < indexMax ; index ++ ) {
		key = pathArray[ index ] ;

		if ( typeof key === 'object' || key === '__proto__' || typeof pointer === 'function' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
		if ( ! pointer || typeof pointer !== 'object' ) { return undefined ; }

		pointer = pointer[ key ] ;
	}

	return pointer ;
}



// Walk the tree, create missing element: pave the path up to before the last part of the path.
// Return that before-the-last element.
// Object MUST be an object! no check are performed for the first step!
function pave( object , pathArray ) {
	var index , indexMax , key ,
		pointer = object ;

	for ( index = 0 , indexMax = pathArray.length - 1 ; index < indexMax ; index ++ ) {
		key = pathArray[ index ] ;

		if ( typeof key === 'object' || key === '__proto__' || typeof pointer[ key ] === 'function' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
		if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = {} ; }

		pointer = pointer[ key ] ;
	}

	return pointer ;
}



dotPath.get = ( object , path ) => walk( object , toPathArray( path ) ) ;



dotPath.set = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return ; }

	var pathArray = toPathArray( path ) ,
		key = pathArray[ pathArray.length - 1 ] ;

	if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }

	var pointer = pave( object , pathArray ) ;

	pointer[ key ] = value ;

	return value ;
} ;



dotPath.define = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return ; }

	var pathArray = toPathArray( path ) ,
		key = pathArray[ pathArray.length - 1 ] ;

	if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }

	var pointer = pave( object , pathArray ) ;

	if ( ! ( key in pointer ) ) { pointer[ key ] = value ; }

	return pointer[ key ] ;
} ;



dotPath.inc = ( object , path ) => {
	if ( ! object || typeof object !== 'object' ) { return ; }

	var pathArray = toPathArray( path ) ,
		key = pathArray[ pathArray.length - 1 ] ;

	if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }

	var pointer = pave( object , pathArray ) ;

	if ( typeof pointer[ key ] === 'number' ) { pointer[ key ] ++ ; }
	else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = 1 ; }

	return pointer[ key ] ;
} ;



dotPath.dec = ( object , path ) => {
	if ( ! object || typeof object !== 'object' ) { return ; }

	var pathArray = toPathArray( path ) ,
		key = pathArray[ pathArray.length - 1 ] ;

	if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }

	var pointer = pave( object , pathArray ) ;

	if ( typeof pointer[ key ] === 'number' ) { pointer[ key ] -- ; }
	else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = - 1 ; }

	return pointer[ key ] ;
} ;



dotPath.concat = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return ; }

	var pathArray = toPathArray( path ) ,
		key = pathArray[ pathArray.length - 1 ] ;

	if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }

	var pointer = pave( object , pathArray ) ;

	if ( ! pointer[ key ] ) { pointer[ key ] = value ; }
	else if ( Array.isArray( pointer[ key ] ) && Array.isArray( value ) ) {
		pointer[ key ] = pointer[ key ].concat( value ) ;
	}
	//else ? do nothing???

	return pointer[ key ] ;
} ;



dotPath.insert = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return ; }

	var pathArray = toPathArray( path ) ,
		key = pathArray[ pathArray.length - 1 ] ;

	if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }

	var pointer = pave( object , pathArray ) ;

	if ( ! pointer[ key ] ) { pointer[ key ] = value ; }
	else if ( Array.isArray( pointer[ key ] ) && Array.isArray( value ) ) {
		pointer[ key ] = value.concat( pointer[ key ] ) ;
	}
	//else ? do nothing???

	return pointer[ key ] ;
} ;



dotPath.delete = ( object , path ) => {
	var pathArray = toPathArray( path ) ,
		key = pathArray[ pathArray.length - 1 ] ;

	if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }

	var pointer = walk( object , pathArray , - 1 ) ;

	if ( ! pointer || typeof pointer !== 'object' || ! Object.hasOwn( pointer , key ) ) { return false ; }

	delete pointer[ key ] ;

	return true ;
} ;



dotPath.autoPush = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return ; }

	var pathArray = toPathArray( path ) ,
		key = pathArray[ pathArray.length - 1 ] ;

	if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }

	var pointer = pave( object , pathArray ) ;

	if ( pointer[ key ] === undefined ) { pointer[ key ] = value ; }
	else if ( Array.isArray( pointer[ key ] ) ) { pointer[ key ].push( value ) ; }
	else { pointer[ key ] = [ pointer[ key ] , value ] ; }

	return pointer[ key ] ;
} ;



dotPath.append = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return ; }

	var pathArray = toPathArray( path ) ,
		key = pathArray[ pathArray.length - 1 ] ;

	if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }

	var pointer = pave( object , pathArray ) ;

	if ( ! pointer[ key ] ) { pointer[ key ] = [ value ] ; }
	else if ( Array.isArray( pointer[ key ] ) ) { pointer[ key ].push( value ) ; }
	//else ? do nothing???

	return pointer[ key ] ;
} ;



dotPath.prepend = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return ; }

	var pathArray = toPathArray( path ) ,
		key = pathArray[ pathArray.length - 1 ] ;

	if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }

	var pointer = pave( object , pathArray ) ;

	if ( ! pointer[ key ] ) { pointer[ key ] = [ value ] ; }
	else if ( Array.isArray( pointer[ key ] ) ) { pointer[ key ].unshift( value ) ; }
	//else ? do nothing???

	return pointer[ key ] ;
} ;


},{}],141:[function(require,module,exports){
/*
	Tree Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



/*
	== Extend function ==
*/

/*
	options:
		* own: only copy own properties that are enumerable
		* nonEnum: copy non-enumerable properties as well, works only with own:true
		* descriptor: preserve property's descriptor
		* deep: boolean/Array/Set, if true perform a deep (recursive) extend, if it is an Array/Set of prototypes, only deep-copy
			objects of those prototypes
			(it is a replacement for deepFilter.whitelist which was removed in Tree Kit 0.6).
		* immutables: an Array/Set of immutable object's prototypes that are filtered out for deep-copy
			(it is a replacement for deepFilter.blacklist which was removed in Tree Kit 0.6).
		* maxDepth: used in conjunction with deep, when max depth is reached an exception is raised, default to 100 when
			the 'circular' option is off, or default to null if 'circular' is on
		* circular: boolean, circular references reconnection
		* move: boolean, move properties to target (delete properties from the sources)
		* preserve: boolean, existing properties in the target object are not overwritten
		* mask: boolean or number, reverse of 'preserve', only update existing properties in the target, do not create new keys,
			if its a number, the mask effect is only effective for the Nth element.
			E.g: .extend( {mask:2} , {} , object1 , object2 )
			So object1 extends the empty object like, but object2 do not create new keys not present in object1.
			With mask:true or mask:1, the mask behavior would apply at step 1 too, when object1 would try to extend the empty object,
			and since an empty object has no key, nothing would change, and the whole extend would return an empty object.
		* nofunc: skip functions
		* deepFunc: in conjunction with 'deep', this will process sources functions like objects rather than
			copying/referencing them directly into the source, thus, the result will not be a function, it forces 'deep'
		* proto: try to clone objects with the right prototype, using Object.create() or mutating it with Object.setPrototypeOf(),
			it forces option 'own'.
		* inherit: rather than mutating target prototype for source prototype like the 'proto' option does, here it is
			the source itself that IS the prototype for the target. Force option 'own' and disable 'proto'.
		* skipRoot: the prototype of the target root object is NOT mutated only if this option is set.
		* flat: extend into the target top-level only, compose name with the path of the source, force 'deep',
			disable 'unflat', 'proto', 'inherit'
		* unflat: assume sources are in the 'flat' format, expand all properties deeply into the target, disable 'flat'
*/
function extend( options , target , ... sources ) {
	var i , source , newTarget = false , length = sources.length ;

	if ( ! length ) { return target ; }

	if ( ! options || typeof options !== 'object' ) { options = {} ; }

	var runtime = { depth: 0 , prefix: '' } ;

	if ( options.deep ) {
		if ( Array.isArray( options.deep ) ) { options.deep = new Set( options.deep ) ; }
		else if ( ! ( options.deep instanceof Set ) ) { options.deep = true ; }
	}

	if ( options.immutables ) {
		if ( Array.isArray( options.immutables ) ) { options.immutables = new Set( options.immutables ) ; }
		else if ( ! ( options.immutables instanceof Set ) ) { delete options.immutables ; }
	}

	if ( ! options.maxDepth && options.deep && ! options.circular ) { options.maxDepth = 100 ; }

	if ( options.deepFunc ) { options.deep = true ; }

	// 'flat' option force 'deep'
	if ( options.flat ) {
		options.deep = true ;
		options.proto = false ;
		options.inherit = false ;
		options.unflat = false ;
		if ( typeof options.flat !== 'string' ) { options.flat = '.' ; }
	}

	if ( options.unflat ) {
		options.deep = false ;
		options.proto = false ;
		options.inherit = false ;
		options.flat = false ;
		if ( typeof options.unflat !== 'string' ) { options.unflat = '.' ; }
	}

	// If the prototype is applied, only owned properties should be copied
	if ( options.inherit ) { options.own = true ; options.proto = false ; }
	else if ( options.proto ) { options.own = true ; }

	if ( ! target || ( typeof target !== 'object' && typeof target !== 'function' ) ) {
		newTarget = true ;
	}

	if ( ! options.skipRoot && ( options.inherit || options.proto ) ) {
		for ( i = length - 1 ; i >= 0 ; i -- ) {
			source = sources[ i ] ;
			if ( source && ( typeof source === 'object' || typeof source === 'function' ) ) {
				if ( options.inherit ) {
					if ( newTarget ) { target = Object.create( source ) ; }
					else { Object.setPrototypeOf( target , source ) ; }
				}
				else if ( options.proto ) {
					if ( newTarget ) { target = Object.create( Object.getPrototypeOf( source ) ) ; }
					else { Object.setPrototypeOf( target , Object.getPrototypeOf( source ) ) ; }
				}

				break ;
			}
		}
	}
	else if ( newTarget ) {
		target = {} ;
	}

	runtime.references = { sources: [] , targets: [] } ;

	for ( i = 0 ; i < length ; i ++ ) {
		source = sources[ i ] ;
		if ( ! source || ( typeof source !== 'object' && typeof source !== 'function' ) ) { continue ; }
		extendOne( runtime , options , target , source , options.mask <= i + 1 ) ;
	}

	return target ;
}

module.exports = extend ;



function extendOne( runtime , options , target , source , mask ) {
	var sourceKeys , sourceKey ;

	// Max depth check
	if ( options.maxDepth && runtime.depth > options.maxDepth ) {
		throw new Error( '[tree] extend(): max depth reached(' + options.maxDepth + ')' ) ;
	}


	if ( options.circular ) {
		runtime.references.sources.push( source ) ;
		runtime.references.targets.push( target ) ;
	}

	// 'unflat' mode computing
	if ( options.unflat && runtime.depth === 0 ) {
		for ( sourceKey in source ) {
			runtime.unflatKeys = sourceKey.split( options.unflat ) ;
			runtime.unflatIndex = 0 ;
			runtime.unflatFullKey = sourceKey ;
			extendOneKV( runtime , options , target , source , runtime.unflatKeys[ runtime.unflatIndex ] , mask ) ;
		}

		delete runtime.unflatKeys ;
		delete runtime.unflatIndex ;
		delete runtime.unflatFullKey ;
	}
	else if ( options.own ) {
		if ( options.nonEnum ) { sourceKeys = Object.getOwnPropertyNames( source ) ; }
		else { sourceKeys = Object.keys( source ) ; }

		for ( sourceKey of sourceKeys ) {
			extendOneKV( runtime , options , target , source , sourceKey , mask ) ;
		}
	}
	else {
		for ( sourceKey in source ) {
			extendOneKV( runtime , options , target , source , sourceKey , mask ) ;
		}
	}
}



function extendOneKV( runtime , options , target , source , sourceKey , mask ) {
	// OMG, this DEPRECATED __proto__ shit is still alive and can be used to hack anything ><
	if ( sourceKey === '__proto__' ) { return ; }

	let sourceValue , sourceDescriptor , sourceValueProto ;

	if ( runtime.unflatKeys ) {
		if ( runtime.unflatIndex < runtime.unflatKeys.length - 1 ) {
			sourceValue = {} ;
		}
		else {
			sourceValue = source[ runtime.unflatFullKey ] ;
		}
	}
	else if ( options.descriptor ) {
		// If descriptor is on, get it now
		sourceDescriptor = Object.getOwnPropertyDescriptor( source , sourceKey ) ;
		sourceValue = sourceDescriptor.value ;
	}
	else {
		// We have to trigger an eventual getter only once
		sourceValue = source[ sourceKey ] ;
	}

	let targetKey = runtime.prefix + sourceKey ;

	// Do not copy if property is a function and we don't want them
	if ( options.nofunc && typeof sourceValue === 'function' ) { return ; }

	// Again, trigger an eventual getter only once
	let targetValue = target[ targetKey ] ;
	let targetValueIsObject = targetValue && ( typeof targetValue === 'object' || typeof targetValue === 'function' ) ;
	let sourceValueIsObject = sourceValue && ( typeof sourceValue === 'object' || typeof sourceValue === 'function' ) ;

	if (
		( options.deep || runtime.unflatKeys )
		&& sourceValue
		&& ( typeof sourceValue === 'object' || ( options.deepFunc && typeof sourceValue === 'function' ) )
		&& ( ! options.descriptor || ! sourceDescriptor.get )
		// not a condition we just cache sourceValueProto now... ok it's trashy ><
		&& ( ( sourceValueProto = Object.getPrototypeOf( sourceValue ) ) || true )
		&& ( ! ( options.deep instanceof Set ) || options.deep.has( sourceValueProto ) )
		&& ( ! options.immutables || ! options.immutables.has( sourceValueProto ) )
		&& ( ! options.preserve || targetValueIsObject )
		&& ( ! mask || targetValueIsObject )
	) {
		let indexOfSource = options.circular ? runtime.references.sources.indexOf( sourceValue ) : - 1 ;

		if ( options.flat ) {
			// No circular references reconnection when in 'flat' mode
			if ( indexOfSource >= 0 ) { return ; }

			extendOne(
				{
					depth: runtime.depth + 1 ,
					prefix: runtime.prefix + sourceKey + options.flat ,
					references: runtime.references
				} ,
				options , target , sourceValue , mask
			) ;
		}
		else {
			if ( indexOfSource >= 0 ) {
				// Circular references reconnection...
				targetValue = runtime.references.targets[ indexOfSource ] ;

				if ( options.descriptor ) {
					Object.defineProperty( target , targetKey , {
						value: targetValue ,
						enumerable: sourceDescriptor.enumerable ,
						writable: sourceDescriptor.writable ,
						configurable: sourceDescriptor.configurable
					} ) ;
				}
				else {
					target[ targetKey ] = targetValue ;
				}

				return ;
			}

			if ( ! targetValueIsObject || ! Object.hasOwn( target , targetKey ) ) {
				if ( Array.isArray( sourceValue ) ) { targetValue = [] ; }
				else if ( options.proto ) { targetValue = Object.create( sourceValueProto ) ; }
				else if ( options.inherit ) { targetValue = Object.create( sourceValue ) ; }
				else { targetValue = {} ; }

				if ( options.descriptor ) {
					Object.defineProperty( target , targetKey , {
						value: targetValue ,
						enumerable: sourceDescriptor.enumerable ,
						writable: sourceDescriptor.writable ,
						configurable: sourceDescriptor.configurable
					} ) ;
				}
				else {
					target[ targetKey ] = targetValue ;
				}
			}
			else if ( options.proto && Object.getPrototypeOf( targetValue ) !== sourceValueProto ) {
				Object.setPrototypeOf( targetValue , sourceValueProto ) ;
			}
			else if ( options.inherit && Object.getPrototypeOf( targetValue ) !== sourceValue ) {
				Object.setPrototypeOf( targetValue , sourceValue ) ;
			}

			if ( options.circular ) {
				runtime.references.sources.push( sourceValue ) ;
				runtime.references.targets.push( targetValue ) ;
			}

			if ( runtime.unflatKeys && runtime.unflatIndex < runtime.unflatKeys.length - 1 ) {
				// Finish unflatting this property
				let nextSourceKey = runtime.unflatKeys[ runtime.unflatIndex + 1 ] ;

				extendOneKV(
					{
						depth: runtime.depth ,	// keep the same depth
						unflatKeys: runtime.unflatKeys ,
						unflatIndex: runtime.unflatIndex + 1 ,
						unflatFullKey: runtime.unflatFullKey ,
						prefix: '' ,
						references: runtime.references
					} ,
					options , targetValue , source , nextSourceKey , mask
				) ;
			}
			else {
				// Recursively extends sub-object
				extendOne(
					{
						depth: runtime.depth + 1 ,
						prefix: '' ,
						references: runtime.references
					} ,
					options , targetValue , sourceValue , mask
				) ;
			}
		}
	}
	else if ( mask && ( targetValue === undefined || targetValueIsObject || sourceValueIsObject ) ) {
		// Do not create new value, and so do not delete source's properties that were not moved.
		// We also do not overwrite object with non-object, and we don't overwrite non-object with object (preserve hierarchy)
		return ;
	}
	else if ( options.preserve && targetValue !== undefined ) {
		// Do not overwrite, and so do not delete source's properties that were not moved
		return ;
	}
	else if ( ! options.inherit ) {
		if ( options.descriptor ) { Object.defineProperty( target , targetKey , sourceDescriptor ) ; }
		else { target[ targetKey ] = targetValue = sourceValue ; }
	}

	// Delete owned property of the source object
	if ( options.move ) { delete source[ sourceKey ] ; }
}


},{}],142:[function(require,module,exports){
/*
	Tree Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



/*
	DEPRECATED, moved to its own module: lazyness
*/

exports.defineLazyProperty = function defineLazyProperty( object , name , func ) {
	Object.defineProperty( object , name , {
		configurable: true ,
		enumerable: true ,
		get: function() {

			var value = func() ;

			Object.defineProperty( object , name , {
				configurable: true ,
				enumerable: true ,
				writable: false ,
				value: value
			} ) ;

			return value ;
		}
	} ) ;
} ;


},{}],143:[function(require,module,exports){
/*
	Tree Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const tree = require( './tree.js' ) ;
const util = require( 'util' ) ;



// Create and export
const masklib = {} ;
module.exports = masklib ;



/*
	== Mask-family class ==

	Recursively select values in the input object if the same path in the mask object is set.
*/

/*
	TODO:
	- negative mask
	- constraint check
	- Maskable object, like in csk-php
*/

masklib.Mask = function Mask() {
	throw new Error( 'Cannot create a tree.Mask() directly' ) ;
} ;



const maskDefaultOptions = {
	clone: false ,
	path: '<object>' ,
	pathSeparator: '.'
} ;



/*
	options:
		clone: the output clone the input rather than reference it
		pathSeperator: when expressing path, this is the separator
		leaf: a callback to exec for each mask leaf
		node? a callback to exec for each mask node
*/
masklib.createMask = function createMask( maskArgument , options ) {
	if ( maskArgument === null || typeof maskArgument !== 'object' ) {
		throw new TypeError( '[tree] .createMask() : Argument #1 should be an object' ) ;
	}

	if ( options !== null && typeof options === 'object' ) { options = tree.extend( null , {} , maskDefaultOptions , options ) ; }
	else { options = maskDefaultOptions ; }

	var mask = Object.create( masklib.Mask.prototype , {
		__options__: { value: options , writable: true  }
	} ) ;

	tree.extend( null , mask , maskArgument ) ;

	return mask ;
} ;



// Apply the mask to an input tree
masklib.Mask.prototype.applyTo = function applyTo( input , context , contextOverideDefault ) {
	// Arguments checking
	if ( input === null || typeof input !== 'object' ) {
		throw new TypeError( '[tree] .applyTo() : Argument #1 should be an object' ) ;
	}

	if ( contextOverideDefault ) {
		context = tree.extend( null ,
			{
				mask: this ,
				options: this.__options__ ,
				path: this.__options__.path
			} ,
			context
		) ;
	}
	else if ( context === undefined ) {
		context = {
			mask: this ,
			options: this.__options__ ,
			path: this.__options__.path
		} ;
	}


	// Init
	//console.log( context ) ;
	var result , nextPath , output ,
		i , key , maskValue ,
		maskKeyList = Object.keys( context.mask ) ,
		j , inputKey , inputValue , inputKeyList ;

	if ( Array.isArray( input ) ) { output = [] ; }
	else { output = {} ; }


	// Iterate through mask properties
	for ( i = 0 ; i < maskKeyList.length ; i ++ ) {
		key = maskKeyList[ i ] ;
		maskValue = context.mask[ key ] ;

		//console.log( '\nnext loop: ' , key , maskValue ) ;

		// The special key * is a wildcard, it match everything
		if ( key === '*' ) {
			//console.log( 'wildcard' ) ;
			inputKeyList = Object.keys( input ) ;

			for ( j = 0 ; j < inputKeyList.length ; j ++ ) {
				inputKey = inputKeyList[ j ] ;
				inputValue = input[ inputKey ] ;

				//console.log( '*: ' , inputKey ) ;
				nextPath = context.path + context.options.pathSeparator + inputKey ;

				// If it is an array or object, recursively check it
				if ( maskValue !== null && typeof maskValue === 'object' ) {
					if ( inputValue !== null && typeof inputValue === 'object' ) {
						if ( inputValue instanceof masklib.Mask ) {
							output[ inputKey ] = inputValue.applyTo( inputValue , { path: nextPath } , true ) ;
						}
						else {
							output[ inputKey ] = this.applyTo( inputValue , tree.extend( null , {} , context , { mask: maskValue , path: nextPath } ) ) ;
						}
					}
					else if ( typeof context.options.leaf === 'function' ) {
						output[ inputKey ] = this.applyTo( {} , tree.extend( null , {} , context , { mask: maskValue , path: nextPath } ) ) ;
					}
				}
				else if ( maskValue !== null && typeof context.options.leaf === 'function' ) {
					//console.log( 'leaf callback' ) ;
					result = context.options.leaf( input , inputKey , maskValue , nextPath ) ;
					if ( ! ( result instanceof Error ) ) { output[ inputKey ] = result ; }
				}
				else if ( context.options.clone && ( inputValue !== null && typeof inputValue === 'object' ) ) {
					output[ inputKey ] = tree.extend( { deep: true } , {} , inputValue ) ;
				}
				else {
					output[ inputKey ] = inputValue ;
				}
			}

			continue ;
		}


		nextPath = context.path + context.options.pathSeparator + key ;

		// If it is an object, recursively check it
		//if ( maskValue instanceof masklib.Mask )
		if ( maskValue !== null && typeof maskValue === 'object' ) {
			//console.log( 'sub' ) ;

			if ( Object.hasOwn( input , key ) && input[ key ] !== null && typeof input[ key ] === 'object' ) {
				//console.log( 'recursive call' ) ;

				if ( input.key instanceof masklib.Mask ) {
					output[ key ] = input.key.applyTo( input[ key ] , { path: nextPath } , true ) ;
				}
				else {
					output[ key ] = this.applyTo( input[ key ] , tree.extend( null , {} , context , { mask: maskValue , path: nextPath } ) ) ;
				}
			}
			// recursive call only if there are callback
			else if ( context.options.leaf ) {
				//console.log( 'recursive call' ) ;
				output[ key ] = this.applyTo( {} , tree.extend( null , {} , context , { mask: maskValue , path: nextPath } ) ) ;
			}
		}
		// If mask exists, add the key
		else if ( Object.hasOwn( input , key ) ) {
			//console.log( 'property found' ) ;

			if ( maskValue !== undefined && typeof context.options.leaf === 'function' ) {
				//console.log( 'leaf callback' ) ;
				result = context.options.leaf( input , key , maskValue , nextPath ) ;
				if ( ! ( result instanceof Error ) ) { output[ key ] = result ; }
			}
			else if ( context.options.clone && ( input[ key ] !== null && typeof input[ key ] === 'object' ) ) {
				output[ key ] = tree.extend( { deep: true } , {} , input[ key ] ) ;
			}
			else {
				output[ key ] = input[ key ] ;
			}
		}
		else if ( maskValue !== undefined && typeof context.options.leaf === 'function' ) {
			//console.log( 'leaf callback' ) ;
			result = context.options.leaf( input , key , maskValue , nextPath ) ;
			if ( ! ( result instanceof Error ) ) { output[ key ] = result ; }
		}
	}

	return output ;
} ;



// InverseMask: create an output tree from the input, by excluding properties of the mask

masklib.InverseMask = function InverseMask() {
	throw new Error( 'Cannot create a tree.InverseMask() directly' ) ;
} ;

util.inherits( masklib.InverseMask , masklib.Mask ) ;



/*
	options:
		clone: the output clone the input rather than reference it
		pathSeperator: when expressing path, this is the separator
*/
masklib.createInverseMask = function createInverseMask( maskArgument , options ) {
	if ( maskArgument === null || typeof maskArgument !== 'object' ) {
		throw new TypeError( '[tree] .createInverseMask() : Argument #1 should be an object' ) ;
	}

	if ( options !== null && typeof options === 'object' ) { options = tree.extend( null , {} , maskDefaultOptions , options ) ; }
	else { options = maskDefaultOptions ; }

	var mask = Object.create( masklib.InverseMask.prototype , {
		__options__: { value: options , writable: true  }
	} ) ;

	tree.extend( null , mask , maskArgument ) ;

	return mask ;
} ;



// Apply the mask to an input tree
masklib.InverseMask.prototype.applyTo = function applyTo( input , context , contextOverideDefault ) {
	// Arguments checking
	if ( input === null || typeof input !== 'object' ) {
		throw new TypeError( '[tree] .applyTo() : Argument #1 should be an object' ) ;
	}

	if ( contextOverideDefault ) {
		context = tree.extend( null ,
			{
				mask: this ,
				options: this.__options__ ,
				path: this.__options__.path
			} ,
			context
		) ;
	}
	else if ( context === undefined ) {
		context = {
			mask: this ,
			options: this.__options__ ,
			path: this.__options__.path
		} ;
	}


	// Init
	//console.log( context ) ;
	var nextPath , output ,
		i , key , maskValue ,
		maskKeyList = Object.keys( context.mask ) ,
		j , inputKey , inputValue , inputKeyList ;

	if ( Array.isArray( input ) ) { output = tree.extend( { deep: true } , [] , input ) ; }
	else { output = tree.extend( { deep: true } , {} , input ) ; }

	//console.log( output ) ;

	// Iterate through mask properties
	for ( i = 0 ; i < maskKeyList.length ; i ++ ) {
		key = maskKeyList[ i ] ;
		maskValue = context.mask[ key ] ;

		//console.log( '\nnext loop: ' , key , maskValue ) ;

		// The special key * is a wildcard, it match everything
		if ( key === '*' ) {
			//console.log( 'wildcard' ) ;
			inputKeyList = Object.keys( input ) ;

			for ( j = 0 ; j < inputKeyList.length ; j ++ ) {
				inputKey = inputKeyList[ j ] ;
				inputValue = input[ inputKey ] ;

				//console.log( '*: ' , inputKey ) ;
				nextPath = context.path + context.options.pathSeparator + inputKey ;

				// If it is an array or object, recursively check it
				if ( maskValue !== null && typeof maskValue === 'object' ) {
					if ( inputValue !== null && typeof inputValue === 'object' ) {
						if ( inputValue instanceof masklib.Mask ) {
							output[ inputKey ] = inputValue.applyTo( inputValue , { path: nextPath } , true ) ;
						}
						else {
							output[ inputKey ] = this.applyTo( inputValue , tree.extend( null , {} , context , { mask: maskValue , path: nextPath } ) ) ;
						}
					}
				}
				else {
					delete output[ inputKey ] ;
				}
			}

			continue ;
		}


		nextPath = context.path + context.options.pathSeparator + key ;

		// If it is an object, recursively check it
		//if ( maskValue instanceof masklib.Mask )
		if ( maskValue !== null && typeof maskValue === 'object' ) {
			//console.log( 'sub' ) ;

			if ( Object.hasOwn( input , key ) && input[ key ] !== null && typeof input[ key ] === 'object' ) {
				//console.log( 'recursive call' ) ;

				if ( input.key instanceof masklib.Mask ) {
					output[ key ] = input.key.applyTo( input[ key ] , { path: nextPath } , true ) ;
				}
				else {
					output[ key ] = this.applyTo( input[ key ] , tree.extend( null , {} , context , { mask: maskValue , path: nextPath } ) ) ;
				}
			}
		}
		// If mask exists, remove the key
		else if ( Object.hasOwn( input , key ) ) {
			delete output[ key ] ;
		}
	}

	return output ;
} ;


},{"./tree.js":145,"util":214}],144:[function(require,module,exports){
/*
	Tree Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const treePath = {} ;
module.exports = treePath ;



const PROTO_POLLUTION_MESSAGE = 'This would cause prototype pollution' ;



treePath.op = function( type , object , path , value ) {
	var i , parts , last , pointer , key , isArray = false , pathArrayMode = false , isGenericSet , canBeEmpty = true ;

	if ( ! object || typeof object !== 'object' ) { return ; }

	if ( typeof path === 'string' ) {
		// Split the path into parts
		if ( path ) { parts = path.match( /([.#[\]]|[^.#[\]]+)/g ) ; }
		else { parts = [ '' ] ; }

		if ( parts[ 0 ] === '.' ) { parts.unshift( '' ) ; }
		if ( parts[ parts.length - 1 ] === '.' ) { parts.push( '' ) ; }
	}
	else if ( Array.isArray( path ) ) {
		parts = path ;
		pathArrayMode = true ;
		/*
		for ( i = 0 ; i < parts.length ; i ++ ) {
			if ( typeof parts[ i ] !== 'string' || typeof parts[ i ] !== 'number' ) { parts[ i ] = '' + parts[ i ] ; }
		}
		//*/
	}
	else {
		throw new TypeError( '[tree.path] .' + type + '(): the path argument should be a string or an array' ) ;
	}

	switch ( type ) {
		case 'get' :
		case 'delete' :
			isGenericSet = false ;
			break ;
		case 'set' :
		case 'define' :
		case 'inc' :
		case 'dec' :
		case 'append' :
		case 'prepend' :
		case 'concat' :
		case 'insert' :
		case 'autoPush' :
			isGenericSet = true ;
			break ;
		default :
			throw new TypeError( "[tree.path] .op(): wrong type of operation '" + type + "'" ) ;
	}

	//console.log( parts ) ;
	// The pointer start at the object's root
	pointer = object ;

	last = parts.length - 1 ;

	for ( i = 0 ; i <= last ; i ++ ) {
		if ( pathArrayMode ) {
			if ( key === undefined ) {
				key = parts[ i ] ;
				if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
				continue ;
			}

			if ( typeof pointer[ key ] === 'function' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
			if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) {
				if ( ! isGenericSet ) { return undefined ; }
				pointer[ key ] = {} ;
			}

			pointer = pointer[ key ] ;
			key = parts[ i ] ;
			if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
			continue ;
		}
		else if ( parts[ i ] === '.' ) {
			isArray = false ;

			if ( key === undefined ) {
				if ( ! canBeEmpty ) {
					canBeEmpty = true ;
					continue ;
				}

				key = '' ;
			}

			if ( typeof pointer[ key ] === 'function' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
			if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) {
				if ( ! isGenericSet ) { return undefined ; }
				pointer[ key ] = {} ;
			}

			pointer = pointer[ key ] ;
			canBeEmpty = true ;

			continue ;
		}
		else if ( parts[ i ] === '#' || parts[ i ] === '[' ) {
			isArray = true ;
			canBeEmpty = false ;

			if ( key === undefined ) {
				// The root element cannot be altered, we are in trouble if an array is expected but we have only a regular object.
				if ( ! Array.isArray( pointer ) ) { return undefined ; }
				continue ;
			}

			if ( typeof pointer[ key ] === 'function' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
			if ( ! pointer[ key ] || ! Array.isArray( pointer[ key ] ) ) {
				if ( ! isGenericSet ) { return undefined ; }
				pointer[ key ] = [] ;
			}

			pointer = pointer[ key ] ;

			continue ;
		}
		else if ( parts[ i ] === ']' ) {
			// Closing bracket: do nothing
			canBeEmpty = false ;
			continue ;
		}

		canBeEmpty = false ;

		if ( ! isArray ) {
			key = parts[ i ] ;
			if ( typeof key === 'object' || key === '__proto__' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
			continue ;
		}

		switch ( parts[ i ] ) {
			case 'length' :
				key = 'length' ;
				break ;

			// Pseudo-key
			case 'first' :
				key = 0 ;
				break ;
			case 'last' :
				key = pointer.length - 1 ;
				if ( key < 0 ) { key = 0 ; }
				break ;
			case 'next' :
				if ( ! isGenericSet ) { return undefined ; }
				key = pointer.length ;
				break ;
			case 'insert' :
				if ( ! isGenericSet ) { return undefined ; }
				pointer.unshift( undefined ) ;
				key = 0 ;
				break ;

			// default = number
			default :
				// Convert the string key to a numerical index
				key = parseInt( parts[ i ] , 10 ) ;
		}
	}

	switch ( type ) {
		case 'get' :
			return pointer[ key ] ;
		case 'delete' :
			if ( isArray && typeof key === 'number' ) { pointer.splice( key , 1 ) ; }
			else { delete pointer[ key ] ; }
			return ;
		case 'set' :
			pointer[ key ] = value ;
			return pointer[ key ] ;
		case 'define' :
			// define: set only if it doesn't exist
			if ( ! ( key in pointer ) ) { pointer[ key ] = value ; }
			return pointer[ key ] ;
		case 'inc' :
			if ( typeof pointer[ key ] === 'number' ) { pointer[ key ] ++ ; }
			else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = 1 ; }
			return pointer[ key ] ;
		case 'dec' :
			if ( typeof pointer[ key ] === 'number' ) { pointer[ key ] -- ; }
			else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = - 1 ; }
			return pointer[ key ] ;
		case 'append' :
			if ( ! pointer[ key ] ) { pointer[ key ] = [ value ] ; }
			else if ( Array.isArray( pointer[ key ] ) ) { pointer[ key ].push( value ) ; }
			//else ? do nothing???
			return pointer[ key ] ;
		case 'prepend' :
			if ( ! pointer[ key ] ) { pointer[ key ] = [ value ] ; }
			else if ( Array.isArray( pointer[ key ] ) ) { pointer[ key ].unshift( value ) ; }
			//else ? do nothing???
			return pointer[ key ] ;
		case 'concat' :
			if ( ! pointer[ key ] ) { pointer[ key ] = value ; }
			else if ( Array.isArray( pointer[ key ] ) && Array.isArray( value ) ) {
				pointer[ key ] = pointer[ key ].concat( value ) ;
			}
			//else ? do nothing???
			return pointer[ key ] ;
		case 'insert' :
			if ( ! pointer[ key ] ) { pointer[ key ] = value ; }
			else if ( Array.isArray( pointer[ key ] ) && Array.isArray( value ) ) {
				pointer[ key ] = value.concat( pointer[ key ] ) ;
			}
			//else ? do nothing???
			return pointer[ key ] ;
		case 'autoPush' :
			if ( pointer[ key ] === undefined ) { pointer[ key ] = value ; }
			else if ( Array.isArray( pointer[ key ] ) ) { pointer[ key ].push( value ) ; }
			else { pointer[ key ] = [ pointer[ key ] , value ] ; }
			return pointer[ key ] ;
	}
} ;



// get, set and delete use the same op() function
treePath.get = treePath.op.bind( undefined , 'get' ) ;
treePath.delete = treePath.op.bind( undefined , 'delete' ) ;
treePath.set = treePath.op.bind( undefined , 'set' ) ;
treePath.define = treePath.op.bind( undefined , 'define' ) ;
treePath.inc = treePath.op.bind( undefined , 'inc' ) ;
treePath.dec = treePath.op.bind( undefined , 'dec' ) ;
treePath.append = treePath.op.bind( undefined , 'append' ) ;
treePath.prepend = treePath.op.bind( undefined , 'prepend' ) ;
treePath.concat = treePath.op.bind( undefined , 'concat' ) ;
treePath.insert = treePath.op.bind( undefined , 'insert' ) ;
treePath.autoPush = treePath.op.bind( undefined , 'autoPush' ) ;



// Prototype used for object creation, so they can be created with Object.create( tree.path.prototype )
treePath.prototype = {
	get: function( path ) { return treePath.get( this , path ) ; } ,
	delete: function( path ) { return treePath.delete( this , path ) ; } ,
	set: function( path , value ) { return treePath.set( this , path , value ) ; } ,
	define: function( path , value ) { return treePath.define( this , path , value ) ; } ,
	inc: function( path , value ) { return treePath.inc( this , path , value ) ; } ,
	dec: function( path , value ) { return treePath.dec( this , path , value ) ; } ,
	append: function( path , value ) { return treePath.append( this , path , value ) ; } ,
	prepend: function( path , value ) { return treePath.prepend( this , path , value ) ; } ,
	concat: function( path , value ) { return treePath.concat( this , path , value ) ; } ,
	insert: function( path , value ) { return treePath.insert( this , path , value ) ; } ,
	autoPush: function( path , value ) { return treePath.autoPush( this , path , value ) ; }
} ;



// Upgrade an object so it can support get, set and delete at its root
treePath.upgrade = function( object ) {
	Object.defineProperties( object , {
		get: { value: treePath.op.bind( undefined , 'get' , object ) } ,
		delete: { value: treePath.op.bind( undefined , 'delete' , object ) } ,
		set: { value: treePath.op.bind( undefined , 'set' , object ) } ,
		define: { value: treePath.op.bind( undefined , 'define' , object ) } ,
		inc: { value: treePath.op.bind( undefined , 'inc' , object ) } ,
		dec: { value: treePath.op.bind( undefined , 'dec' , object ) } ,
		append: { value: treePath.op.bind( undefined , 'append' , object ) } ,
		prepend: { value: treePath.op.bind( undefined , 'prepend' , object ) } ,
		concat: { value: treePath.op.bind( undefined , 'concat' , object ) } ,
		insert: { value: treePath.op.bind( undefined , 'insert' , object ) } ,
		autoPush: { value: treePath.op.bind( undefined , 'autoPush' , object ) }
	} ) ;
} ;


},{}],145:[function(require,module,exports){
/*
	Tree Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const tree = {} ;
module.exports = tree ;



Object.assign( tree ,
	{
		extend: require( './extend.js' ) ,
		clone: require( './clone.js' ) ,
		path: require( './path.js' ) ,
		dotPath: require( './dotPath.js' ) ,
		wildDotPath: require( './wildDotPath.js' )
	} ,
	require( './lazy.js' ) ,
	require( './diff.js' ) ,
	require( './mask.js' ) ,
	require( './arrayLike.js' )
) ;


},{"./arrayLike.js":137,"./clone.js":138,"./diff.js":139,"./dotPath.js":140,"./extend.js":141,"./lazy.js":142,"./mask.js":143,"./path.js":144,"./wildDotPath.js":146}],146:[function(require,module,exports){
/*
	Tree Kit

	Copyright (c) 2014 - 2021 Cédric Ronvel

	The MIT License (MIT)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
	SOFTWARE.
*/

"use strict" ;



const wildDotPath = {} ;
module.exports = wildDotPath ;



const EMPTY_PATH = [] ;
const PROTO_POLLUTION_MESSAGE = 'This would cause prototype pollution' ;



function toPathArray( path ) {
	if ( Array.isArray( path ) ) {
		/*
		let i , iMax = path.length ;
		for ( i = 0 ; i < iMax ; i ++ ) {
			if ( typeof path[ i ] !== 'string' || typeof path[ i ] !== 'number' ) { path[ i ] = '' + path[ i ] ; }
		}
		//*/
		return path ;
	}

	if ( ! path ) { return EMPTY_PATH ; }
	if ( typeof path === 'string' ) {
		return path[ path.length - 1 ] === '.' ? path.slice( 0 , - 1 ).split( '.' ) : path.split( '.' ) ;
	}

	throw new TypeError( '[tree.wildDotPath]: the path argument should be a string or an array' ) ;
}

// Expose toPathArray()
wildDotPath.toPathArray = toPathArray ;



// Walk the tree and return an array of values.
function wildWalk( object , pathArray , maxOffset = 0 , index = 0 , result = [] ) {
	var indexMax , key ,
		pointer = object ;

	for ( indexMax = pathArray.length + maxOffset ; index < indexMax ; index ++ ) {
		key = pathArray[ index ] ;

		if ( typeof key === 'object' || key === '__proto__' || typeof pointer === 'function' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
		if ( ! pointer || typeof pointer !== 'object' ) { return result ; }

		if ( key === '*' ) {
			for ( let subPointer of Array.isArray( pointer ) ? pointer : Object.values( pointer ) ) {
				wildWalk( subPointer , pathArray , maxOffset , index + 1 , result ) ;
			}

			return result ;
		}

		pointer = pointer[ key ] ;
	}

	result.push( pointer ) ;

	return result ;
}



const VALUE_LIST_MODE = 0 ;
const PATH_LIST_MODE = 1 ;
const PATH_VALUE_MAP_MODE = 2 ;

// Same than wildWalk, but either return an array of path, or an object of path => value.
function wildWalkPathValue( object , pathArray , resultMode , maxOffset = 0 , index = 0 , path = '' , result = null ) {
	if ( ! result ) {
		result = resultMode === PATH_VALUE_MAP_MODE ? {} : [] ;
	}

	var indexMax , key ,
		pointer = object ;

	for ( indexMax = pathArray.length + maxOffset ; index < indexMax ; index ++ ) {
		key = pathArray[ index ] ;

		if ( typeof key === 'object' || key === '__proto__' || typeof pointer === 'function' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
		if ( ! pointer || typeof pointer !== 'object' ) { return result ; }

		if ( key === '*' ) {
			if ( Array.isArray( pointer ) ) {
				let subKey = 0 ;
				for ( let subPointer of pointer ) {
					wildWalkPathValue( subPointer , pathArray , resultMode , maxOffset , index + 1 , path ? path + '.' + subKey : subKey , result ) ;
					subKey ++ ;
				}
			}
			else {
				for ( let [ subKey , subPointer ] of Object.entries( pointer ) ) {
					wildWalkPathValue( subPointer , pathArray , resultMode , maxOffset , index + 1 , path ? path + '.' + subKey : subKey , result ) ;
				}
			}

			return result ;
		}

		path = path ? path + '.' + key : key ;
		pointer = pointer[ key ] ;
	}

	if ( resultMode === VALUE_LIST_MODE ) {
		result.push( pointer ) ;
	}
	else if ( resultMode === PATH_LIST_MODE ) {
		result.push( path ) ;
	}
	else if ( resultMode === PATH_VALUE_MAP_MODE ) {
		result[ path ] = pointer ;
	}

	return result ;
}



// Get with wildcard, return an array
wildDotPath.get = wildDotPath.getValues = ( object , path ) => wildWalk( object , toPathArray( path ) ) ;
wildDotPath.getPaths = ( object , path ) => wildWalkPathValue( object , toPathArray( path ) , PATH_LIST_MODE ) ;
wildDotPath.getPathValueMap = ( object , path ) => wildWalkPathValue( object , toPathArray( path ) , PATH_VALUE_MAP_MODE ) ;



// Walk and pave the tree and exec a hook at leaf.
// Object MUST be an object! no check are performed for the first step!
function wildWalkPave( object , pathArray , leafHookFn , leafHookArg , index = 0 ) {
	var indexMax , key ,
		pointer = object ;

	for ( indexMax = pathArray.length - 1 ; index < indexMax ; index ++ ) {
		key = pathArray[ index ] ;

		if ( typeof key === 'object' || key === '__proto__' || typeof pointer === 'function' ) { throw new Error( PROTO_POLLUTION_MESSAGE ) ; }
		if ( ! pointer || typeof pointer !== 'object' ) { return 0 ; }

		if ( key === '*' ) {
			let count = 0 ;
			for ( let subPointer of Array.isArray( pointer ) ? pointer : Object.values( pointer ) ) {
				count += wildWalkPave( subPointer , pathArray , leafHookFn , leafHookArg , index + 1 ) ;
			}

			return count ;
		}

		// Pave
		if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = {} ; }

		pointer = pointer[ key ] ;
	}

	key = pathArray[ index ] ;	// last key
	return leafHookFn( pointer , key , leafHookArg ) ;
}



const SET_HOOK = ( pointer , key , value ) => {
	pointer[ key ] = value ;
	return 1 ;
} ;

wildDotPath.set = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return 0 ; }
	return wildWalkPave( object , toPathArray( path ) , SET_HOOK , value ) ;
} ;



const DEFINE_HOOK = ( pointer , key , value ) => {
	if ( key in pointer ) { return 0 ; }
	pointer[ key ] = value ;
	return 1 ;
} ;

wildDotPath.define = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return 0 ; }
	return wildWalkPave( object , toPathArray( path ) , DEFINE_HOOK , value ) ;
} ;



const INC_HOOK = ( pointer , key ) => {
	if ( typeof pointer[ key ] === 'number' ) { pointer[ key ] ++ ; }
	else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = 1 ; }
	return 1 ;
} ;

wildDotPath.inc = ( object , path ) => {
	if ( ! object || typeof object !== 'object' ) { return 0 ; }
	return wildWalkPave( object , toPathArray( path ) , INC_HOOK ) ;
} ;



const DEC_HOOK = ( pointer , key ) => {
	if ( typeof pointer[ key ] === 'number' ) { pointer[ key ] -- ; }
	else if ( ! pointer[ key ] || typeof pointer[ key ] !== 'object' ) { pointer[ key ] = - 1 ; }
	return 1 ;
} ;

wildDotPath.dec = ( object , path ) => {
	if ( ! object || typeof object !== 'object' ) { return 0 ; }
	return wildWalkPave( object , toPathArray( path ) , DEC_HOOK ) ;
} ;



const CONCAT_HOOK = ( pointer , key , value ) => {
	if ( ! pointer[ key ] ) {
		pointer[ key ] = value ;
	}
	else if ( Array.isArray( pointer[ key ] ) && Array.isArray( value ) ) {
		pointer[ key ] = pointer[ key ].concat( value ) ;
	}

	return 1 ;
} ;

wildDotPath.concat = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return 0 ; }
	return wildWalkPave( object , toPathArray( path ) , CONCAT_HOOK , value ) ;
} ;



const INSERT_HOOK = ( pointer , key , value ) => {
	if ( ! pointer[ key ] ) {
		pointer[ key ] = value ;
	}
	else if ( Array.isArray( pointer[ key ] ) && Array.isArray( value ) ) {
		pointer[ key ] = value.concat( pointer[ key ] ) ;
	}

	return 1 ;
} ;

wildDotPath.insert = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return 0 ; }
	return wildWalkPave( object , toPathArray( path ) , INSERT_HOOK , value ) ;
} ;



const DELETE_HOOK = ( pointer , key ) => {
	if ( ! Object.hasOwn( pointer , key ) ) { return 0 ; }
	delete pointer[ key ] ;
	return 1 ;
} ;

wildDotPath.delete = ( object , path ) => {
	if ( ! object || typeof object !== 'object' ) { return 0 ; }
	return wildWalkPave( object , toPathArray( path ) , DELETE_HOOK ) ;
} ;



const AUTOPUSH_HOOK = ( pointer , key , value ) => {
	if ( pointer[ key ] === undefined ) { pointer[ key ] = value ; }
	else if ( Array.isArray( pointer[ key ] ) ) { pointer[ key ].push( value ) ; }
	else { pointer[ key ] = [ pointer[ key ] , value ] ; }

	return 1 ;
} ;

wildDotPath.autoPush = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return 0 ; }
	return wildWalkPave( object , toPathArray( path ) , AUTOPUSH_HOOK , value ) ;
} ;



const APPEND_HOOK = ( pointer , key , value ) => {
	if ( ! pointer[ key ] ) { pointer[ key ] = [ value ] ; }
	else if ( Array.isArray( pointer[ key ] ) ) { pointer[ key ].push( value ) ; }
	return 1 ;
} ;

wildDotPath.append = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return 0 ; }
	return wildWalkPave( object , toPathArray( path ) , APPEND_HOOK , value ) ;
} ;



const PREPEND_HOOK = ( pointer , key , value ) => {
	if ( ! pointer[ key ] ) { pointer[ key ] = [ value ] ; }
	else if ( Array.isArray( pointer[ key ] ) ) { pointer[ key ].unshift( value ) ; }
	return 1 ;
} ;

wildDotPath.prepend = ( object , path , value ) => {
	if ( ! object || typeof object !== 'object' ) { return 0 ; }
	return wildWalkPave( object , toPathArray( path ) , PREPEND_HOOK , value ) ;
} ;


},{}],147:[function(require,module,exports){
"use strict"

function unique_pred(list, compare) {
  var ptr = 1
    , len = list.length
    , a=list[0], b=list[0]
  for(var i=1; i<len; ++i) {
    b = a
    a = list[i]
    if(compare(a, b)) {
      if(i === ptr) {
        ptr++
        continue
      }
      list[ptr++] = a
    }
  }
  list.length = ptr
  return list
}

function unique_eq(list) {
  var ptr = 1
    , len = list.length
    , a=list[0], b = list[0]
  for(var i=1; i<len; ++i, b=a) {
    b = a
    a = list[i]
    if(a !== b) {
      if(i === ptr) {
        ptr++
        continue
      }
      list[ptr++] = a
    }
  }
  list.length = ptr
  return list
}

function unique(list, compare, sorted) {
  if(list.length === 0) {
    return list
  }
  if(compare) {
    if(!sorted) {
      list.sort(compare)
    }
    return unique_pred(list, compare)
  }
  if(!sorted) {
    list.sort()
  }
  return unique_eq(list)
}

module.exports = unique

},{}],148:[function(require,module,exports){

},{}],149:[function(require,module,exports){

/**
 * Array#filter.
 *
 * @param {Array} arr
 * @param {Function} fn
 * @param {Object=} self
 * @return {Array}
 * @throw TypeError
 */

module.exports = function (arr, fn, self) {
  if (arr.filter) return arr.filter(fn, self);
  if (void 0 === arr || null === arr) throw new TypeError;
  if ('function' != typeof fn) throw new TypeError;
  var ret = [];
  for (var i = 0; i < arr.length; i++) {
    if (!hasOwn.call(arr, i)) continue;
    var val = arr[i];
    if (fn.call(self, val, i, arr)) ret.push(val);
  }
  return ret;
};

var hasOwn = Object.prototype.hasOwnProperty;

},{}],150:[function(require,module,exports){
(function (global){(function (){
'use strict';

var objectAssign = require('object-assign');

// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js
// original notice:

/*!
 * The buffer module from node.js, for the browser.
 *
 * @author   Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
 * @license  MIT
 */
function compare(a, b) {
  if (a === b) {
    return 0;
  }

  var x = a.length;
  var y = b.length;

  for (var i = 0, len = Math.min(x, y); i < len; ++i) {
    if (a[i] !== b[i]) {
      x = a[i];
      y = b[i];
      break;
    }
  }

  if (x < y) {
    return -1;
  }
  if (y < x) {
    return 1;
  }
  return 0;
}
function isBuffer(b) {
  if (global.Buffer && typeof global.Buffer.isBuffer === 'function') {
    return global.Buffer.isBuffer(b);
  }
  return !!(b != null && b._isBuffer);
}

// based on node assert, original notice:
// NB: The URL to the CommonJS spec is kept just for tradition.
//     node-assert has evolved a lot since then, both in API and behavior.

// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
//
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
//
// Originally from narwhal.js (http://narwhaljs.org)
// Copyright (c) 2009 Thomas Robinson <280north.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

var util = require('util/');
var hasOwn = Object.prototype.hasOwnProperty;
var pSlice = Array.prototype.slice;
var functionsHaveNames = (function () {
  return function foo() {}.name === 'foo';
}());
function pToString (obj) {
  return Object.prototype.toString.call(obj);
}
function isView(arrbuf) {
  if (isBuffer(arrbuf)) {
    return false;
  }
  if (typeof global.ArrayBuffer !== 'function') {
    return false;
  }
  if (typeof ArrayBuffer.isView === 'function') {
    return ArrayBuffer.isView(arrbuf);
  }
  if (!arrbuf) {
    return false;
  }
  if (arrbuf instanceof DataView) {
    return true;
  }
  if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) {
    return true;
  }
  return false;
}
// 1. The assert module provides functions that throw
// AssertionError's when particular conditions are not met. The
// assert module must conform to the following interface.

var assert = module.exports = ok;

// 2. The AssertionError is defined in assert.
// new assert.AssertionError({ message: message,
//                             actual: actual,
//                             expected: expected })

var regex = /\s*function\s+([^\(\s]*)\s*/;
// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js
function getName(func) {
  if (!util.isFunction(func)) {
    return;
  }
  if (functionsHaveNames) {
    return func.name;
  }
  var str = func.toString();
  var match = str.match(regex);
  return match && match[1];
}
assert.AssertionError = function AssertionError(options) {
  this.name = 'AssertionError';
  this.actual = options.actual;
  this.expected = options.expected;
  this.operator = options.operator;
  if (options.message) {
    this.message = options.message;
    this.generatedMessage = false;
  } else {
    this.message = getMessage(this);
    this.generatedMessage = true;
  }
  var stackStartFunction = options.stackStartFunction || fail;
  if (Error.captureStackTrace) {
    Error.captureStackTrace(this, stackStartFunction);
  } else {
    // non v8 browsers so we can have a stacktrace
    var err = new Error();
    if (err.stack) {
      var out = err.stack;

      // try to strip useless frames
      var fn_name = getName(stackStartFunction);
      var idx = out.indexOf('\n' + fn_name);
      if (idx >= 0) {
        // once we have located the function frame
        // we need to strip out everything before it (and its line)
        var next_line = out.indexOf('\n', idx + 1);
        out = out.substring(next_line + 1);
      }

      this.stack = out;
    }
  }
};

// assert.AssertionError instanceof Error
util.inherits(assert.AssertionError, Error);

function truncate(s, n) {
  if (typeof s === 'string') {
    return s.length < n ? s : s.slice(0, n);
  } else {
    return s;
  }
}
function inspect(something) {
  if (functionsHaveNames || !util.isFunction(something)) {
    return util.inspect(something);
  }
  var rawname = getName(something);
  var name = rawname ? ': ' + rawname : '';
  return '[Function' +  name + ']';
}
function getMessage(self) {
  return truncate(inspect(self.actual), 128) + ' ' +
         self.operator + ' ' +
         truncate(inspect(self.expected), 128);
}

// At present only the three keys mentioned above are used and
// understood by the spec. Implementations or sub modules can pass
// other keys to the AssertionError's constructor - they will be
// ignored.

// 3. All of the following functions must throw an AssertionError
// when a corresponding condition is not met, with a message that
// may be undefined if not provided.  All assertion methods provide
// both the actual and expected values to the assertion error for
// display purposes.

function fail(actual, expected, message, operator, stackStartFunction) {
  throw new assert.AssertionError({
    message: message,
    actual: actual,
    expected: expected,
    operator: operator,
    stackStartFunction: stackStartFunction
  });
}

// EXTENSION! allows for well behaved errors defined elsewhere.
assert.fail = fail;

// 4. Pure assertion tests whether a value is truthy, as determined
// by !!guard.
// assert.ok(guard, message_opt);
// This statement is equivalent to assert.equal(true, !!guard,
// message_opt);. To test strictly for the value true, use
// assert.strictEqual(true, guard, message_opt);.

function ok(value, message) {
  if (!value) fail(value, true, message, '==', assert.ok);
}
assert.ok = ok;

// 5. The equality assertion tests shallow, coercive equality with
// ==.
// assert.equal(actual, expected, message_opt);

assert.equal = function equal(actual, expected, message) {
  if (actual != expected) fail(actual, expected, message, '==', assert.equal);
};

// 6. The non-equality assertion tests for whether two objects are not equal
// with != assert.notEqual(actual, expected, message_opt);

assert.notEqual = function notEqual(actual, expected, message) {
  if (actual == expected) {
    fail(actual, expected, message, '!=', assert.notEqual);
  }
};

// 7. The equivalence assertion tests a deep equality relation.
// assert.deepEqual(actual, expected, message_opt);

assert.deepEqual = function deepEqual(actual, expected, message) {
  if (!_deepEqual(actual, expected, false)) {
    fail(actual, expected, message, 'deepEqual', assert.deepEqual);
  }
};

assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) {
  if (!_deepEqual(actual, expected, true)) {
    fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual);
  }
};

function _deepEqual(actual, expected, strict, memos) {
  // 7.1. All identical values are equivalent, as determined by ===.
  if (actual === expected) {
    return true;
  } else if (isBuffer(actual) && isBuffer(expected)) {
    return compare(actual, expected) === 0;

  // 7.2. If the expected value is a Date object, the actual value is
  // equivalent if it is also a Date object that refers to the same time.
  } else if (util.isDate(actual) && util.isDate(expected)) {
    return actual.getTime() === expected.getTime();

  // 7.3 If the expected value is a RegExp object, the actual value is
  // equivalent if it is also a RegExp object with the same source and
  // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`).
  } else if (util.isRegExp(actual) && util.isRegExp(expected)) {
    return actual.source === expected.source &&
           actual.global === expected.global &&
           actual.multiline === expected.multiline &&
           actual.lastIndex === expected.lastIndex &&
           actual.ignoreCase === expected.ignoreCase;

  // 7.4. Other pairs that do not both pass typeof value == 'object',
  // equivalence is determined by ==.
  } else if ((actual === null || typeof actual !== 'object') &&
             (expected === null || typeof expected !== 'object')) {
    return strict ? actual === expected : actual == expected;

  // If both values are instances of typed arrays, wrap their underlying
  // ArrayBuffers in a Buffer each to increase performance
  // This optimization requires the arrays to have the same type as checked by
  // Object.prototype.toString (aka pToString). Never perform binary
  // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their
  // bit patterns are not identical.
  } else if (isView(actual) && isView(expected) &&
             pToString(actual) === pToString(expected) &&
             !(actual instanceof Float32Array ||
               actual instanceof Float64Array)) {
    return compare(new Uint8Array(actual.buffer),
                   new Uint8Array(expected.buffer)) === 0;

  // 7.5 For all other Object pairs, including Array objects, equivalence is
  // determined by having the same number of owned properties (as verified
  // with Object.prototype.hasOwnProperty.call), the same set of keys
  // (although not necessarily the same order), equivalent values for every
  // corresponding key, and an identical 'prototype' property. Note: this
  // accounts for both named and indexed properties on Arrays.
  } else if (isBuffer(actual) !== isBuffer(expected)) {
    return false;
  } else {
    memos = memos || {actual: [], expected: []};

    var actualIndex = memos.actual.indexOf(actual);
    if (actualIndex !== -1) {
      if (actualIndex === memos.expected.indexOf(expected)) {
        return true;
      }
    }

    memos.actual.push(actual);
    memos.expected.push(expected);

    return objEquiv(actual, expected, strict, memos);
  }
}

function isArguments(object) {
  return Object.prototype.toString.call(object) == '[object Arguments]';
}

function objEquiv(a, b, strict, actualVisitedObjects) {
  if (a === null || a === undefined || b === null || b === undefined)
    return false;
  // if one is a primitive, the other must be same
  if (util.isPrimitive(a) || util.isPrimitive(b))
    return a === b;
  if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
    return false;
  var aIsArgs = isArguments(a);
  var bIsArgs = isArguments(b);
  if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs))
    return false;
  if (aIsArgs) {
    a = pSlice.call(a);
    b = pSlice.call(b);
    return _deepEqual(a, b, strict);
  }
  var ka = objectKeys(a);
  var kb = objectKeys(b);
  var key, i;
  // having the same number of owned properties (keys incorporates
  // hasOwnProperty)
  if (ka.length !== kb.length)
    return false;
  //the same set of keys (although not necessarily the same order),
  ka.sort();
  kb.sort();
  //~~~cheap key test
  for (i = ka.length - 1; i >= 0; i--) {
    if (ka[i] !== kb[i])
      return false;
  }
  //equivalent values for every corresponding key, and
  //~~~possibly expensive deep test
  for (i = ka.length - 1; i >= 0; i--) {
    key = ka[i];
    if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects))
      return false;
  }
  return true;
}

// 8. The non-equivalence assertion tests for any deep inequality.
// assert.notDeepEqual(actual, expected, message_opt);

assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
  if (_deepEqual(actual, expected, false)) {
    fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
  }
};

assert.notDeepStrictEqual = notDeepStrictEqual;
function notDeepStrictEqual(actual, expected, message) {
  if (_deepEqual(actual, expected, true)) {
    fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual);
  }
}


// 9. The strict equality assertion tests strict equality, as determined by ===.
// assert.strictEqual(actual, expected, message_opt);

assert.strictEqual = function strictEqual(actual, expected, message) {
  if (actual !== expected) {
    fail(actual, expected, message, '===', assert.strictEqual);
  }
};

// 10. The strict non-equality assertion tests for strict inequality, as
// determined by !==.  assert.notStrictEqual(actual, expected, message_opt);

assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
  if (actual === expected) {
    fail(actual, expected, message, '!==', assert.notStrictEqual);
  }
};

function expectedException(actual, expected) {
  if (!actual || !expected) {
    return false;
  }

  if (Object.prototype.toString.call(expected) == '[object RegExp]') {
    return expected.test(actual);
  }

  try {
    if (actual instanceof expected) {
      return true;
    }
  } catch (e) {
    // Ignore.  The instanceof check doesn't work for arrow functions.
  }

  if (Error.isPrototypeOf(expected)) {
    return false;
  }

  return expected.call({}, actual) === true;
}

function _tryBlock(block) {
  var error;
  try {
    block();
  } catch (e) {
    error = e;
  }
  return error;
}

function _throws(shouldThrow, block, expected, message) {
  var actual;

  if (typeof block !== 'function') {
    throw new TypeError('"block" argument must be a function');
  }

  if (typeof expected === 'string') {
    message = expected;
    expected = null;
  }

  actual = _tryBlock(block);

  message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
            (message ? ' ' + message : '.');

  if (shouldThrow && !actual) {
    fail(actual, expected, 'Missing expected exception' + message);
  }

  var userProvidedMessage = typeof message === 'string';
  var isUnwantedException = !shouldThrow && util.isError(actual);
  var isUnexpectedException = !shouldThrow && actual && !expected;

  if ((isUnwantedException &&
      userProvidedMessage &&
      expectedException(actual, expected)) ||
      isUnexpectedException) {
    fail(actual, expected, 'Got unwanted exception' + message);
  }

  if ((shouldThrow && actual && expected &&
      !expectedException(actual, expected)) || (!shouldThrow && actual)) {
    throw actual;
  }
}

// 11. Expected to throw an error:
// assert.throws(block, Error_opt, message_opt);

assert.throws = function(block, /*optional*/error, /*optional*/message) {
  _throws(true, block, error, message);
};

// EXTENSION! This is annoying to write outside this module.
assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) {
  _throws(false, block, error, message);
};

assert.ifError = function(err) { if (err) throw err; };

// Expose a strict only variant of assert
function strict(value, message) {
  if (!value) fail(value, true, message, '==', strict);
}
assert.strict = objectAssign(strict, assert, {
  equal: assert.strictEqual,
  deepEqual: assert.deepStrictEqual,
  notEqual: assert.notStrictEqual,
  notDeepEqual: assert.notDeepStrictEqual
});
assert.strict.strict = assert.strict;

var objectKeys = Object.keys || function (obj) {
  var keys = [];
  for (var key in obj) {
    if (hasOwn.call(obj, key)) keys.push(key);
  }
  return keys;
};

}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"object-assign":177,"util/":153}],151:[function(require,module,exports){
if (typeof Object.create === 'function') {
  // implementation from standard node.js 'util' module
  module.exports = function inherits(ctor, superCtor) {
    ctor.super_ = superCtor
    ctor.prototype = Object.create(superCtor.prototype, {
      constructor: {
        value: ctor,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
  };
} else {
  // old school shim for old browsers
  module.exports = function inherits(ctor, superCtor) {
    ctor.super_ = superCtor
    var TempCtor = function () {}
    TempCtor.prototype = superCtor.prototype
    ctor.prototype = new TempCtor()
    ctor.prototype.constructor = ctor
  }
}

},{}],152:[function(require,module,exports){
module.exports = function isBuffer(arg) {
  return arg && typeof arg === 'object'
    && typeof arg.copy === 'function'
    && typeof arg.fill === 'function'
    && typeof arg.readUInt8 === 'function';
}
},{}],153:[function(require,module,exports){
(function (process,global){(function (){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var formatRegExp = /%[sdj%]/g;
exports.format = function(f) {
  if (!isString(f)) {
    var objects = [];
    for (var i = 0; i < arguments.length; i++) {
      objects.push(inspect(arguments[i]));
    }
    return objects.join(' ');
  }

  var i = 1;
  var args = arguments;
  var len = args.length;
  var str = String(f).replace(formatRegExp, function(x) {
    if (x === '%%') return '%';
    if (i >= len) return x;
    switch (x) {
      case '%s': return String(args[i++]);
      case '%d': return Number(args[i++]);
      case '%j':
        try {
          return JSON.stringify(args[i++]);
        } catch (_) {
          return '[Circular]';
        }
      default:
        return x;
    }
  });
  for (var x = args[i]; i < len; x = args[++i]) {
    if (isNull(x) || !isObject(x)) {
      str += ' ' + x;
    } else {
      str += ' ' + inspect(x);
    }
  }
  return str;
};


// Mark that a method should not be used.
// Returns a modified function which warns once by default.
// If --no-deprecation is set, then it is a no-op.
exports.deprecate = function(fn, msg) {
  // Allow for deprecating things in the process of starting up.
  if (isUndefined(global.process)) {
    return function() {
      return exports.deprecate(fn, msg).apply(this, arguments);
    };
  }

  if (process.noDeprecation === true) {
    return fn;
  }

  var warned = false;
  function deprecated() {
    if (!warned) {
      if (process.throwDeprecation) {
        throw new Error(msg);
      } else if (process.traceDeprecation) {
        console.trace(msg);
      } else {
        console.error(msg);
      }
      warned = true;
    }
    return fn.apply(this, arguments);
  }

  return deprecated;
};


var debugs = {};
var debugEnviron;
exports.debuglog = function(set) {
  if (isUndefined(debugEnviron))
    debugEnviron = process.env.NODE_DEBUG || '';
  set = set.toUpperCase();
  if (!debugs[set]) {
    if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
      var pid = process.pid;
      debugs[set] = function() {
        var msg = exports.format.apply(exports, arguments);
        console.error('%s %d: %s', set, pid, msg);
      };
    } else {
      debugs[set] = function() {};
    }
  }
  return debugs[set];
};


/**
 * Echos the value of a value. Trys to print the value out
 * in the best way possible given the different types.
 *
 * @param {Object} obj The object to print out.
 * @param {Object} opts Optional options object that alters the output.
 */
/* legacy: obj, showHidden, depth, colors*/
function inspect(obj, opts) {
  // default options
  var ctx = {
    seen: [],
    stylize: stylizeNoColor
  };
  // legacy...
  if (arguments.length >= 3) ctx.depth = arguments[2];
  if (arguments.length >= 4) ctx.colors = arguments[3];
  if (isBoolean(opts)) {
    // legacy...
    ctx.showHidden = opts;
  } else if (opts) {
    // got an "options" object
    exports._extend(ctx, opts);
  }
  // set default options
  if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
  if (isUndefined(ctx.depth)) ctx.depth = 2;
  if (isUndefined(ctx.colors)) ctx.colors = false;
  if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
  if (ctx.colors) ctx.stylize = stylizeWithColor;
  return formatValue(ctx, obj, ctx.depth);
}
exports.inspect = inspect;


// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
inspect.colors = {
  'bold' : [1, 22],
  'italic' : [3, 23],
  'underline' : [4, 24],
  'inverse' : [7, 27],
  'white' : [37, 39],
  'grey' : [90, 39],
  'black' : [30, 39],
  'blue' : [34, 39],
  'cyan' : [36, 39],
  'green' : [32, 39],
  'magenta' : [35, 39],
  'red' : [31, 39],
  'yellow' : [33, 39]
};

// Don't use 'blue' not visible on cmd.exe
inspect.styles = {
  'special': 'cyan',
  'number': 'yellow',
  'boolean': 'yellow',
  'undefined': 'grey',
  'null': 'bold',
  'string': 'green',
  'date': 'magenta',
  // "name": intentionally not styling
  'regexp': 'red'
};


function stylizeWithColor(str, styleType) {
  var style = inspect.styles[styleType];

  if (style) {
    return '\u001b[' + inspect.colors[style][0] + 'm' + str +
           '\u001b[' + inspect.colors[style][1] + 'm';
  } else {
    return str;
  }
}


function stylizeNoColor(str, styleType) {
  return str;
}


function arrayToHash(array) {
  var hash = {};

  array.forEach(function(val, idx) {
    hash[val] = true;
  });

  return hash;
}


function formatValue(ctx, value, recurseTimes) {
  // Provide a hook for user-specified inspect functions.
  // Check that value is an object with an inspect function on it
  if (ctx.customInspect &&
      value &&
      isFunction(value.inspect) &&
      // Filter out the util module, it's inspect function is special
      value.inspect !== exports.inspect &&
      // Also filter out any prototype objects using the circular check.
      !(value.constructor && value.constructor.prototype === value)) {
    var ret = value.inspect(recurseTimes, ctx);
    if (!isString(ret)) {
      ret = formatValue(ctx, ret, recurseTimes);
    }
    return ret;
  }

  // Primitive types cannot have properties
  var primitive = formatPrimitive(ctx, value);
  if (primitive) {
    return primitive;
  }

  // Look up the keys of the object.
  var keys = Object.keys(value);
  var visibleKeys = arrayToHash(keys);

  if (ctx.showHidden) {
    keys = Object.getOwnPropertyNames(value);
  }

  // IE doesn't make error fields non-enumerable
  // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
  if (isError(value)
      && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
    return formatError(value);
  }

  // Some type of object without properties can be shortcutted.
  if (keys.length === 0) {
    if (isFunction(value)) {
      var name = value.name ? ': ' + value.name : '';
      return ctx.stylize('[Function' + name + ']', 'special');
    }
    if (isRegExp(value)) {
      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
    }
    if (isDate(value)) {
      return ctx.stylize(Date.prototype.toString.call(value), 'date');
    }
    if (isError(value)) {
      return formatError(value);
    }
  }

  var base = '', array = false, braces = ['{', '}'];

  // Make Array say that they are Array
  if (isArray(value)) {
    array = true;
    braces = ['[', ']'];
  }

  // Make functions say that they are functions
  if (isFunction(value)) {
    var n = value.name ? ': ' + value.name : '';
    base = ' [Function' + n + ']';
  }

  // Make RegExps say that they are RegExps
  if (isRegExp(value)) {
    base = ' ' + RegExp.prototype.toString.call(value);
  }

  // Make dates with properties first say the date
  if (isDate(value)) {
    base = ' ' + Date.prototype.toUTCString.call(value);
  }

  // Make error with message first say the error
  if (isError(value)) {
    base = ' ' + formatError(value);
  }

  if (keys.length === 0 && (!array || value.length == 0)) {
    return braces[0] + base + braces[1];
  }

  if (recurseTimes < 0) {
    if (isRegExp(value)) {
      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
    } else {
      return ctx.stylize('[Object]', 'special');
    }
  }

  ctx.seen.push(value);

  var output;
  if (array) {
    output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
  } else {
    output = keys.map(function(key) {
      return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
    });
  }

  ctx.seen.pop();

  return reduceToSingleString(output, base, braces);
}


function formatPrimitive(ctx, value) {
  if (isUndefined(value))
    return ctx.stylize('undefined', 'undefined');
  if (isString(value)) {
    var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
                                             .replace(/'/g, "\\'")
                                             .replace(/\\"/g, '"') + '\'';
    return ctx.stylize(simple, 'string');
  }
  if (isNumber(value))
    return ctx.stylize('' + value, 'number');
  if (isBoolean(value))
    return ctx.stylize('' + value, 'boolean');
  // For some reason typeof null is "object", so special case here.
  if (isNull(value))
    return ctx.stylize('null', 'null');
}


function formatError(value) {
  return '[' + Error.prototype.toString.call(value) + ']';
}


function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
  var output = [];
  for (var i = 0, l = value.length; i < l; ++i) {
    if (hasOwnProperty(value, String(i))) {
      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
          String(i), true));
    } else {
      output.push('');
    }
  }
  keys.forEach(function(key) {
    if (!key.match(/^\d+$/)) {
      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
          key, true));
    }
  });
  return output;
}


function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
  var name, str, desc;
  desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
  if (desc.get) {
    if (desc.set) {
      str = ctx.stylize('[Getter/Setter]', 'special');
    } else {
      str = ctx.stylize('[Getter]', 'special');
    }
  } else {
    if (desc.set) {
      str = ctx.stylize('[Setter]', 'special');
    }
  }
  if (!hasOwnProperty(visibleKeys, key)) {
    name = '[' + key + ']';
  }
  if (!str) {
    if (ctx.seen.indexOf(desc.value) < 0) {
      if (isNull(recurseTimes)) {
        str = formatValue(ctx, desc.value, null);
      } else {
        str = formatValue(ctx, desc.value, recurseTimes - 1);
      }
      if (str.indexOf('\n') > -1) {
        if (array) {
          str = str.split('\n').map(function(line) {
            return '  ' + line;
          }).join('\n').substr(2);
        } else {
          str = '\n' + str.split('\n').map(function(line) {
            return '   ' + line;
          }).join('\n');
        }
      }
    } else {
      str = ctx.stylize('[Circular]', 'special');
    }
  }
  if (isUndefined(name)) {
    if (array && key.match(/^\d+$/)) {
      return str;
    }
    name = JSON.stringify('' + key);
    if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
      name = name.substr(1, name.length - 2);
      name = ctx.stylize(name, 'name');
    } else {
      name = name.replace(/'/g, "\\'")
                 .replace(/\\"/g, '"')
                 .replace(/(^"|"$)/g, "'");
      name = ctx.stylize(name, 'string');
    }
  }

  return name + ': ' + str;
}


function reduceToSingleString(output, base, braces) {
  var numLinesEst = 0;
  var length = output.reduce(function(prev, cur) {
    numLinesEst++;
    if (cur.indexOf('\n') >= 0) numLinesEst++;
    return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
  }, 0);

  if (length > 60) {
    return braces[0] +
           (base === '' ? '' : base + '\n ') +
           ' ' +
           output.join(',\n  ') +
           ' ' +
           braces[1];
  }

  return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
}


// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
  return Array.isArray(ar);
}
exports.isArray = isArray;

function isBoolean(arg) {
  return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;

function isNull(arg) {
  return arg === null;
}
exports.isNull = isNull;

function isNullOrUndefined(arg) {
  return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;

function isNumber(arg) {
  return typeof arg === 'number';
}
exports.isNumber = isNumber;

function isString(arg) {
  return typeof arg === 'string';
}
exports.isString = isString;

function isSymbol(arg) {
  return typeof arg === 'symbol';
}
exports.isSymbol = isSymbol;

function isUndefined(arg) {
  return arg === void 0;
}
exports.isUndefined = isUndefined;

function isRegExp(re) {
  return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;

function isObject(arg) {
  return typeof arg === 'object' && arg !== null;
}
exports.isObject = isObject;

function isDate(d) {
  return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;

function isError(e) {
  return isObject(e) &&
      (objectToString(e) === '[object Error]' || e instanceof Error);
}
exports.isError = isError;

function isFunction(arg) {
  return typeof arg === 'function';
}
exports.isFunction = isFunction;

function isPrimitive(arg) {
  return arg === null ||
         typeof arg === 'boolean' ||
         typeof arg === 'number' ||
         typeof arg === 'string' ||
         typeof arg === 'symbol' ||  // ES6 symbol
         typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;

exports.isBuffer = require('./support/isBuffer');

function objectToString(o) {
  return Object.prototype.toString.call(o);
}


function pad(n) {
  return n < 10 ? '0' + n.toString(10) : n.toString(10);
}


var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
              'Oct', 'Nov', 'Dec'];

// 26 Feb 16:19:34
function timestamp() {
  var d = new Date();
  var time = [pad(d.getHours()),
              pad(d.getMinutes()),
              pad(d.getSeconds())].join(':');
  return [d.getDate(), months[d.getMonth()], time].join(' ');
}


// log is just a thin wrapper to console.log that prepends a timestamp
exports.log = function() {
  console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
};


/**
 * Inherit the prototype methods from one constructor into another.
 *
 * The Function.prototype.inherits from lang.js rewritten as a standalone
 * function (not on Function.prototype). NOTE: If this file is to be loaded
 * during bootstrapping this function needs to be rewritten using some native
 * functions as prototype setup using normal JavaScript does not work as
 * expected during bootstrapping (see mirror.js in r114903).
 *
 * @param {function} ctor Constructor function which needs to inherit the
 *     prototype.
 * @param {function} superCtor Constructor function to inherit prototype from.
 */
exports.inherits = require('inherits');

exports._extend = function(origin, add) {
  // Don't do anything if add isn't an object
  if (!add || !isObject(add)) return origin;

  var keys = Object.keys(add);
  var i = keys.length;
  while (i--) {
    origin[keys[i]] = add[keys[i]];
  }
  return origin;
};

function hasOwnProperty(obj, prop) {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}

}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./support/isBuffer":152,"_process":191,"inherits":151}],154:[function(require,module,exports){
(function (global){(function (){
'use strict';

var filter = require('array-filter');

module.exports = function availableTypedArrays() {
	return filter([
		'BigInt64Array',
		'BigUint64Array',
		'Float32Array',
		'Float64Array',
		'Int16Array',
		'Int32Array',
		'Int8Array',
		'Uint16Array',
		'Uint32Array',
		'Uint8Array',
		'Uint8ClampedArray'
	], function (typedArray) {
		return typeof global[typedArray] === 'function';
	});
};

}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"array-filter":149}],155:[function(require,module,exports){
'use strict'

exports.byteLength = byteLength
exports.toByteArray = toByteArray
exports.fromByteArray = fromByteArray

var lookup = []
var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array

var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) {
  lookup[i] = code[i]
  revLookup[code.charCodeAt(i)] = i
}

// Support decoding URL-safe base64 strings, as Node.js does.
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
revLookup['-'.charCodeAt(0)] = 62
revLookup['_'.charCodeAt(0)] = 63

function getLens (b64) {
  var len = b64.length

  if (len % 4 > 0) {
    throw new Error('Invalid string. Length must be a multiple of 4')
  }

  // Trim off extra bytes after placeholder bytes are found
  // See: https://github.com/beatgammit/base64-js/issues/42
  var validLen = b64.indexOf('=')
  if (validLen === -1) validLen = len

  var placeHoldersLen = validLen === len
    ? 0
    : 4 - (validLen % 4)

  return [validLen, placeHoldersLen]
}

// base64 is 4/3 + up to two characters of the original data
function byteLength (b64) {
  var lens = getLens(b64)
  var validLen = lens[0]
  var placeHoldersLen = lens[1]
  return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}

function _byteLength (b64, validLen, placeHoldersLen) {
  return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}

function toByteArray (b64) {
  var tmp
  var lens = getLens(b64)
  var validLen = lens[0]
  var placeHoldersLen = lens[1]

  var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))

  var curByte = 0

  // if there are placeholders, only get up to the last complete 4 chars
  var len = placeHoldersLen > 0
    ? validLen - 4
    : validLen

  var i
  for (i = 0; i < len; i += 4) {
    tmp =
      (revLookup[b64.charCodeAt(i)] << 18) |
      (revLookup[b64.charCodeAt(i + 1)] << 12) |
      (revLookup[b64.charCodeAt(i + 2)] << 6) |
      revLookup[b64.charCodeAt(i + 3)]
    arr[curByte++] = (tmp >> 16) & 0xFF
    arr[curByte++] = (tmp >> 8) & 0xFF
    arr[curByte++] = tmp & 0xFF
  }

  if (placeHoldersLen === 2) {
    tmp =
      (revLookup[b64.charCodeAt(i)] << 2) |
      (revLookup[b64.charCodeAt(i + 1)] >> 4)
    arr[curByte++] = tmp & 0xFF
  }

  if (placeHoldersLen === 1) {
    tmp =
      (revLookup[b64.charCodeAt(i)] << 10) |
      (revLookup[b64.charCodeAt(i + 1)] << 4) |
      (revLookup[b64.charCodeAt(i + 2)] >> 2)
    arr[curByte++] = (tmp >> 8) & 0xFF
    arr[curByte++] = tmp & 0xFF
  }

  return arr
}

function tripletToBase64 (num) {
  return lookup[num >> 18 & 0x3F] +
    lookup[num >> 12 & 0x3F] +
    lookup[num >> 6 & 0x3F] +
    lookup[num & 0x3F]
}

function encodeChunk (uint8, start, end) {
  var tmp
  var output = []
  for (var i = start; i < end; i += 3) {
    tmp =
      ((uint8[i] << 16) & 0xFF0000) +
      ((uint8[i + 1] << 8) & 0xFF00) +
      (uint8[i + 2] & 0xFF)
    output.push(tripletToBase64(tmp))
  }
  return output.join('')
}

function fromByteArray (uint8) {
  var tmp
  var len = uint8.length
  var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
  var parts = []
  var maxChunkLength = 16383 // must be multiple of 3

  // go through the array every three bytes, we'll deal with trailing stuff later
  for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
    parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
  }

  // pad the end with zeros, but make sure to not forget the extra bytes
  if (extraBytes === 1) {
    tmp = uint8[len - 1]
    parts.push(
      lookup[tmp >> 2] +
      lookup[(tmp << 4) & 0x3F] +
      '=='
    )
  } else if (extraBytes === 2) {
    tmp = (uint8[len - 2] << 8) + uint8[len - 1]
    parts.push(
      lookup[tmp >> 10] +
      lookup[(tmp >> 4) & 0x3F] +
      lookup[(tmp << 2) & 0x3F] +
      '='
    )
  }

  return parts.join('')
}

},{}],156:[function(require,module,exports){
arguments[4][148][0].apply(exports,arguments)
},{"dup":148}],157:[function(require,module,exports){
(function (process,Buffer){(function (){
'use strict';
/* eslint camelcase: "off" */

var assert = require('assert');

var Zstream = require('pako/lib/zlib/zstream');
var zlib_deflate = require('pako/lib/zlib/deflate.js');
var zlib_inflate = require('pako/lib/zlib/inflate.js');
var constants = require('pako/lib/zlib/constants');

for (var key in constants) {
  exports[key] = constants[key];
}

// zlib modes
exports.NONE = 0;
exports.DEFLATE = 1;
exports.INFLATE = 2;
exports.GZIP = 3;
exports.GUNZIP = 4;
exports.DEFLATERAW = 5;
exports.INFLATERAW = 6;
exports.UNZIP = 7;

var GZIP_HEADER_ID1 = 0x1f;
var GZIP_HEADER_ID2 = 0x8b;

/**
 * Emulate Node's zlib C++ layer for use by the JS layer in index.js
 */
function Zlib(mode) {
  if (typeof mode !== 'number' || mode < exports.DEFLATE || mode > exports.UNZIP) {
    throw new TypeError('Bad argument');
  }

  this.dictionary = null;
  this.err = 0;
  this.flush = 0;
  this.init_done = false;
  this.level = 0;
  this.memLevel = 0;
  this.mode = mode;
  this.strategy = 0;
  this.windowBits = 0;
  this.write_in_progress = false;
  this.pending_close = false;
  this.gzip_id_bytes_read = 0;
}

Zlib.prototype.close = function () {
  if (this.write_in_progress) {
    this.pending_close = true;
    return;
  }

  this.pending_close = false;

  assert(this.init_done, 'close before init');
  assert(this.mode <= exports.UNZIP);

  if (this.mode === exports.DEFLATE || this.mode === exports.GZIP || this.mode === exports.DEFLATERAW) {
    zlib_deflate.deflateEnd(this.strm);
  } else if (this.mode === exports.INFLATE || this.mode === exports.GUNZIP || this.mode === exports.INFLATERAW || this.mode === exports.UNZIP) {
    zlib_inflate.inflateEnd(this.strm);
  }

  this.mode = exports.NONE;

  this.dictionary = null;
};

Zlib.prototype.write = function (flush, input, in_off, in_len, out, out_off, out_len) {
  return this._write(true, flush, input, in_off, in_len, out, out_off, out_len);
};

Zlib.prototype.writeSync = function (flush, input, in_off, in_len, out, out_off, out_len) {
  return this._write(false, flush, input, in_off, in_len, out, out_off, out_len);
};

Zlib.prototype._write = function (async, flush, input, in_off, in_len, out, out_off, out_len) {
  assert.equal(arguments.length, 8);

  assert(this.init_done, 'write before init');
  assert(this.mode !== exports.NONE, 'already finalized');
  assert.equal(false, this.write_in_progress, 'write already in progress');
  assert.equal(false, this.pending_close, 'close is pending');

  this.write_in_progress = true;

  assert.equal(false, flush === undefined, 'must provide flush value');

  this.write_in_progress = true;

  if (flush !== exports.Z_NO_FLUSH && flush !== exports.Z_PARTIAL_FLUSH && flush !== exports.Z_SYNC_FLUSH && flush !== exports.Z_FULL_FLUSH && flush !== exports.Z_FINISH && flush !== exports.Z_BLOCK) {
    throw new Error('Invalid flush value');
  }

  if (input == null) {
    input = Buffer.alloc(0);
    in_len = 0;
    in_off = 0;
  }

  this.strm.avail_in = in_len;
  this.strm.input = input;
  this.strm.next_in = in_off;
  this.strm.avail_out = out_len;
  this.strm.output = out;
  this.strm.next_out = out_off;
  this.flush = flush;

  if (!async) {
    // sync version
    this._process();

    if (this._checkError()) {
      return this._afterSync();
    }
    return;
  }

  // async version
  var self = this;
  process.nextTick(function () {
    self._process();
    self._after();
  });

  return this;
};

Zlib.prototype._afterSync = function () {
  var avail_out = this.strm.avail_out;
  var avail_in = this.strm.avail_in;

  this.write_in_progress = false;

  return [avail_in, avail_out];
};

Zlib.prototype._process = function () {
  var next_expected_header_byte = null;

  // If the avail_out is left at 0, then it means that it ran out
  // of room.  If there was avail_out left over, then it means
  // that all of the input was consumed.
  switch (this.mode) {
    case exports.DEFLATE:
    case exports.GZIP:
    case exports.DEFLATERAW:
      this.err = zlib_deflate.deflate(this.strm, this.flush);
      break;
    case exports.UNZIP:
      if (this.strm.avail_in > 0) {
        next_expected_header_byte = this.strm.next_in;
      }

      switch (this.gzip_id_bytes_read) {
        case 0:
          if (next_expected_header_byte === null) {
            break;
          }

          if (this.strm.input[next_expected_header_byte] === GZIP_HEADER_ID1) {
            this.gzip_id_bytes_read = 1;
            next_expected_header_byte++;

            if (this.strm.avail_in === 1) {
              // The only available byte was already read.
              break;
            }
          } else {
            this.mode = exports.INFLATE;
            break;
          }

        // fallthrough
        case 1:
          if (next_expected_header_byte === null) {
            break;
          }

          if (this.strm.input[next_expected_header_byte] === GZIP_HEADER_ID2) {
            this.gzip_id_bytes_read = 2;
            this.mode = exports.GUNZIP;
          } else {
            // There is no actual difference between INFLATE and INFLATERAW
            // (after initialization).
            this.mode = exports.INFLATE;
          }

          break;
        default:
          throw new Error('invalid number of gzip magic number bytes read');
      }

    // fallthrough
    case exports.INFLATE:
    case exports.GUNZIP:
    case exports.INFLATERAW:
      this.err = zlib_inflate.inflate(this.strm, this.flush

      // If data was encoded with dictionary
      );if (this.err === exports.Z_NEED_DICT && this.dictionary) {
        // Load it
        this.err = zlib_inflate.inflateSetDictionary(this.strm, this.dictionary);
        if (this.err === exports.Z_OK) {
          // And try to decode again
          this.err = zlib_inflate.inflate(this.strm, this.flush);
        } else if (this.err === exports.Z_DATA_ERROR) {
          // Both inflateSetDictionary() and inflate() return Z_DATA_ERROR.
          // Make it possible for After() to tell a bad dictionary from bad
          // input.
          this.err = exports.Z_NEED_DICT;
        }
      }
      while (this.strm.avail_in > 0 && this.mode === exports.GUNZIP && this.err === exports.Z_STREAM_END && this.strm.next_in[0] !== 0x00) {
        // Bytes remain in input buffer. Perhaps this is another compressed
        // member in the same archive, or just trailing garbage.
        // Trailing zero bytes are okay, though, since they are frequently
        // used for padding.

        this.reset();
        this.err = zlib_inflate.inflate(this.strm, this.flush);
      }
      break;
    default:
      throw new Error('Unknown mode ' + this.mode);
  }
};

Zlib.prototype._checkError = function () {
  // Acceptable error states depend on the type of zlib stream.
  switch (this.err) {
    case exports.Z_OK:
    case exports.Z_BUF_ERROR:
      if (this.strm.avail_out !== 0 && this.flush === exports.Z_FINISH) {
        this._error('unexpected end of file');
        return false;
      }
      break;
    case exports.Z_STREAM_END:
      // normal statuses, not fatal
      break;
    case exports.Z_NEED_DICT:
      if (this.dictionary == null) {
        this._error('Missing dictionary');
      } else {
        this._error('Bad dictionary');
      }
      return false;
    default:
      // something else.
      this._error('Zlib error');
      return false;
  }

  return true;
};

Zlib.prototype._after = function () {
  if (!this._checkError()) {
    return;
  }

  var avail_out = this.strm.avail_out;
  var avail_in = this.strm.avail_in;

  this.write_in_progress = false;

  // call the write() cb
  this.callback(avail_in, avail_out);

  if (this.pending_close) {
    this.close();
  }
};

Zlib.prototype._error = function (message) {
  if (this.strm.msg) {
    message = this.strm.msg;
  }
  this.onerror(message, this.err

  // no hope of rescue.
  );this.write_in_progress = false;
  if (this.pending_close) {
    this.close();
  }
};

Zlib.prototype.init = function (windowBits, level, memLevel, strategy, dictionary) {
  assert(arguments.length === 4 || arguments.length === 5, 'init(windowBits, level, memLevel, strategy, [dictionary])');

  assert(windowBits >= 8 && windowBits <= 15, 'invalid windowBits');
  assert(level >= -1 && level <= 9, 'invalid compression level');

  assert(memLevel >= 1 && memLevel <= 9, 'invalid memlevel');

  assert(strategy === exports.Z_FILTERED || strategy === exports.Z_HUFFMAN_ONLY || strategy === exports.Z_RLE || strategy === exports.Z_FIXED || strategy === exports.Z_DEFAULT_STRATEGY, 'invalid strategy');

  this._init(level, windowBits, memLevel, strategy, dictionary);
  this._setDictionary();
};

Zlib.prototype.params = function () {
  throw new Error('deflateParams Not supported');
};

Zlib.prototype.reset = function () {
  this._reset();
  this._setDictionary();
};

Zlib.prototype._init = function (level, windowBits, memLevel, strategy, dictionary) {
  this.level = level;
  this.windowBits = windowBits;
  this.memLevel = memLevel;
  this.strategy = strategy;

  this.flush = exports.Z_NO_FLUSH;

  this.err = exports.Z_OK;

  if (this.mode === exports.GZIP || this.mode === exports.GUNZIP) {
    this.windowBits += 16;
  }

  if (this.mode === exports.UNZIP) {
    this.windowBits += 32;
  }

  if (this.mode === exports.DEFLATERAW || this.mode === exports.INFLATERAW) {
    this.windowBits = -1 * this.windowBits;
  }

  this.strm = new Zstream();

  switch (this.mode) {
    case exports.DEFLATE:
    case exports.GZIP:
    case exports.DEFLATERAW:
      this.err = zlib_deflate.deflateInit2(this.strm, this.level, exports.Z_DEFLATED, this.windowBits, this.memLevel, this.strategy);
      break;
    case exports.INFLATE:
    case exports.GUNZIP:
    case exports.INFLATERAW:
    case exports.UNZIP:
      this.err = zlib_inflate.inflateInit2(this.strm, this.windowBits);
      break;
    default:
      throw new Error('Unknown mode ' + this.mode);
  }

  if (this.err !== exports.Z_OK) {
    this._error('Init error');
  }

  this.dictionary = dictionary;

  this.write_in_progress = false;
  this.init_done = true;
};

Zlib.prototype._setDictionary = function () {
  if (this.dictionary == null) {
    return;
  }

  this.err = exports.Z_OK;

  switch (this.mode) {
    case exports.DEFLATE:
    case exports.DEFLATERAW:
      this.err = zlib_deflate.deflateSetDictionary(this.strm, this.dictionary);
      break;
    default:
      break;
  }

  if (this.err !== exports.Z_OK) {
    this._error('Failed to set dictionary');
  }
};

Zlib.prototype._reset = function () {
  this.err = exports.Z_OK;

  switch (this.mode) {
    case exports.DEFLATE:
    case exports.DEFLATERAW:
    case exports.GZIP:
      this.err = zlib_deflate.deflateReset(this.strm);
      break;
    case exports.INFLATE:
    case exports.INFLATERAW:
    case exports.GUNZIP:
      this.err = zlib_inflate.inflateReset(this.strm);
      break;
    default:
      break;
  }

  if (this.err !== exports.Z_OK) {
    this._error('Failed to reset stream');
  }
};

exports.Zlib = Zlib;
}).call(this)}).call(this,require('_process'),require("buffer").Buffer)
},{"_process":191,"assert":150,"buffer":159,"pako/lib/zlib/constants":181,"pako/lib/zlib/deflate.js":183,"pako/lib/zlib/inflate.js":185,"pako/lib/zlib/zstream":189}],158:[function(require,module,exports){
(function (process){(function (){
'use strict';

var Buffer = require('buffer').Buffer;
var Transform = require('stream').Transform;
var binding = require('./binding');
var util = require('util');
var assert = require('assert').ok;
var kMaxLength = require('buffer').kMaxLength;
var kRangeErrorMessage = 'Cannot create final Buffer. It would be larger ' + 'than 0x' + kMaxLength.toString(16) + ' bytes';

// zlib doesn't provide these, so kludge them in following the same
// const naming scheme zlib uses.
binding.Z_MIN_WINDOWBITS = 8;
binding.Z_MAX_WINDOWBITS = 15;
binding.Z_DEFAULT_WINDOWBITS = 15;

// fewer than 64 bytes per chunk is stupid.
// technically it could work with as few as 8, but even 64 bytes
// is absurdly low.  Usually a MB or more is best.
binding.Z_MIN_CHUNK = 64;
binding.Z_MAX_CHUNK = Infinity;
binding.Z_DEFAULT_CHUNK = 16 * 1024;

binding.Z_MIN_MEMLEVEL = 1;
binding.Z_MAX_MEMLEVEL = 9;
binding.Z_DEFAULT_MEMLEVEL = 8;

binding.Z_MIN_LEVEL = -1;
binding.Z_MAX_LEVEL = 9;
binding.Z_DEFAULT_LEVEL = binding.Z_DEFAULT_COMPRESSION;

// expose all the zlib constants
var bkeys = Object.keys(binding);
for (var bk = 0; bk < bkeys.length; bk++) {
  var bkey = bkeys[bk];
  if (bkey.match(/^Z/)) {
    Object.defineProperty(exports, bkey, {
      enumerable: true, value: binding[bkey], writable: false
    });
  }
}

// translation table for return codes.
var codes = {
  Z_OK: binding.Z_OK,
  Z_STREAM_END: binding.Z_STREAM_END,
  Z_NEED_DICT: binding.Z_NEED_DICT,
  Z_ERRNO: binding.Z_ERRNO,
  Z_STREAM_ERROR: binding.Z_STREAM_ERROR,
  Z_DATA_ERROR: binding.Z_DATA_ERROR,
  Z_MEM_ERROR: binding.Z_MEM_ERROR,
  Z_BUF_ERROR: binding.Z_BUF_ERROR,
  Z_VERSION_ERROR: binding.Z_VERSION_ERROR
};

var ckeys = Object.keys(codes);
for (var ck = 0; ck < ckeys.length; ck++) {
  var ckey = ckeys[ck];
  codes[codes[ckey]] = ckey;
}

Object.defineProperty(exports, 'codes', {
  enumerable: true, value: Object.freeze(codes), writable: false
});

exports.Deflate = Deflate;
exports.Inflate = Inflate;
exports.Gzip = Gzip;
exports.Gunzip = Gunzip;
exports.DeflateRaw = DeflateRaw;
exports.InflateRaw = InflateRaw;
exports.Unzip = Unzip;

exports.createDeflate = function (o) {
  return new Deflate(o);
};

exports.createInflate = function (o) {
  return new Inflate(o);
};

exports.createDeflateRaw = function (o) {
  return new DeflateRaw(o);
};

exports.createInflateRaw = function (o) {
  return new InflateRaw(o);
};

exports.createGzip = function (o) {
  return new Gzip(o);
};

exports.createGunzip = function (o) {
  return new Gunzip(o);
};

exports.createUnzip = function (o) {
  return new Unzip(o);
};

// Convenience methods.
// compress/decompress a string or buffer in one step.
exports.deflate = function (buffer, opts, callback) {
  if (typeof opts === 'function') {
    callback = opts;
    opts = {};
  }
  return zlibBuffer(new Deflate(opts), buffer, callback);
};

exports.deflateSync = function (buffer, opts) {
  return zlibBufferSync(new Deflate(opts), buffer);
};

exports.gzip = function (buffer, opts, callback) {
  if (typeof opts === 'function') {
    callback = opts;
    opts = {};
  }
  return zlibBuffer(new Gzip(opts), buffer, callback);
};

exports.gzipSync = function (buffer, opts) {
  return zlibBufferSync(new Gzip(opts), buffer);
};

exports.deflateRaw = function (buffer, opts, callback) {
  if (typeof opts === 'function') {
    callback = opts;
    opts = {};
  }
  return zlibBuffer(new DeflateRaw(opts), buffer, callback);
};

exports.deflateRawSync = function (buffer, opts) {
  return zlibBufferSync(new DeflateRaw(opts), buffer);
};

exports.unzip = function (buffer, opts, callback) {
  if (typeof opts === 'function') {
    callback = opts;
    opts = {};
  }
  return zlibBuffer(new Unzip(opts), buffer, callback);
};

exports.unzipSync = function (buffer, opts) {
  return zlibBufferSync(new Unzip(opts), buffer);
};

exports.inflate = function (buffer, opts, callback) {
  if (typeof opts === 'function') {
    callback = opts;
    opts = {};
  }
  return zlibBuffer(new Inflate(opts), buffer, callback);
};

exports.inflateSync = function (buffer, opts) {
  return zlibBufferSync(new Inflate(opts), buffer);
};

exports.gunzip = function (buffer, opts, callback) {
  if (typeof opts === 'function') {
    callback = opts;
    opts = {};
  }
  return zlibBuffer(new Gunzip(opts), buffer, callback);
};

exports.gunzipSync = function (buffer, opts) {
  return zlibBufferSync(new Gunzip(opts), buffer);
};

exports.inflateRaw = function (buffer, opts, callback) {
  if (typeof opts === 'function') {
    callback = opts;
    opts = {};
  }
  return zlibBuffer(new InflateRaw(opts), buffer, callback);
};

exports.inflateRawSync = function (buffer, opts) {
  return zlibBufferSync(new InflateRaw(opts), buffer);
};

function zlibBuffer(engine, buffer, callback) {
  var buffers = [];
  var nread = 0;

  engine.on('error', onError);
  engine.on('end', onEnd);

  engine.end(buffer);
  flow();

  function flow() {
    var chunk;
    while (null !== (chunk = engine.read())) {
      buffers.push(chunk);
      nread += chunk.length;
    }
    engine.once('readable', flow);
  }

  function onError(err) {
    engine.removeListener('end', onEnd);
    engine.removeListener('readable', flow);
    callback(err);
  }

  function onEnd() {
    var buf;
    var err = null;

    if (nread >= kMaxLength) {
      err = new RangeError(kRangeErrorMessage);
    } else {
      buf = Buffer.concat(buffers, nread);
    }

    buffers = [];
    engine.close();
    callback(err, buf);
  }
}

function zlibBufferSync(engine, buffer) {
  if (typeof buffer === 'string') buffer = Buffer.from(buffer);

  if (!Buffer.isBuffer(buffer)) throw new TypeError('Not a string or buffer');

  var flushFlag = engine._finishFlushFlag;

  return engine._processChunk(buffer, flushFlag);
}

// generic zlib
// minimal 2-byte header
function Deflate(opts) {
  if (!(this instanceof Deflate)) return new Deflate(opts);
  Zlib.call(this, opts, binding.DEFLATE);
}

function Inflate(opts) {
  if (!(this instanceof Inflate)) return new Inflate(opts);
  Zlib.call(this, opts, binding.INFLATE);
}

// gzip - bigger header, same deflate compression
function Gzip(opts) {
  if (!(this instanceof Gzip)) return new Gzip(opts);
  Zlib.call(this, opts, binding.GZIP);
}

function Gunzip(opts) {
  if (!(this instanceof Gunzip)) return new Gunzip(opts);
  Zlib.call(this, opts, binding.GUNZIP);
}

// raw - no header
function DeflateRaw(opts) {
  if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts);
  Zlib.call(this, opts, binding.DEFLATERAW);
}

function InflateRaw(opts) {
  if (!(this instanceof InflateRaw)) return new InflateRaw(opts);
  Zlib.call(this, opts, binding.INFLATERAW);
}

// auto-detect header.
function Unzip(opts) {
  if (!(this instanceof Unzip)) return new Unzip(opts);
  Zlib.call(this, opts, binding.UNZIP);
}

function isValidFlushFlag(flag) {
  return flag === binding.Z_NO_FLUSH || flag === binding.Z_PARTIAL_FLUSH || flag === binding.Z_SYNC_FLUSH || flag === binding.Z_FULL_FLUSH || flag === binding.Z_FINISH || flag === binding.Z_BLOCK;
}

// the Zlib class they all inherit from
// This thing manages the queue of requests, and returns
// true or false if there is anything in the queue when
// you call the .write() method.

function Zlib(opts, mode) {
  var _this = this;

  this._opts = opts = opts || {};
  this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK;

  Transform.call(this, opts);

  if (opts.flush && !isValidFlushFlag(opts.flush)) {
    throw new Error('Invalid flush flag: ' + opts.flush);
  }
  if (opts.finishFlush && !isValidFlushFlag(opts.finishFlush)) {
    throw new Error('Invalid flush flag: ' + opts.finishFlush);
  }

  this._flushFlag = opts.flush || binding.Z_NO_FLUSH;
  this._finishFlushFlag = typeof opts.finishFlush !== 'undefined' ? opts.finishFlush : binding.Z_FINISH;

  if (opts.chunkSize) {
    if (opts.chunkSize < exports.Z_MIN_CHUNK || opts.chunkSize > exports.Z_MAX_CHUNK) {
      throw new Error('Invalid chunk size: ' + opts.chunkSize);
    }
  }

  if (opts.windowBits) {
    if (opts.windowBits < exports.Z_MIN_WINDOWBITS || opts.windowBits > exports.Z_MAX_WINDOWBITS) {
      throw new Error('Invalid windowBits: ' + opts.windowBits);
    }
  }

  if (opts.level) {
    if (opts.level < exports.Z_MIN_LEVEL || opts.level > exports.Z_MAX_LEVEL) {
      throw new Error('Invalid compression level: ' + opts.level);
    }
  }

  if (opts.memLevel) {
    if (opts.memLevel < exports.Z_MIN_MEMLEVEL || opts.memLevel > exports.Z_MAX_MEMLEVEL) {
      throw new Error('Invalid memLevel: ' + opts.memLevel);
    }
  }

  if (opts.strategy) {
    if (opts.strategy != exports.Z_FILTERED && opts.strategy != exports.Z_HUFFMAN_ONLY && opts.strategy != exports.Z_RLE && opts.strategy != exports.Z_FIXED && opts.strategy != exports.Z_DEFAULT_STRATEGY) {
      throw new Error('Invalid strategy: ' + opts.strategy);
    }
  }

  if (opts.dictionary) {
    if (!Buffer.isBuffer(opts.dictionary)) {
      throw new Error('Invalid dictionary: it should be a Buffer instance');
    }
  }

  this._handle = new binding.Zlib(mode);

  var self = this;
  this._hadError = false;
  this._handle.onerror = function (message, errno) {
    // there is no way to cleanly recover.
    // continuing only obscures problems.
    _close(self);
    self._hadError = true;

    var error = new Error(message);
    error.errno = errno;
    error.code = exports.codes[errno];
    self.emit('error', error);
  };

  var level = exports.Z_DEFAULT_COMPRESSION;
  if (typeof opts.level === 'number') level = opts.level;

  var strategy = exports.Z_DEFAULT_STRATEGY;
  if (typeof opts.strategy === 'number') strategy = opts.strategy;

  this._handle.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS, level, opts.memLevel || exports.Z_DEFAULT_MEMLEVEL, strategy, opts.dictionary);

  this._buffer = Buffer.allocUnsafe(this._chunkSize);
  this._offset = 0;
  this._level = level;
  this._strategy = strategy;

  this.once('end', this.close);

  Object.defineProperty(this, '_closed', {
    get: function () {
      return !_this._handle;
    },
    configurable: true,
    enumerable: true
  });
}

util.inherits(Zlib, Transform);

Zlib.prototype.params = function (level, strategy, callback) {
  if (level < exports.Z_MIN_LEVEL || level > exports.Z_MAX_LEVEL) {
    throw new RangeError('Invalid compression level: ' + level);
  }
  if (strategy != exports.Z_FILTERED && strategy != exports.Z_HUFFMAN_ONLY && strategy != exports.Z_RLE && strategy != exports.Z_FIXED && strategy != exports.Z_DEFAULT_STRATEGY) {
    throw new TypeError('Invalid strategy: ' + strategy);
  }

  if (this._level !== level || this._strategy !== strategy) {
    var self = this;
    this.flush(binding.Z_SYNC_FLUSH, function () {
      assert(self._handle, 'zlib binding closed');
      self._handle.params(level, strategy);
      if (!self._hadError) {
        self._level = level;
        self._strategy = strategy;
        if (callback) callback();
      }
    });
  } else {
    process.nextTick(callback);
  }
};

Zlib.prototype.reset = function () {
  assert(this._handle, 'zlib binding closed');
  return this._handle.reset();
};

// This is the _flush function called by the transform class,
// internally, when the last chunk has been written.
Zlib.prototype._flush = function (callback) {
  this._transform(Buffer.alloc(0), '', callback);
};

Zlib.prototype.flush = function (kind, callback) {
  var _this2 = this;

  var ws = this._writableState;

  if (typeof kind === 'function' || kind === undefined && !callback) {
    callback = kind;
    kind = binding.Z_FULL_FLUSH;
  }

  if (ws.ended) {
    if (callback) process.nextTick(callback);
  } else if (ws.ending) {
    if (callback) this.once('end', callback);
  } else if (ws.needDrain) {
    if (callback) {
      this.once('drain', function () {
        return _this2.flush(kind, callback);
      });
    }
  } else {
    this._flushFlag = kind;
    this.write(Buffer.alloc(0), '', callback);
  }
};

Zlib.prototype.close = function (callback) {
  _close(this, callback);
  process.nextTick(emitCloseNT, this);
};

function _close(engine, callback) {
  if (callback) process.nextTick(callback);

  // Caller may invoke .close after a zlib error (which will null _handle).
  if (!engine._handle) return;

  engine._handle.close();
  engine._handle = null;
}

function emitCloseNT(self) {
  self.emit('close');
}

Zlib.prototype._transform = function (chunk, encoding, cb) {
  var flushFlag;
  var ws = this._writableState;
  var ending = ws.ending || ws.ended;
  var last = ending && (!chunk || ws.length === chunk.length);

  if (chunk !== null && !Buffer.isBuffer(chunk)) return cb(new Error('invalid input'));

  if (!this._handle) return cb(new Error('zlib binding closed'));

  // If it's the last chunk, or a final flush, we use the Z_FINISH flush flag
  // (or whatever flag was provided using opts.finishFlush).
  // If it's explicitly flushing at some other time, then we use
  // Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression
  // goodness.
  if (last) flushFlag = this._finishFlushFlag;else {
    flushFlag = this._flushFlag;
    // once we've flushed the last of the queue, stop flushing and
    // go back to the normal behavior.
    if (chunk.length >= ws.length) {
      this._flushFlag = this._opts.flush || binding.Z_NO_FLUSH;
    }
  }

  this._processChunk(chunk, flushFlag, cb);
};

Zlib.prototype._processChunk = function (chunk, flushFlag, cb) {
  var availInBefore = chunk && chunk.length;
  var availOutBefore = this._chunkSize - this._offset;
  var inOff = 0;

  var self = this;

  var async = typeof cb === 'function';

  if (!async) {
    var buffers = [];
    var nread = 0;

    var error;
    this.on('error', function (er) {
      error = er;
    });

    assert(this._handle, 'zlib binding closed');
    do {
      var res = this._handle.writeSync(flushFlag, chunk, // in
      inOff, // in_off
      availInBefore, // in_len
      this._buffer, // out
      this._offset, //out_off
      availOutBefore); // out_len
    } while (!this._hadError && callback(res[0], res[1]));

    if (this._hadError) {
      throw error;
    }

    if (nread >= kMaxLength) {
      _close(this);
      throw new RangeError(kRangeErrorMessage);
    }

    var buf = Buffer.concat(buffers, nread);
    _close(this);

    return buf;
  }

  assert(this._handle, 'zlib binding closed');
  var req = this._handle.write(flushFlag, chunk, // in
  inOff, // in_off
  availInBefore, // in_len
  this._buffer, // out
  this._offset, //out_off
  availOutBefore); // out_len

  req.buffer = chunk;
  req.callback = callback;

  function callback(availInAfter, availOutAfter) {
    // When the callback is used in an async write, the callback's
    // context is the `req` object that was created. The req object
    // is === this._handle, and that's why it's important to null
    // out the values after they are done being used. `this._handle`
    // can stay in memory longer than the callback and buffer are needed.
    if (this) {
      this.buffer = null;
      this.callback = null;
    }

    if (self._hadError) return;

    var have = availOutBefore - availOutAfter;
    assert(have >= 0, 'have should not go down');

    if (have > 0) {
      var out = self._buffer.slice(self._offset, self._offset + have);
      self._offset += have;
      // serve some output to the consumer.
      if (async) {
        self.push(out);
      } else {
        buffers.push(out);
        nread += out.length;
      }
    }

    // exhausted the output buffer, or used all the input create a new one.
    if (availOutAfter === 0 || self._offset >= self._chunkSize) {
      availOutBefore = self._chunkSize;
      self._offset = 0;
      self._buffer = Buffer.allocUnsafe(self._chunkSize);
    }

    if (availOutAfter === 0) {
      // Not actually done.  Need to reprocess.
      // Also, update the availInBefore to the availInAfter value,
      // so that if we have to hit it a third (fourth, etc.) time,
      // it'll have the correct byte counts.
      inOff += availInBefore - availInAfter;
      availInBefore = availInAfter;

      if (!async) return true;

      var newReq = self._handle.write(flushFlag, chunk, inOff, availInBefore, self._buffer, self._offset, self._chunkSize);
      newReq.callback = callback; // this same function
      newReq.buffer = chunk;
      return;
    }

    if (!async) return false;

    // finished with the chunk.
    cb();
  }
};

util.inherits(Deflate, Zlib);
util.inherits(Inflate, Zlib);
util.inherits(Gzip, Zlib);
util.inherits(Gunzip, Zlib);
util.inherits(DeflateRaw, Zlib);
util.inherits(InflateRaw, Zlib);
util.inherits(Unzip, Zlib);
}).call(this)}).call(this,require('_process'))
},{"./binding":157,"_process":191,"assert":150,"buffer":159,"stream":193,"util":214}],159:[function(require,module,exports){
(function (Buffer){(function (){
/*!
 * The buffer module from node.js, for the browser.
 *
 * @author   Feross Aboukhadijeh <https://feross.org>
 * @license  MIT
 */
/* eslint-disable no-proto */

'use strict'

var base64 = require('base64-js')
var ieee754 = require('ieee754')

exports.Buffer = Buffer
exports.SlowBuffer = SlowBuffer
exports.INSPECT_MAX_BYTES = 50

var K_MAX_LENGTH = 0x7fffffff
exports.kMaxLength = K_MAX_LENGTH

/**
 * If `Buffer.TYPED_ARRAY_SUPPORT`:
 *   === true    Use Uint8Array implementation (fastest)
 *   === false   Print warning and recommend using `buffer` v4.x which has an Object
 *               implementation (most compatible, even IE6)
 *
 * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
 * Opera 11.6+, iOS 4.2+.
 *
 * We report that the browser does not support typed arrays if the are not subclassable
 * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array`
 * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support
 * for __proto__ and has a buggy typed array implementation.
 */
Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport()

if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' &&
    typeof console.error === 'function') {
  console.error(
    'This browser lacks typed array (Uint8Array) support which is required by ' +
    '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.'
  )
}

function typedArraySupport () {
  // Can typed array instances can be augmented?
  try {
    var arr = new Uint8Array(1)
    arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } }
    return arr.foo() === 42
  } catch (e) {
    return false
  }
}

Object.defineProperty(Buffer.prototype, 'parent', {
  enumerable: true,
  get: function () {
    if (!Buffer.isBuffer(this)) return undefined
    return this.buffer
  }
})

Object.defineProperty(Buffer.prototype, 'offset', {
  enumerable: true,
  get: function () {
    if (!Buffer.isBuffer(this)) return undefined
    return this.byteOffset
  }
})

function createBuffer (length) {
  if (length > K_MAX_LENGTH) {
    throw new RangeError('The value "' + length + '" is invalid for option "size"')
  }
  // Return an augmented `Uint8Array` instance
  var buf = new Uint8Array(length)
  buf.__proto__ = Buffer.prototype
  return buf
}

/**
 * The Buffer constructor returns instances of `Uint8Array` that have their
 * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
 * `Uint8Array`, so the returned instances will have all the node `Buffer` methods
 * and the `Uint8Array` methods. Square bracket notation works as expected -- it
 * returns a single octet.
 *
 * The `Uint8Array` prototype remains unmodified.
 */

function Buffer (arg, encodingOrOffset, length) {
  // Common case.
  if (typeof arg === 'number') {
    if (typeof encodingOrOffset === 'string') {
      throw new TypeError(
        'The "string" argument must be of type string. Received type number'
      )
    }
    return allocUnsafe(arg)
  }
  return from(arg, encodingOrOffset, length)
}

// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
if (typeof Symbol !== 'undefined' && Symbol.species != null &&
    Buffer[Symbol.species] === Buffer) {
  Object.defineProperty(Buffer, Symbol.species, {
    value: null,
    configurable: true,
    enumerable: false,
    writable: false
  })
}

Buffer.poolSize = 8192 // not used by this implementation

function from (value, encodingOrOffset, length) {
  if (typeof value === 'string') {
    return fromString(value, encodingOrOffset)
  }

  if (ArrayBuffer.isView(value)) {
    return fromArrayLike(value)
  }

  if (value == null) {
    throw TypeError(
      'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
      'or Array-like Object. Received type ' + (typeof value)
    )
  }

  if (isInstance(value, ArrayBuffer) ||
      (value && isInstance(value.buffer, ArrayBuffer))) {
    return fromArrayBuffer(value, encodingOrOffset, length)
  }

  if (typeof value === 'number') {
    throw new TypeError(
      'The "value" argument must not be of type number. Received type number'
    )
  }

  var valueOf = value.valueOf && value.valueOf()
  if (valueOf != null && valueOf !== value) {
    return Buffer.from(valueOf, encodingOrOffset, length)
  }

  var b = fromObject(value)
  if (b) return b

  if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null &&
      typeof value[Symbol.toPrimitive] === 'function') {
    return Buffer.from(
      value[Symbol.toPrimitive]('string'), encodingOrOffset, length
    )
  }

  throw new TypeError(
    'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
    'or Array-like Object. Received type ' + (typeof value)
  )
}

/**
 * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
 * if value is a number.
 * Buffer.from(str[, encoding])
 * Buffer.from(array)
 * Buffer.from(buffer)
 * Buffer.from(arrayBuffer[, byteOffset[, length]])
 **/
Buffer.from = function (value, encodingOrOffset, length) {
  return from(value, encodingOrOffset, length)
}

// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug:
// https://github.com/feross/buffer/pull/148
Buffer.prototype.__proto__ = Uint8Array.prototype
Buffer.__proto__ = Uint8Array

function assertSize (size) {
  if (typeof size !== 'number') {
    throw new TypeError('"size" argument must be of type number')
  } else if (size < 0) {
    throw new RangeError('The value "' + size + '" is invalid for option "size"')
  }
}

function alloc (size, fill, encoding) {
  assertSize(size)
  if (size <= 0) {
    return createBuffer(size)
  }
  if (fill !== undefined) {
    // Only pay attention to encoding if it's a string. This
    // prevents accidentally sending in a number that would
    // be interpretted as a start offset.
    return typeof encoding === 'string'
      ? createBuffer(size).fill(fill, encoding)
      : createBuffer(size).fill(fill)
  }
  return createBuffer(size)
}

/**
 * Creates a new filled Buffer instance.
 * alloc(size[, fill[, encoding]])
 **/
Buffer.alloc = function (size, fill, encoding) {
  return alloc(size, fill, encoding)
}

function allocUnsafe (size) {
  assertSize(size)
  return createBuffer(size < 0 ? 0 : checked(size) | 0)
}

/**
 * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
 * */
Buffer.allocUnsafe = function (size) {
  return allocUnsafe(size)
}
/**
 * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
 */
Buffer.allocUnsafeSlow = function (size) {
  return allocUnsafe(size)
}

function fromString (string, encoding) {
  if (typeof encoding !== 'string' || encoding === '') {
    encoding = 'utf8'
  }

  if (!Buffer.isEncoding(encoding)) {
    throw new TypeError('Unknown encoding: ' + encoding)
  }

  var length = byteLength(string, encoding) | 0
  var buf = createBuffer(length)

  var actual = buf.write(string, encoding)

  if (actual !== length) {
    // Writing a hex string, for example, that contains invalid characters will
    // cause everything after the first invalid character to be ignored. (e.g.
    // 'abxxcd' will be treated as 'ab')
    buf = buf.slice(0, actual)
  }

  return buf
}

function fromArrayLike (array) {
  var length = array.length < 0 ? 0 : checked(array.length) | 0
  var buf = createBuffer(length)
  for (var i = 0; i < length; i += 1) {
    buf[i] = array[i] & 255
  }
  return buf
}

function fromArrayBuffer (array, byteOffset, length) {
  if (byteOffset < 0 || array.byteLength < byteOffset) {
    throw new RangeError('"offset" is outside of buffer bounds')
  }

  if (array.byteLength < byteOffset + (length || 0)) {
    throw new RangeError('"length" is outside of buffer bounds')
  }

  var buf
  if (byteOffset === undefined && length === undefined) {
    buf = new Uint8Array(array)
  } else if (length === undefined) {
    buf = new Uint8Array(array, byteOffset)
  } else {
    buf = new Uint8Array(array, byteOffset, length)
  }

  // Return an augmented `Uint8Array` instance
  buf.__proto__ = Buffer.prototype
  return buf
}

function fromObject (obj) {
  if (Buffer.isBuffer(obj)) {
    var len = checked(obj.length) | 0
    var buf = createBuffer(len)

    if (buf.length === 0) {
      return buf
    }

    obj.copy(buf, 0, 0, len)
    return buf
  }

  if (obj.length !== undefined) {
    if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) {
      return createBuffer(0)
    }
    return fromArrayLike(obj)
  }

  if (obj.type === 'Buffer' && Array.isArray(obj.data)) {
    return fromArrayLike(obj.data)
  }
}

function checked (length) {
  // Note: cannot use `length < K_MAX_LENGTH` here because that fails when
  // length is NaN (which is otherwise coerced to zero.)
  if (length >= K_MAX_LENGTH) {
    throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
                         'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes')
  }
  return length | 0
}

function SlowBuffer (length) {
  if (+length != length) { // eslint-disable-line eqeqeq
    length = 0
  }
  return Buffer.alloc(+length)
}

Buffer.isBuffer = function isBuffer (b) {
  return b != null && b._isBuffer === true &&
    b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false
}

Buffer.compare = function compare (a, b) {
  if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength)
  if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength)
  if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
    throw new TypeError(
      'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array'
    )
  }

  if (a === b) return 0

  var x = a.length
  var y = b.length

  for (var i = 0, len = Math.min(x, y); i < len; ++i) {
    if (a[i] !== b[i]) {
      x = a[i]
      y = b[i]
      break
    }
  }

  if (x < y) return -1
  if (y < x) return 1
  return 0
}

Buffer.isEncoding = function isEncoding (encoding) {
  switch (String(encoding).toLowerCase()) {
    case 'hex':
    case 'utf8':
    case 'utf-8':
    case 'ascii':
    case 'latin1':
    case 'binary':
    case 'base64':
    case 'ucs2':
    case 'ucs-2':
    case 'utf16le':
    case 'utf-16le':
      return true
    default:
      return false
  }
}

Buffer.concat = function concat (list, length) {
  if (!Array.isArray(list)) {
    throw new TypeError('"list" argument must be an Array of Buffers')
  }

  if (list.length === 0) {
    return Buffer.alloc(0)
  }

  var i
  if (length === undefined) {
    length = 0
    for (i = 0; i < list.length; ++i) {
      length += list[i].length
    }
  }

  var buffer = Buffer.allocUnsafe(length)
  var pos = 0
  for (i = 0; i < list.length; ++i) {
    var buf = list[i]
    if (isInstance(buf, Uint8Array)) {
      buf = Buffer.from(buf)
    }
    if (!Buffer.isBuffer(buf)) {
      throw new TypeError('"list" argument must be an Array of Buffers')
    }
    buf.copy(buffer, pos)
    pos += buf.length
  }
  return buffer
}

function byteLength (string, encoding) {
  if (Buffer.isBuffer(string)) {
    return string.length
  }
  if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) {
    return string.byteLength
  }
  if (typeof string !== 'string') {
    throw new TypeError(
      'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' +
      'Received type ' + typeof string
    )
  }

  var len = string.length
  var mustMatch = (arguments.length > 2 && arguments[2] === true)
  if (!mustMatch && len === 0) return 0

  // Use a for loop to avoid recursion
  var loweredCase = false
  for (;;) {
    switch (encoding) {
      case 'ascii':
      case 'latin1':
      case 'binary':
        return len
      case 'utf8':
      case 'utf-8':
        return utf8ToBytes(string).length
      case 'ucs2':
      case 'ucs-2':
      case 'utf16le':
      case 'utf-16le':
        return len * 2
      case 'hex':
        return len >>> 1
      case 'base64':
        return base64ToBytes(string).length
      default:
        if (loweredCase) {
          return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8
        }
        encoding = ('' + encoding).toLowerCase()
        loweredCase = true
    }
  }
}
Buffer.byteLength = byteLength

function slowToString (encoding, start, end) {
  var loweredCase = false

  // No need to verify that "this.length <= MAX_UINT32" since it's a read-only
  // property of a typed array.

  // This behaves neither like String nor Uint8Array in that we set start/end
  // to their upper/lower bounds if the value passed is out of range.
  // undefined is handled specially as per ECMA-262 6th Edition,
  // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
  if (start === undefined || start < 0) {
    start = 0
  }
  // Return early if start > this.length. Done here to prevent potential uint32
  // coercion fail below.
  if (start > this.length) {
    return ''
  }

  if (end === undefined || end > this.length) {
    end = this.length
  }

  if (end <= 0) {
    return ''
  }

  // Force coersion to uint32. This will also coerce falsey/NaN values to 0.
  end >>>= 0
  start >>>= 0

  if (end <= start) {
    return ''
  }

  if (!encoding) encoding = 'utf8'

  while (true) {
    switch (encoding) {
      case 'hex':
        return hexSlice(this, start, end)

      case 'utf8':
      case 'utf-8':
        return utf8Slice(this, start, end)

      case 'ascii':
        return asciiSlice(this, start, end)

      case 'latin1':
      case 'binary':
        return latin1Slice(this, start, end)

      case 'base64':
        return base64Slice(this, start, end)

      case 'ucs2':
      case 'ucs-2':
      case 'utf16le':
      case 'utf-16le':
        return utf16leSlice(this, start, end)

      default:
        if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
        encoding = (encoding + '').toLowerCase()
        loweredCase = true
    }
  }
}

// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package)
// to detect a Buffer instance. It's not possible to use `instanceof Buffer`
// reliably in a browserify context because there could be multiple different
// copies of the 'buffer' package in use. This method works even for Buffer
// instances that were created from another copy of the `buffer` package.
// See: https://github.com/feross/buffer/issues/154
Buffer.prototype._isBuffer = true

function swap (b, n, m) {
  var i = b[n]
  b[n] = b[m]
  b[m] = i
}

Buffer.prototype.swap16 = function swap16 () {
  var len = this.length
  if (len % 2 !== 0) {
    throw new RangeError('Buffer size must be a multiple of 16-bits')
  }
  for (var i = 0; i < len; i += 2) {
    swap(this, i, i + 1)
  }
  return this
}

Buffer.prototype.swap32 = function swap32 () {
  var len = this.length
  if (len % 4 !== 0) {
    throw new RangeError('Buffer size must be a multiple of 32-bits')
  }
  for (var i = 0; i < len; i += 4) {
    swap(this, i, i + 3)
    swap(this, i + 1, i + 2)
  }
  return this
}

Buffer.prototype.swap64 = function swap64 () {
  var len = this.length
  if (len % 8 !== 0) {
    throw new RangeError('Buffer size must be a multiple of 64-bits')
  }
  for (var i = 0; i < len; i += 8) {
    swap(this, i, i + 7)
    swap(this, i + 1, i + 6)
    swap(this, i + 2, i + 5)
    swap(this, i + 3, i + 4)
  }
  return this
}

Buffer.prototype.toString = function toString () {
  var length = this.length
  if (length === 0) return ''
  if (arguments.length === 0) return utf8Slice(this, 0, length)
  return slowToString.apply(this, arguments)
}

Buffer.prototype.toLocaleString = Buffer.prototype.toString

Buffer.prototype.equals = function equals (b) {
  if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
  if (this === b) return true
  return Buffer.compare(this, b) === 0
}

Buffer.prototype.inspect = function inspect () {
  var str = ''
  var max = exports.INSPECT_MAX_BYTES
  str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim()
  if (this.length > max) str += ' ... '
  return '<Buffer ' + str + '>'
}

Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
  if (isInstance(target, Uint8Array)) {
    target = Buffer.from(target, target.offset, target.byteLength)
  }
  if (!Buffer.isBuffer(target)) {
    throw new TypeError(
      'The "target" argument must be one of type Buffer or Uint8Array. ' +
      'Received type ' + (typeof target)
    )
  }

  if (start === undefined) {
    start = 0
  }
  if (end === undefined) {
    end = target ? target.length : 0
  }
  if (thisStart === undefined) {
    thisStart = 0
  }
  if (thisEnd === undefined) {
    thisEnd = this.length
  }

  if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
    throw new RangeError('out of range index')
  }

  if (thisStart >= thisEnd && start >= end) {
    return 0
  }
  if (thisStart >= thisEnd) {
    return -1
  }
  if (start >= end) {
    return 1
  }

  start >>>= 0
  end >>>= 0
  thisStart >>>= 0
  thisEnd >>>= 0

  if (this === target) return 0

  var x = thisEnd - thisStart
  var y = end - start
  var len = Math.min(x, y)

  var thisCopy = this.slice(thisStart, thisEnd)
  var targetCopy = target.slice(start, end)

  for (var i = 0; i < len; ++i) {
    if (thisCopy[i] !== targetCopy[i]) {
      x = thisCopy[i]
      y = targetCopy[i]
      break
    }
  }

  if (x < y) return -1
  if (y < x) return 1
  return 0
}

// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
// OR the last index of `val` in `buffer` at offset <= `byteOffset`.
//
// Arguments:
// - buffer - a Buffer to search
// - val - a string, Buffer, or number
// - byteOffset - an index into `buffer`; will be clamped to an int32
// - encoding - an optional encoding, relevant is val is a string
// - dir - true for indexOf, false for lastIndexOf
function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
  // Empty buffer means no match
  if (buffer.length === 0) return -1

  // Normalize byteOffset
  if (typeof byteOffset === 'string') {
    encoding = byteOffset
    byteOffset = 0
  } else if (byteOffset > 0x7fffffff) {
    byteOffset = 0x7fffffff
  } else if (byteOffset < -0x80000000) {
    byteOffset = -0x80000000
  }
  byteOffset = +byteOffset // Coerce to Number.
  if (numberIsNaN(byteOffset)) {
    // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
    byteOffset = dir ? 0 : (buffer.length - 1)
  }

  // Normalize byteOffset: negative offsets start from the end of the buffer
  if (byteOffset < 0) byteOffset = buffer.length + byteOffset
  if (byteOffset >= buffer.length) {
    if (dir) return -1
    else byteOffset = buffer.length - 1
  } else if (byteOffset < 0) {
    if (dir) byteOffset = 0
    else return -1
  }

  // Normalize val
  if (typeof val === 'string') {
    val = Buffer.from(val, encoding)
  }

  // Finally, search either indexOf (if dir is true) or lastIndexOf
  if (Buffer.isBuffer(val)) {
    // Special case: looking for empty string/buffer always fails
    if (val.length === 0) {
      return -1
    }
    return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
  } else if (typeof val === 'number') {
    val = val & 0xFF // Search for a byte value [0-255]
    if (typeof Uint8Array.prototype.indexOf === 'function') {
      if (dir) {
        return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
      } else {
        return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
      }
    }
    return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
  }

  throw new TypeError('val must be string, number or Buffer')
}

function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
  var indexSize = 1
  var arrLength = arr.length
  var valLength = val.length

  if (encoding !== undefined) {
    encoding = String(encoding).toLowerCase()
    if (encoding === 'ucs2' || encoding === 'ucs-2' ||
        encoding === 'utf16le' || encoding === 'utf-16le') {
      if (arr.length < 2 || val.length < 2) {
        return -1
      }
      indexSize = 2
      arrLength /= 2
      valLength /= 2
      byteOffset /= 2
    }
  }

  function read (buf, i) {
    if (indexSize === 1) {
      return buf[i]
    } else {
      return buf.readUInt16BE(i * indexSize)
    }
  }

  var i
  if (dir) {
    var foundIndex = -1
    for (i = byteOffset; i < arrLength; i++) {
      if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
        if (foundIndex === -1) foundIndex = i
        if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
      } else {
        if (foundIndex !== -1) i -= i - foundIndex
        foundIndex = -1
      }
    }
  } else {
    if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
    for (i = byteOffset; i >= 0; i--) {
      var found = true
      for (var j = 0; j < valLength; j++) {
        if (read(arr, i + j) !== read(val, j)) {
          found = false
          break
        }
      }
      if (found) return i
    }
  }

  return -1
}

Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
  return this.indexOf(val, byteOffset, encoding) !== -1
}

Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
  return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
}

Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
  return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
}

function hexWrite (buf, string, offset, length) {
  offset = Number(offset) || 0
  var remaining = buf.length - offset
  if (!length) {
    length = remaining
  } else {
    length = Number(length)
    if (length > remaining) {
      length = remaining
    }
  }

  var strLen = string.length

  if (length > strLen / 2) {
    length = strLen / 2
  }
  for (var i = 0; i < length; ++i) {
    var parsed = parseInt(string.substr(i * 2, 2), 16)
    if (numberIsNaN(parsed)) return i
    buf[offset + i] = parsed
  }
  return i
}

function utf8Write (buf, string, offset, length) {
  return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
}

function asciiWrite (buf, string, offset, length) {
  return blitBuffer(asciiToBytes(string), buf, offset, length)
}

function latin1Write (buf, string, offset, length) {
  return asciiWrite(buf, string, offset, length)
}

function base64Write (buf, string, offset, length) {
  return blitBuffer(base64ToBytes(string), buf, offset, length)
}

function ucs2Write (buf, string, offset, length) {
  return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
}

Buffer.prototype.write = function write (string, offset, length, encoding) {
  // Buffer#write(string)
  if (offset === undefined) {
    encoding = 'utf8'
    length = this.length
    offset = 0
  // Buffer#write(string, encoding)
  } else if (length === undefined && typeof offset === 'string') {
    encoding = offset
    length = this.length
    offset = 0
  // Buffer#write(string, offset[, length][, encoding])
  } else if (isFinite(offset)) {
    offset = offset >>> 0
    if (isFinite(length)) {
      length = length >>> 0
      if (encoding === undefined) encoding = 'utf8'
    } else {
      encoding = length
      length = undefined
    }
  } else {
    throw new Error(
      'Buffer.write(string, encoding, offset[, length]) is no longer supported'
    )
  }

  var remaining = this.length - offset
  if (length === undefined || length > remaining) length = remaining

  if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
    throw new RangeError('Attempt to write outside buffer bounds')
  }

  if (!encoding) encoding = 'utf8'

  var loweredCase = false
  for (;;) {
    switch (encoding) {
      case 'hex':
        return hexWrite(this, string, offset, length)

      case 'utf8':
      case 'utf-8':
        return utf8Write(this, string, offset, length)

      case 'ascii':
        return asciiWrite(this, string, offset, length)

      case 'latin1':
      case 'binary':
        return latin1Write(this, string, offset, length)

      case 'base64':
        // Warning: maxLength not taken into account in base64Write
        return base64Write(this, string, offset, length)

      case 'ucs2':
      case 'ucs-2':
      case 'utf16le':
      case 'utf-16le':
        return ucs2Write(this, string, offset, length)

      default:
        if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
        encoding = ('' + encoding).toLowerCase()
        loweredCase = true
    }
  }
}

Buffer.prototype.toJSON = function toJSON () {
  return {
    type: 'Buffer',
    data: Array.prototype.slice.call(this._arr || this, 0)
  }
}

function base64Slice (buf, start, end) {
  if (start === 0 && end === buf.length) {
    return base64.fromByteArray(buf)
  } else {
    return base64.fromByteArray(buf.slice(start, end))
  }
}

function utf8Slice (buf, start, end) {
  end = Math.min(buf.length, end)
  var res = []

  var i = start
  while (i < end) {
    var firstByte = buf[i]
    var codePoint = null
    var bytesPerSequence = (firstByte > 0xEF) ? 4
      : (firstByte > 0xDF) ? 3
        : (firstByte > 0xBF) ? 2
          : 1

    if (i + bytesPerSequence <= end) {
      var secondByte, thirdByte, fourthByte, tempCodePoint

      switch (bytesPerSequence) {
        case 1:
          if (firstByte < 0x80) {
            codePoint = firstByte
          }
          break
        case 2:
          secondByte = buf[i + 1]
          if ((secondByte & 0xC0) === 0x80) {
            tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
            if (tempCodePoint > 0x7F) {
              codePoint = tempCodePoint
            }
          }
          break
        case 3:
          secondByte = buf[i + 1]
          thirdByte = buf[i + 2]
          if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
            tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
            if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
              codePoint = tempCodePoint
            }
          }
          break
        case 4:
          secondByte = buf[i + 1]
          thirdByte = buf[i + 2]
          fourthByte = buf[i + 3]
          if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
            tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
            if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
              codePoint = tempCodePoint
            }
          }
      }
    }

    if (codePoint === null) {
      // we did not generate a valid codePoint so insert a
      // replacement char (U+FFFD) and advance only 1 byte
      codePoint = 0xFFFD
      bytesPerSequence = 1
    } else if (codePoint > 0xFFFF) {
      // encode to utf16 (surrogate pair dance)
      codePoint -= 0x10000
      res.push(codePoint >>> 10 & 0x3FF | 0xD800)
      codePoint = 0xDC00 | codePoint & 0x3FF
    }

    res.push(codePoint)
    i += bytesPerSequence
  }

  return decodeCodePointsArray(res)
}

// Based on http://stackoverflow.com/a/22747272/680742, the browser with
// the lowest limit is Chrome, with 0x10000 args.
// We go 1 magnitude less, for safety
var MAX_ARGUMENTS_LENGTH = 0x1000

function decodeCodePointsArray (codePoints) {
  var len = codePoints.length
  if (len <= MAX_ARGUMENTS_LENGTH) {
    return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
  }

  // Decode in chunks to avoid "call stack size exceeded".
  var res = ''
  var i = 0
  while (i < len) {
    res += String.fromCharCode.apply(
      String,
      codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
    )
  }
  return res
}

function asciiSlice (buf, start, end) {
  var ret = ''
  end = Math.min(buf.length, end)

  for (var i = start; i < end; ++i) {
    ret += String.fromCharCode(buf[i] & 0x7F)
  }
  return ret
}

function latin1Slice (buf, start, end) {
  var ret = ''
  end = Math.min(buf.length, end)

  for (var i = start; i < end; ++i) {
    ret += String.fromCharCode(buf[i])
  }
  return ret
}

function hexSlice (buf, start, end) {
  var len = buf.length

  if (!start || start < 0) start = 0
  if (!end || end < 0 || end > len) end = len

  var out = ''
  for (var i = start; i < end; ++i) {
    out += toHex(buf[i])
  }
  return out
}

function utf16leSlice (buf, start, end) {
  var bytes = buf.slice(start, end)
  var res = ''
  for (var i = 0; i < bytes.length; i += 2) {
    res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256))
  }
  return res
}

Buffer.prototype.slice = function slice (start, end) {
  var len = this.length
  start = ~~start
  end = end === undefined ? len : ~~end

  if (start < 0) {
    start += len
    if (start < 0) start = 0
  } else if (start > len) {
    start = len
  }

  if (end < 0) {
    end += len
    if (end < 0) end = 0
  } else if (end > len) {
    end = len
  }

  if (end < start) end = start

  var newBuf = this.subarray(start, end)
  // Return an augmented `Uint8Array` instance
  newBuf.__proto__ = Buffer.prototype
  return newBuf
}

/*
 * Need to make sure that buffer isn't trying to write out of bounds.
 */
function checkOffset (offset, ext, length) {
  if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
  if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
}

Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
  offset = offset >>> 0
  byteLength = byteLength >>> 0
  if (!noAssert) checkOffset(offset, byteLength, this.length)

  var val = this[offset]
  var mul = 1
  var i = 0
  while (++i < byteLength && (mul *= 0x100)) {
    val += this[offset + i] * mul
  }

  return val
}

Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
  offset = offset >>> 0
  byteLength = byteLength >>> 0
  if (!noAssert) {
    checkOffset(offset, byteLength, this.length)
  }

  var val = this[offset + --byteLength]
  var mul = 1
  while (byteLength > 0 && (mul *= 0x100)) {
    val += this[offset + --byteLength] * mul
  }

  return val
}

Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 1, this.length)
  return this[offset]
}

Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 2, this.length)
  return this[offset] | (this[offset + 1] << 8)
}

Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 2, this.length)
  return (this[offset] << 8) | this[offset + 1]
}

Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 4, this.length)

  return ((this[offset]) |
      (this[offset + 1] << 8) |
      (this[offset + 2] << 16)) +
      (this[offset + 3] * 0x1000000)
}

Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 4, this.length)

  return (this[offset] * 0x1000000) +
    ((this[offset + 1] << 16) |
    (this[offset + 2] << 8) |
    this[offset + 3])
}

Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
  offset = offset >>> 0
  byteLength = byteLength >>> 0
  if (!noAssert) checkOffset(offset, byteLength, this.length)

  var val = this[offset]
  var mul = 1
  var i = 0
  while (++i < byteLength && (mul *= 0x100)) {
    val += this[offset + i] * mul
  }
  mul *= 0x80

  if (val >= mul) val -= Math.pow(2, 8 * byteLength)

  return val
}

Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
  offset = offset >>> 0
  byteLength = byteLength >>> 0
  if (!noAssert) checkOffset(offset, byteLength, this.length)

  var i = byteLength
  var mul = 1
  var val = this[offset + --i]
  while (i > 0 && (mul *= 0x100)) {
    val += this[offset + --i] * mul
  }
  mul *= 0x80

  if (val >= mul) val -= Math.pow(2, 8 * byteLength)

  return val
}

Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 1, this.length)
  if (!(this[offset] & 0x80)) return (this[offset])
  return ((0xff - this[offset] + 1) * -1)
}

Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 2, this.length)
  var val = this[offset] | (this[offset + 1] << 8)
  return (val & 0x8000) ? val | 0xFFFF0000 : val
}

Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 2, this.length)
  var val = this[offset + 1] | (this[offset] << 8)
  return (val & 0x8000) ? val | 0xFFFF0000 : val
}

Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 4, this.length)

  return (this[offset]) |
    (this[offset + 1] << 8) |
    (this[offset + 2] << 16) |
    (this[offset + 3] << 24)
}

Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 4, this.length)

  return (this[offset] << 24) |
    (this[offset + 1] << 16) |
    (this[offset + 2] << 8) |
    (this[offset + 3])
}

Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 4, this.length)
  return ieee754.read(this, offset, true, 23, 4)
}

Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 4, this.length)
  return ieee754.read(this, offset, false, 23, 4)
}

Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 8, this.length)
  return ieee754.read(this, offset, true, 52, 8)
}

Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
  offset = offset >>> 0
  if (!noAssert) checkOffset(offset, 8, this.length)
  return ieee754.read(this, offset, false, 52, 8)
}

function checkInt (buf, value, offset, ext, max, min) {
  if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
  if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
  if (offset + ext > buf.length) throw new RangeError('Index out of range')
}

Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
  value = +value
  offset = offset >>> 0
  byteLength = byteLength >>> 0
  if (!noAssert) {
    var maxBytes = Math.pow(2, 8 * byteLength) - 1
    checkInt(this, value, offset, byteLength, maxBytes, 0)
  }

  var mul = 1
  var i = 0
  this[offset] = value & 0xFF
  while (++i < byteLength && (mul *= 0x100)) {
    this[offset + i] = (value / mul) & 0xFF
  }

  return offset + byteLength
}

Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
  value = +value
  offset = offset >>> 0
  byteLength = byteLength >>> 0
  if (!noAssert) {
    var maxBytes = Math.pow(2, 8 * byteLength) - 1
    checkInt(this, value, offset, byteLength, maxBytes, 0)
  }

  var i = byteLength - 1
  var mul = 1
  this[offset + i] = value & 0xFF
  while (--i >= 0 && (mul *= 0x100)) {
    this[offset + i] = (value / mul) & 0xFF
  }

  return offset + byteLength
}

Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
  this[offset] = (value & 0xff)
  return offset + 1
}

Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
  this[offset] = (value & 0xff)
  this[offset + 1] = (value >>> 8)
  return offset + 2
}

Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
  this[offset] = (value >>> 8)
  this[offset + 1] = (value & 0xff)
  return offset + 2
}

Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
  this[offset + 3] = (value >>> 24)
  this[offset + 2] = (value >>> 16)
  this[offset + 1] = (value >>> 8)
  this[offset] = (value & 0xff)
  return offset + 4
}

Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
  this[offset] = (value >>> 24)
  this[offset + 1] = (value >>> 16)
  this[offset + 2] = (value >>> 8)
  this[offset + 3] = (value & 0xff)
  return offset + 4
}

Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) {
    var limit = Math.pow(2, (8 * byteLength) - 1)

    checkInt(this, value, offset, byteLength, limit - 1, -limit)
  }

  var i = 0
  var mul = 1
  var sub = 0
  this[offset] = value & 0xFF
  while (++i < byteLength && (mul *= 0x100)) {
    if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
      sub = 1
    }
    this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
  }

  return offset + byteLength
}

Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) {
    var limit = Math.pow(2, (8 * byteLength) - 1)

    checkInt(this, value, offset, byteLength, limit - 1, -limit)
  }

  var i = byteLength - 1
  var mul = 1
  var sub = 0
  this[offset + i] = value & 0xFF
  while (--i >= 0 && (mul *= 0x100)) {
    if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
      sub = 1
    }
    this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
  }

  return offset + byteLength
}

Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
  if (value < 0) value = 0xff + value + 1
  this[offset] = (value & 0xff)
  return offset + 1
}

Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
  this[offset] = (value & 0xff)
  this[offset + 1] = (value >>> 8)
  return offset + 2
}

Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
  this[offset] = (value >>> 8)
  this[offset + 1] = (value & 0xff)
  return offset + 2
}

Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
  this[offset] = (value & 0xff)
  this[offset + 1] = (value >>> 8)
  this[offset + 2] = (value >>> 16)
  this[offset + 3] = (value >>> 24)
  return offset + 4
}

Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
  if (value < 0) value = 0xffffffff + value + 1
  this[offset] = (value >>> 24)
  this[offset + 1] = (value >>> 16)
  this[offset + 2] = (value >>> 8)
  this[offset + 3] = (value & 0xff)
  return offset + 4
}

function checkIEEE754 (buf, value, offset, ext, max, min) {
  if (offset + ext > buf.length) throw new RangeError('Index out of range')
  if (offset < 0) throw new RangeError('Index out of range')
}

function writeFloat (buf, value, offset, littleEndian, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) {
    checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
  }
  ieee754.write(buf, value, offset, littleEndian, 23, 4)
  return offset + 4
}

Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
  return writeFloat(this, value, offset, true, noAssert)
}

Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
  return writeFloat(this, value, offset, false, noAssert)
}

function writeDouble (buf, value, offset, littleEndian, noAssert) {
  value = +value
  offset = offset >>> 0
  if (!noAssert) {
    checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
  }
  ieee754.write(buf, value, offset, littleEndian, 52, 8)
  return offset + 8
}

Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
  return writeDouble(this, value, offset, true, noAssert)
}

Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
  return writeDouble(this, value, offset, false, noAssert)
}

// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
Buffer.prototype.copy = function copy (target, targetStart, start, end) {
  if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer')
  if (!start) start = 0
  if (!end && end !== 0) end = this.length
  if (targetStart >= target.length) targetStart = target.length
  if (!targetStart) targetStart = 0
  if (end > 0 && end < start) end = start

  // Copy 0 bytes; we're done
  if (end === start) return 0
  if (target.length === 0 || this.length === 0) return 0

  // Fatal error conditions
  if (targetStart < 0) {
    throw new RangeError('targetStart out of bounds')
  }
  if (start < 0 || start >= this.length) throw new RangeError('Index out of range')
  if (end < 0) throw new RangeError('sourceEnd out of bounds')

  // Are we oob?
  if (end > this.length) end = this.length
  if (target.length - targetStart < end - start) {
    end = target.length - targetStart + start
  }

  var len = end - start

  if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') {
    // Use built-in when available, missing from IE11
    this.copyWithin(targetStart, start, end)
  } else if (this === target && start < targetStart && targetStart < end) {
    // descending copy from end
    for (var i = len - 1; i >= 0; --i) {
      target[i + targetStart] = this[i + start]
    }
  } else {
    Uint8Array.prototype.set.call(
      target,
      this.subarray(start, end),
      targetStart
    )
  }

  return len
}

// Usage:
//    buffer.fill(number[, offset[, end]])
//    buffer.fill(buffer[, offset[, end]])
//    buffer.fill(string[, offset[, end]][, encoding])
Buffer.prototype.fill = function fill (val, start, end, encoding) {
  // Handle string cases:
  if (typeof val === 'string') {
    if (typeof start === 'string') {
      encoding = start
      start = 0
      end = this.length
    } else if (typeof end === 'string') {
      encoding = end
      end = this.length
    }
    if (encoding !== undefined && typeof encoding !== 'string') {
      throw new TypeError('encoding must be a string')
    }
    if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
      throw new TypeError('Unknown encoding: ' + encoding)
    }
    if (val.length === 1) {
      var code = val.charCodeAt(0)
      if ((encoding === 'utf8' && code < 128) ||
          encoding === 'latin1') {
        // Fast path: If `val` fits into a single byte, use that numeric value.
        val = code
      }
    }
  } else if (typeof val === 'number') {
    val = val & 255
  }

  // Invalid ranges are not set to a default, so can range check early.
  if (start < 0 || this.length < start || this.length < end) {
    throw new RangeError('Out of range index')
  }

  if (end <= start) {
    return this
  }

  start = start >>> 0
  end = end === undefined ? this.length : end >>> 0

  if (!val) val = 0

  var i
  if (typeof val === 'number') {
    for (i = start; i < end; ++i) {
      this[i] = val
    }
  } else {
    var bytes = Buffer.isBuffer(val)
      ? val
      : Buffer.from(val, encoding)
    var len = bytes.length
    if (len === 0) {
      throw new TypeError('The value "' + val +
        '" is invalid for argument "value"')
    }
    for (i = 0; i < end - start; ++i) {
      this[i + start] = bytes[i % len]
    }
  }

  return this
}

// HELPER FUNCTIONS
// ================

var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g

function base64clean (str) {
  // Node takes equal signs as end of the Base64 encoding
  str = str.split('=')[0]
  // Node strips out invalid characters like \n and \t from the string, base64-js does not
  str = str.trim().replace(INVALID_BASE64_RE, '')
  // Node converts strings with length < 2 to ''
  if (str.length < 2) return ''
  // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
  while (str.length % 4 !== 0) {
    str = str + '='
  }
  return str
}

function toHex (n) {
  if (n < 16) return '0' + n.toString(16)
  return n.toString(16)
}

function utf8ToBytes (string, units) {
  units = units || Infinity
  var codePoint
  var length = string.length
  var leadSurrogate = null
  var bytes = []

  for (var i = 0; i < length; ++i) {
    codePoint = string.charCodeAt(i)

    // is surrogate component
    if (codePoint > 0xD7FF && codePoint < 0xE000) {
      // last char was a lead
      if (!leadSurrogate) {
        // no lead yet
        if (codePoint > 0xDBFF) {
          // unexpected trail
          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
          continue
        } else if (i + 1 === length) {
          // unpaired lead
          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
          continue
        }

        // valid lead
        leadSurrogate = codePoint

        continue
      }

      // 2 leads in a row
      if (codePoint < 0xDC00) {
        if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
        leadSurrogate = codePoint
        continue
      }

      // valid surrogate pair
      codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
    } else if (leadSurrogate) {
      // valid bmp char, but last char was a lead
      if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
    }

    leadSurrogate = null

    // encode utf8
    if (codePoint < 0x80) {
      if ((units -= 1) < 0) break
      bytes.push(codePoint)
    } else if (codePoint < 0x800) {
      if ((units -= 2) < 0) break
      bytes.push(
        codePoint >> 0x6 | 0xC0,
        codePoint & 0x3F | 0x80
      )
    } else if (codePoint < 0x10000) {
      if ((units -= 3) < 0) break
      bytes.push(
        codePoint >> 0xC | 0xE0,
        codePoint >> 0x6 & 0x3F | 0x80,
        codePoint & 0x3F | 0x80
      )
    } else if (codePoint < 0x110000) {
      if ((units -= 4) < 0) break
      bytes.push(
        codePoint >> 0x12 | 0xF0,
        codePoint >> 0xC & 0x3F | 0x80,
        codePoint >> 0x6 & 0x3F | 0x80,
        codePoint & 0x3F | 0x80
      )
    } else {
      throw new Error('Invalid code point')
    }
  }

  return bytes
}

function asciiToBytes (str) {
  var byteArray = []
  for (var i = 0; i < str.length; ++i) {
    // Node's code seems to be doing this and not & 0x7F..
    byteArray.push(str.charCodeAt(i) & 0xFF)
  }
  return byteArray
}

function utf16leToBytes (str, units) {
  var c, hi, lo
  var byteArray = []
  for (var i = 0; i < str.length; ++i) {
    if ((units -= 2) < 0) break

    c = str.charCodeAt(i)
    hi = c >> 8
    lo = c % 256
    byteArray.push(lo)
    byteArray.push(hi)
  }

  return byteArray
}

function base64ToBytes (str) {
  return base64.toByteArray(base64clean(str))
}

function blitBuffer (src, dst, offset, length) {
  for (var i = 0; i < length; ++i) {
    if ((i + offset >= dst.length) || (i >= src.length)) break
    dst[i + offset] = src[i]
  }
  return i
}

// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass
// the `instanceof` check but they should be treated as of that type.
// See: https://github.com/feross/buffer/issues/166
function isInstance (obj, type) {
  return obj instanceof type ||
    (obj != null && obj.constructor != null && obj.constructor.name != null &&
      obj.constructor.name === type.name)
}
function numberIsNaN (obj) {
  // For IE11 support
  return obj !== obj // eslint-disable-line no-self-compare
}

}).call(this)}).call(this,require("buffer").Buffer)
},{"base64-js":155,"buffer":159,"ieee754":171}],160:[function(require,module,exports){
'use strict';

var GetIntrinsic = require('get-intrinsic');

var callBind = require('./');

var $indexOf = callBind(GetIntrinsic('String.prototype.indexOf'));

module.exports = function callBoundIntrinsic(name, allowMissing) {
	var intrinsic = GetIntrinsic(name, !!allowMissing);
	if (typeof intrinsic === 'function' && $indexOf(name, '.prototype.') > -1) {
		return callBind(intrinsic);
	}
	return intrinsic;
};

},{"./":161,"get-intrinsic":167}],161:[function(require,module,exports){
'use strict';

var bind = require('function-bind');
var GetIntrinsic = require('get-intrinsic');

var $apply = GetIntrinsic('%Function.prototype.apply%');
var $call = GetIntrinsic('%Function.prototype.call%');
var $reflectApply = GetIntrinsic('%Reflect.apply%', true) || bind.call($call, $apply);

var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true);
var $defineProperty = GetIntrinsic('%Object.defineProperty%', true);
var $max = GetIntrinsic('%Math.max%');

if ($defineProperty) {
	try {
		$defineProperty({}, 'a', { value: 1 });
	} catch (e) {
		// IE 8 has a broken defineProperty
		$defineProperty = null;
	}
}

module.exports = function callBind(originalFunction) {
	var func = $reflectApply(bind, $call, arguments);
	if ($gOPD && $defineProperty) {
		var desc = $gOPD(func, 'length');
		if (desc.configurable) {
			// original length, plus the receiver, minus any additional arguments (after the receiver)
			$defineProperty(
				func,
				'length',
				{ value: 1 + $max(0, originalFunction.length - (arguments.length - 1)) }
			);
		}
	}
	return func;
};

var applyBind = function applyBind() {
	return $reflectApply(bind, $apply, arguments);
};

if ($defineProperty) {
	$defineProperty(module.exports, 'apply', { value: applyBind });
} else {
	module.exports.apply = applyBind;
}

},{"function-bind":166,"get-intrinsic":167}],162:[function(require,module,exports){
'use strict';

var GetIntrinsic = require('get-intrinsic');

var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%');
if ($gOPD) {
	try {
		$gOPD([], 'length');
	} catch (e) {
		// IE 8 has a broken gOPD
		$gOPD = null;
	}
}

module.exports = $gOPD;

},{"get-intrinsic":167}],163:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

'use strict';

var R = typeof Reflect === 'object' ? Reflect : null
var ReflectApply = R && typeof R.apply === 'function'
  ? R.apply
  : function ReflectApply(target, receiver, args) {
    return Function.prototype.apply.call(target, receiver, args);
  }

var ReflectOwnKeys
if (R && typeof R.ownKeys === 'function') {
  ReflectOwnKeys = R.ownKeys
} else if (Object.getOwnPropertySymbols) {
  ReflectOwnKeys = function ReflectOwnKeys(target) {
    return Object.getOwnPropertyNames(target)
      .concat(Object.getOwnPropertySymbols(target));
  };
} else {
  ReflectOwnKeys = function ReflectOwnKeys(target) {
    return Object.getOwnPropertyNames(target);
  };
}

function ProcessEmitWarning(warning) {
  if (console && console.warn) console.warn(warning);
}

var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
  return value !== value;
}

function EventEmitter() {
  EventEmitter.init.call(this);
}
module.exports = EventEmitter;
module.exports.once = once;

// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;

EventEmitter.prototype._events = undefined;
EventEmitter.prototype._eventsCount = 0;
EventEmitter.prototype._maxListeners = undefined;

// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
var defaultMaxListeners = 10;

function checkListener(listener) {
  if (typeof listener !== 'function') {
    throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
  }
}

Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
  enumerable: true,
  get: function() {
    return defaultMaxListeners;
  },
  set: function(arg) {
    if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
      throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
    }
    defaultMaxListeners = arg;
  }
});

EventEmitter.init = function() {

  if (this._events === undefined ||
      this._events === Object.getPrototypeOf(this)._events) {
    this._events = Object.create(null);
    this._eventsCount = 0;
  }

  this._maxListeners = this._maxListeners || undefined;
};

// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
  if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
    throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
  }
  this._maxListeners = n;
  return this;
};

function _getMaxListeners(that) {
  if (that._maxListeners === undefined)
    return EventEmitter.defaultMaxListeners;
  return that._maxListeners;
}

EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
  return _getMaxListeners(this);
};

EventEmitter.prototype.emit = function emit(type) {
  var args = [];
  for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
  var doError = (type === 'error');

  var events = this._events;
  if (events !== undefined)
    doError = (doError && events.error === undefined);
  else if (!doError)
    return false;

  // If there is no 'error' event listener then throw.
  if (doError) {
    var er;
    if (args.length > 0)
      er = args[0];
    if (er instanceof Error) {
      // Note: The comments on the `throw` lines are intentional, they show
      // up in Node's output if this results in an unhandled exception.
      throw er; // Unhandled 'error' event
    }
    // At least give some kind of context to the user
    var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
    err.context = er;
    throw err; // Unhandled 'error' event
  }

  var handler = events[type];

  if (handler === undefined)
    return false;

  if (typeof handler === 'function') {
    ReflectApply(handler, this, args);
  } else {
    var len = handler.length;
    var listeners = arrayClone(handler, len);
    for (var i = 0; i < len; ++i)
      ReflectApply(listeners[i], this, args);
  }

  return true;
};

function _addListener(target, type, listener, prepend) {
  var m;
  var events;
  var existing;

  checkListener(listener);

  events = target._events;
  if (events === undefined) {
    events = target._events = Object.create(null);
    target._eventsCount = 0;
  } else {
    // To avoid recursion in the case that type === "newListener"! Before
    // adding it to the listeners, first emit "newListener".
    if (events.newListener !== undefined) {
      target.emit('newListener', type,
                  listener.listener ? listener.listener : listener);

      // Re-assign `events` because a newListener handler could have caused the
      // this._events to be assigned to a new object
      events = target._events;
    }
    existing = events[type];
  }

  if (existing === undefined) {
    // Optimize the case of one listener. Don't need the extra array object.
    existing = events[type] = listener;
    ++target._eventsCount;
  } else {
    if (typeof existing === 'function') {
      // Adding the second element, need to change to array.
      existing = events[type] =
        prepend ? [listener, existing] : [existing, listener];
      // If we've already got an array, just append.
    } else if (prepend) {
      existing.unshift(listener);
    } else {
      existing.push(listener);
    }

    // Check for listener leak
    m = _getMaxListeners(target);
    if (m > 0 && existing.length > m && !existing.warned) {
      existing.warned = true;
      // No error code for this since it is a Warning
      // eslint-disable-next-line no-restricted-syntax
      var w = new Error('Possible EventEmitter memory leak detected. ' +
                          existing.length + ' ' + String(type) + ' listeners ' +
                          'added. Use emitter.setMaxListeners() to ' +
                          'increase limit');
      w.name = 'MaxListenersExceededWarning';
      w.emitter = target;
      w.type = type;
      w.count = existing.length;
      ProcessEmitWarning(w);
    }
  }

  return target;
}

EventEmitter.prototype.addListener = function addListener(type, listener) {
  return _addListener(this, type, listener, false);
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

EventEmitter.prototype.prependListener =
    function prependListener(type, listener) {
      return _addListener(this, type, listener, true);
    };

function onceWrapper() {
  if (!this.fired) {
    this.target.removeListener(this.type, this.wrapFn);
    this.fired = true;
    if (arguments.length === 0)
      return this.listener.call(this.target);
    return this.listener.apply(this.target, arguments);
  }
}

function _onceWrap(target, type, listener) {
  var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
  var wrapped = onceWrapper.bind(state);
  wrapped.listener = listener;
  state.wrapFn = wrapped;
  return wrapped;
}

EventEmitter.prototype.once = function once(type, listener) {
  checkListener(listener);
  this.on(type, _onceWrap(this, type, listener));
  return this;
};

EventEmitter.prototype.prependOnceListener =
    function prependOnceListener(type, listener) {
      checkListener(listener);
      this.prependListener(type, _onceWrap(this, type, listener));
      return this;
    };

// Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener =
    function removeListener(type, listener) {
      var list, events, position, i, originalListener;

      checkListener(listener);

      events = this._events;
      if (events === undefined)
        return this;

      list = events[type];
      if (list === undefined)
        return this;

      if (list === listener || list.listener === listener) {
        if (--this._eventsCount === 0)
          this._events = Object.create(null);
        else {
          delete events[type];
          if (events.removeListener)
            this.emit('removeListener', type, list.listener || listener);
        }
      } else if (typeof list !== 'function') {
        position = -1;

        for (i = list.length - 1; i >= 0; i--) {
          if (list[i] === listener || list[i].listener === listener) {
            originalListener = list[i].listener;
            position = i;
            break;
          }
        }

        if (position < 0)
          return this;

        if (position === 0)
          list.shift();
        else {
          spliceOne(list, position);
        }

        if (list.length === 1)
          events[type] = list[0];

        if (events.removeListener !== undefined)
          this.emit('removeListener', type, originalListener || listener);
      }

      return this;
    };

EventEmitter.prototype.off = EventEmitter.prototype.removeListener;

EventEmitter.prototype.removeAllListeners =
    function removeAllListeners(type) {
      var listeners, events, i;

      events = this._events;
      if (events === undefined)
        return this;

      // not listening for removeListener, no need to emit
      if (events.removeListener === undefined) {
        if (arguments.length === 0) {
          this._events = Object.create(null);
          this._eventsCount = 0;
        } else if (events[type] !== undefined) {
          if (--this._eventsCount === 0)
            this._events = Object.create(null);
          else
            delete events[type];
        }
        return this;
      }

      // emit removeListener for all listeners on all events
      if (arguments.length === 0) {
        var keys = Object.keys(events);
        var key;
        for (i = 0; i < keys.length; ++i) {
          key = keys[i];
          if (key === 'removeListener') continue;
          this.removeAllListeners(key);
        }
        this.removeAllListeners('removeListener');
        this._events = Object.create(null);
        this._eventsCount = 0;
        return this;
      }

      listeners = events[type];

      if (typeof listeners === 'function') {
        this.removeListener(type, listeners);
      } else if (listeners !== undefined) {
        // LIFO order
        for (i = listeners.length - 1; i >= 0; i--) {
          this.removeListener(type, listeners[i]);
        }
      }

      return this;
    };

function _listeners(target, type, unwrap) {
  var events = target._events;

  if (events === undefined)
    return [];

  var evlistener = events[type];
  if (evlistener === undefined)
    return [];

  if (typeof evlistener === 'function')
    return unwrap ? [evlistener.listener || evlistener] : [evlistener];

  return unwrap ?
    unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}

EventEmitter.prototype.listeners = function listeners(type) {
  return _listeners(this, type, true);
};

EventEmitter.prototype.rawListeners = function rawListeners(type) {
  return _listeners(this, type, false);
};

EventEmitter.listenerCount = function(emitter, type) {
  if (typeof emitter.listenerCount === 'function') {
    return emitter.listenerCount(type);
  } else {
    return listenerCount.call(emitter, type);
  }
};

EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
  var events = this._events;

  if (events !== undefined) {
    var evlistener = events[type];

    if (typeof evlistener === 'function') {
      return 1;
    } else if (evlistener !== undefined) {
      return evlistener.length;
    }
  }

  return 0;
}

EventEmitter.prototype.eventNames = function eventNames() {
  return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
};

function arrayClone(arr, n) {
  var copy = new Array(n);
  for (var i = 0; i < n; ++i)
    copy[i] = arr[i];
  return copy;
}

function spliceOne(list, index) {
  for (; index + 1 < list.length; index++)
    list[index] = list[index + 1];
  list.pop();
}

function unwrapListeners(arr) {
  var ret = new Array(arr.length);
  for (var i = 0; i < ret.length; ++i) {
    ret[i] = arr[i].listener || arr[i];
  }
  return ret;
}

function once(emitter, name) {
  return new Promise(function (resolve, reject) {
    function eventListener() {
      if (errorListener !== undefined) {
        emitter.removeListener('error', errorListener);
      }
      resolve([].slice.call(arguments));
    };
    var errorListener;

    // Adding an error listener is not optional because
    // if an error is thrown on an event emitter we cannot
    // guarantee that the actual event we are waiting will
    // be fired. The result could be a silent way to create
    // memory or file descriptor leaks, which is something
    // we should avoid.
    if (name !== 'error') {
      errorListener = function errorListener(err) {
        emitter.removeListener(name, eventListener);
        reject(err);
      };

      emitter.once('error', errorListener);
    }

    emitter.once(name, eventListener);
  });
}

},{}],164:[function(require,module,exports){

var hasOwn = Object.prototype.hasOwnProperty;
var toString = Object.prototype.toString;

module.exports = function forEach (obj, fn, ctx) {
    if (toString.call(fn) !== '[object Function]') {
        throw new TypeError('iterator must be a function');
    }
    var l = obj.length;
    if (l === +l) {
        for (var i = 0; i < l; i++) {
            fn.call(ctx, obj[i], i, obj);
        }
    } else {
        for (var k in obj) {
            if (hasOwn.call(obj, k)) {
                fn.call(ctx, obj[k], k, obj);
            }
        }
    }
};


},{}],165:[function(require,module,exports){
'use strict';

/* eslint no-invalid-this: 1 */

var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible ';
var slice = Array.prototype.slice;
var toStr = Object.prototype.toString;
var funcType = '[object Function]';

module.exports = function bind(that) {
    var target = this;
    if (typeof target !== 'function' || toStr.call(target) !== funcType) {
        throw new TypeError(ERROR_MESSAGE + target);
    }
    var args = slice.call(arguments, 1);

    var bound;
    var binder = function () {
        if (this instanceof bound) {
            var result = target.apply(
                this,
                args.concat(slice.call(arguments))
            );
            if (Object(result) === result) {
                return result;
            }
            return this;
        } else {
            return target.apply(
                that,
                args.concat(slice.call(arguments))
            );
        }
    };

    var boundLength = Math.max(0, target.length - args.length);
    var boundArgs = [];
    for (var i = 0; i < boundLength; i++) {
        boundArgs.push('$' + i);
    }

    bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder);

    if (target.prototype) {
        var Empty = function Empty() {};
        Empty.prototype = target.prototype;
        bound.prototype = new Empty();
        Empty.prototype = null;
    }

    return bound;
};

},{}],166:[function(require,module,exports){
'use strict';

var implementation = require('./implementation');

module.exports = Function.prototype.bind || implementation;

},{"./implementation":165}],167:[function(require,module,exports){
'use strict';

var undefined;

var $SyntaxError = SyntaxError;
var $Function = Function;
var $TypeError = TypeError;

// eslint-disable-next-line consistent-return
var getEvalledConstructor = function (expressionSyntax) {
	try {
		return $Function('"use strict"; return (' + expressionSyntax + ').constructor;')();
	} catch (e) {}
};

var $gOPD = Object.getOwnPropertyDescriptor;
if ($gOPD) {
	try {
		$gOPD({}, '');
	} catch (e) {
		$gOPD = null; // this is IE 8, which has a broken gOPD
	}
}

var throwTypeError = function () {
	throw new $TypeError();
};
var ThrowTypeError = $gOPD
	? (function () {
		try {
			// eslint-disable-next-line no-unused-expressions, no-caller, no-restricted-properties
			arguments.callee; // IE 8 does not throw here
			return throwTypeError;
		} catch (calleeThrows) {
			try {
				// IE 8 throws on Object.getOwnPropertyDescriptor(arguments, '')
				return $gOPD(arguments, 'callee').get;
			} catch (gOPDthrows) {
				return throwTypeError;
			}
		}
	}())
	: throwTypeError;

var hasSymbols = require('has-symbols')();

var getProto = Object.getPrototypeOf || function (x) { return x.__proto__; }; // eslint-disable-line no-proto

var needsEval = {};

var TypedArray = typeof Uint8Array === 'undefined' ? undefined : getProto(Uint8Array);

var INTRINSICS = {
	'%AggregateError%': typeof AggregateError === 'undefined' ? undefined : AggregateError,
	'%Array%': Array,
	'%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer,
	'%ArrayIteratorPrototype%': hasSymbols ? getProto([][Symbol.iterator]()) : undefined,
	'%AsyncFromSyncIteratorPrototype%': undefined,
	'%AsyncFunction%': needsEval,
	'%AsyncGenerator%': needsEval,
	'%AsyncGeneratorFunction%': needsEval,
	'%AsyncIteratorPrototype%': needsEval,
	'%Atomics%': typeof Atomics === 'undefined' ? undefined : Atomics,
	'%BigInt%': typeof BigInt === 'undefined' ? undefined : BigInt,
	'%Boolean%': Boolean,
	'%DataView%': typeof DataView === 'undefined' ? undefined : DataView,
	'%Date%': Date,
	'%decodeURI%': decodeURI,
	'%decodeURIComponent%': decodeURIComponent,
	'%encodeURI%': encodeURI,
	'%encodeURIComponent%': encodeURIComponent,
	'%Error%': Error,
	'%eval%': eval, // eslint-disable-line no-eval
	'%EvalError%': EvalError,
	'%Float32Array%': typeof Float32Array === 'undefined' ? undefined : Float32Array,
	'%Float64Array%': typeof Float64Array === 'undefined' ? undefined : Float64Array,
	'%FinalizationRegistry%': typeof FinalizationRegistry === 'undefined' ? undefined : FinalizationRegistry,
	'%Function%': $Function,
	'%GeneratorFunction%': needsEval,
	'%Int8Array%': typeof Int8Array === 'undefined' ? undefined : Int8Array,
	'%Int16Array%': typeof Int16Array === 'undefined' ? undefined : Int16Array,
	'%Int32Array%': typeof Int32Array === 'undefined' ? undefined : Int32Array,
	'%isFinite%': isFinite,
	'%isNaN%': isNaN,
	'%IteratorPrototype%': hasSymbols ? getProto(getProto([][Symbol.iterator]())) : undefined,
	'%JSON%': typeof JSON === 'object' ? JSON : undefined,
	'%Map%': typeof Map === 'undefined' ? undefined : Map,
	'%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols ? undefined : getProto(new Map()[Symbol.iterator]()),
	'%Math%': Math,
	'%Number%': Number,
	'%Object%': Object,
	'%parseFloat%': parseFloat,
	'%parseInt%': parseInt,
	'%Promise%': typeof Promise === 'undefined' ? undefined : Promise,
	'%Proxy%': typeof Proxy === 'undefined' ? undefined : Proxy,
	'%RangeError%': RangeError,
	'%ReferenceError%': ReferenceError,
	'%Reflect%': typeof Reflect === 'undefined' ? undefined : Reflect,
	'%RegExp%': RegExp,
	'%Set%': typeof Set === 'undefined' ? undefined : Set,
	'%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols ? undefined : getProto(new Set()[Symbol.iterator]()),
	'%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer,
	'%String%': String,
	'%StringIteratorPrototype%': hasSymbols ? getProto(''[Symbol.iterator]()) : undefined,
	'%Symbol%': hasSymbols ? Symbol : undefined,
	'%SyntaxError%': $SyntaxError,
	'%ThrowTypeError%': ThrowTypeError,
	'%TypedArray%': TypedArray,
	'%TypeError%': $TypeError,
	'%Uint8Array%': typeof Uint8Array === 'undefined' ? undefined : Uint8Array,
	'%Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined : Uint8ClampedArray,
	'%Uint16Array%': typeof Uint16Array === 'undefined' ? undefined : Uint16Array,
	'%Uint32Array%': typeof Uint32Array === 'undefined' ? undefined : Uint32Array,
	'%URIError%': URIError,
	'%WeakMap%': typeof WeakMap === 'undefined' ? undefined : WeakMap,
	'%WeakRef%': typeof WeakRef === 'undefined' ? undefined : WeakRef,
	'%WeakSet%': typeof WeakSet === 'undefined' ? undefined : WeakSet
};

var doEval = function doEval(name) {
	var value;
	if (name === '%AsyncFunction%') {
		value = getEvalledConstructor('async function () {}');
	} else if (name === '%GeneratorFunction%') {
		value = getEvalledConstructor('function* () {}');
	} else if (name === '%AsyncGeneratorFunction%') {
		value = getEvalledConstructor('async function* () {}');
	} else if (name === '%AsyncGenerator%') {
		var fn = doEval('%AsyncGeneratorFunction%');
		if (fn) {
			value = fn.prototype;
		}
	} else if (name === '%AsyncIteratorPrototype%') {
		var gen = doEval('%AsyncGenerator%');
		if (gen) {
			value = getProto(gen.prototype);
		}
	}

	INTRINSICS[name] = value;

	return value;
};

var LEGACY_ALIASES = {
	'%ArrayBufferPrototype%': ['ArrayBuffer', 'prototype'],
	'%ArrayPrototype%': ['Array', 'prototype'],
	'%ArrayProto_entries%': ['Array', 'prototype', 'entries'],
	'%ArrayProto_forEach%': ['Array', 'prototype', 'forEach'],
	'%ArrayProto_keys%': ['Array', 'prototype', 'keys'],
	'%ArrayProto_values%': ['Array', 'prototype', 'values'],
	'%AsyncFunctionPrototype%': ['AsyncFunction', 'prototype'],
	'%AsyncGenerator%': ['AsyncGeneratorFunction', 'prototype'],
	'%AsyncGeneratorPrototype%': ['AsyncGeneratorFunction', 'prototype', 'prototype'],
	'%BooleanPrototype%': ['Boolean', 'prototype'],
	'%DataViewPrototype%': ['DataView', 'prototype'],
	'%DatePrototype%': ['Date', 'prototype'],
	'%ErrorPrototype%': ['Error', 'prototype'],
	'%EvalErrorPrototype%': ['EvalError', 'prototype'],
	'%Float32ArrayPrototype%': ['Float32Array', 'prototype'],
	'%Float64ArrayPrototype%': ['Float64Array', 'prototype'],
	'%FunctionPrototype%': ['Function', 'prototype'],
	'%Generator%': ['GeneratorFunction', 'prototype'],
	'%GeneratorPrototype%': ['GeneratorFunction', 'prototype', 'prototype'],
	'%Int8ArrayPrototype%': ['Int8Array', 'prototype'],
	'%Int16ArrayPrototype%': ['Int16Array', 'prototype'],
	'%Int32ArrayPrototype%': ['Int32Array', 'prototype'],
	'%JSONParse%': ['JSON', 'parse'],
	'%JSONStringify%': ['JSON', 'stringify'],
	'%MapPrototype%': ['Map', 'prototype'],
	'%NumberPrototype%': ['Number', 'prototype'],
	'%ObjectPrototype%': ['Object', 'prototype'],
	'%ObjProto_toString%': ['Object', 'prototype', 'toString'],
	'%ObjProto_valueOf%': ['Object', 'prototype', 'valueOf'],
	'%PromisePrototype%': ['Promise', 'prototype'],
	'%PromiseProto_then%': ['Promise', 'prototype', 'then'],
	'%Promise_all%': ['Promise', 'all'],
	'%Promise_reject%': ['Promise', 'reject'],
	'%Promise_resolve%': ['Promise', 'resolve'],
	'%RangeErrorPrototype%': ['RangeError', 'prototype'],
	'%ReferenceErrorPrototype%': ['ReferenceError', 'prototype'],
	'%RegExpPrototype%': ['RegExp', 'prototype'],
	'%SetPrototype%': ['Set', 'prototype'],
	'%SharedArrayBufferPrototype%': ['SharedArrayBuffer', 'prototype'],
	'%StringPrototype%': ['String', 'prototype'],
	'%SymbolPrototype%': ['Symbol', 'prototype'],
	'%SyntaxErrorPrototype%': ['SyntaxError', 'prototype'],
	'%TypedArrayPrototype%': ['TypedArray', 'prototype'],
	'%TypeErrorPrototype%': ['TypeError', 'prototype'],
	'%Uint8ArrayPrototype%': ['Uint8Array', 'prototype'],
	'%Uint8ClampedArrayPrototype%': ['Uint8ClampedArray', 'prototype'],
	'%Uint16ArrayPrototype%': ['Uint16Array', 'prototype'],
	'%Uint32ArrayPrototype%': ['Uint32Array', 'prototype'],
	'%URIErrorPrototype%': ['URIError', 'prototype'],
	'%WeakMapPrototype%': ['WeakMap', 'prototype'],
	'%WeakSetPrototype%': ['WeakSet', 'prototype']
};

var bind = require('function-bind');
var hasOwn = require('has');
var $concat = bind.call(Function.call, Array.prototype.concat);
var $spliceApply = bind.call(Function.apply, Array.prototype.splice);
var $replace = bind.call(Function.call, String.prototype.replace);
var $strSlice = bind.call(Function.call, String.prototype.slice);

/* adapted from https://github.com/lodash/lodash/blob/4.17.15/dist/lodash.js#L6735-L6744 */
var rePropName = /[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g;
var reEscapeChar = /\\(\\)?/g; /** Used to match backslashes in property paths. */
var stringToPath = function stringToPath(string) {
	var first = $strSlice(string, 0, 1);
	var last = $strSlice(string, -1);
	if (first === '%' && last !== '%') {
		throw new $SyntaxError('invalid intrinsic syntax, expected closing `%`');
	} else if (last === '%' && first !== '%') {
		throw new $SyntaxError('invalid intrinsic syntax, expected opening `%`');
	}
	var result = [];
	$replace(string, rePropName, function (match, number, quote, subString) {
		result[result.length] = quote ? $replace(subString, reEscapeChar, '$1') : number || match;
	});
	return result;
};
/* end adaptation */

var getBaseIntrinsic = function getBaseIntrinsic(name, allowMissing) {
	var intrinsicName = name;
	var alias;
	if (hasOwn(LEGACY_ALIASES, intrinsicName)) {
		alias = LEGACY_ALIASES[intrinsicName];
		intrinsicName = '%' + alias[0] + '%';
	}

	if (hasOwn(INTRINSICS, intrinsicName)) {
		var value = INTRINSICS[intrinsicName];
		if (value === needsEval) {
			value = doEval(intrinsicName);
		}
		if (typeof value === 'undefined' && !allowMissing) {
			throw new $TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!');
		}

		return {
			alias: alias,
			name: intrinsicName,
			value: value
		};
	}

	throw new $SyntaxError('intrinsic ' + name + ' does not exist!');
};

module.exports = function GetIntrinsic(name, allowMissing) {
	if (typeof name !== 'string' || name.length === 0) {
		throw new $TypeError('intrinsic name must be a non-empty string');
	}
	if (arguments.length > 1 && typeof allowMissing !== 'boolean') {
		throw new $TypeError('"allowMissing" argument must be a boolean');
	}

	var parts = stringToPath(name);
	var intrinsicBaseName = parts.length > 0 ? parts[0] : '';

	var intrinsic = getBaseIntrinsic('%' + intrinsicBaseName + '%', allowMissing);
	var intrinsicRealName = intrinsic.name;
	var value = intrinsic.value;
	var skipFurtherCaching = false;

	var alias = intrinsic.alias;
	if (alias) {
		intrinsicBaseName = alias[0];
		$spliceApply(parts, $concat([0, 1], alias));
	}

	for (var i = 1, isOwn = true; i < parts.length; i += 1) {
		var part = parts[i];
		var first = $strSlice(part, 0, 1);
		var last = $strSlice(part, -1);
		if (
			(
				(first === '"' || first === "'" || first === '`')
				|| (last === '"' || last === "'" || last === '`')
			)
			&& first !== last
		) {
			throw new $SyntaxError('property names with quotes must have matching quotes');
		}
		if (part === 'constructor' || !isOwn) {
			skipFurtherCaching = true;
		}

		intrinsicBaseName += '.' + part;
		intrinsicRealName = '%' + intrinsicBaseName + '%';

		if (hasOwn(INTRINSICS, intrinsicRealName)) {
			value = INTRINSICS[intrinsicRealName];
		} else if (value != null) {
			if (!(part in value)) {
				if (!allowMissing) {
					throw new $TypeError('base intrinsic for ' + name + ' exists, but the property is not available.');
				}
				return void undefined;
			}
			if ($gOPD && (i + 1) >= parts.length) {
				var desc = $gOPD(value, part);
				isOwn = !!desc;

				// By convention, when a data property is converted to an accessor
				// property to emulate a data property that does not suffer from
				// the override mistake, that accessor's getter is marked with
				// an `originalValue` property. Here, when we detect this, we
				// uphold the illusion by pretending to see that original data
				// property, i.e., returning the value rather than the getter
				// itself.
				if (isOwn && 'get' in desc && !('originalValue' in desc.get)) {
					value = desc.get;
				} else {
					value = value[part];
				}
			} else {
				isOwn = hasOwn(value, part);
				value = value[part];
			}

			if (isOwn && !skipFurtherCaching) {
				INTRINSICS[intrinsicRealName] = value;
			}
		}
	}
	return value;
};

},{"function-bind":166,"has":170,"has-symbols":168}],168:[function(require,module,exports){
(function (global){(function (){
'use strict';

var origSymbol = global.Symbol;
var hasSymbolSham = require('./shams');

module.exports = function hasNativeSymbols() {
	if (typeof origSymbol !== 'function') { return false; }
	if (typeof Symbol !== 'function') { return false; }
	if (typeof origSymbol('foo') !== 'symbol') { return false; }
	if (typeof Symbol('bar') !== 'symbol') { return false; }

	return hasSymbolSham();
};

}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./shams":169}],169:[function(require,module,exports){
'use strict';

/* eslint complexity: [2, 18], max-statements: [2, 33] */
module.exports = function hasSymbols() {
	if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; }
	if (typeof Symbol.iterator === 'symbol') { return true; }

	var obj = {};
	var sym = Symbol('test');
	var symObj = Object(sym);
	if (typeof sym === 'string') { return false; }

	if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; }
	if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; }

	// temp disabled per https://github.com/ljharb/object.assign/issues/17
	// if (sym instanceof Symbol) { return false; }
	// temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4
	// if (!(symObj instanceof Symbol)) { return false; }

	// if (typeof Symbol.prototype.toString !== 'function') { return false; }
	// if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; }

	var symVal = 42;
	obj[sym] = symVal;
	for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax
	if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; }

	if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; }

	var syms = Object.getOwnPropertySymbols(obj);
	if (syms.length !== 1 || syms[0] !== sym) { return false; }

	if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; }

	if (typeof Object.getOwnPropertyDescriptor === 'function') {
		var descriptor = Object.getOwnPropertyDescriptor(obj, sym);
		if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; }
	}

	return true;
};

},{}],170:[function(require,module,exports){
'use strict';

var bind = require('function-bind');

module.exports = bind.call(Function.call, Object.prototype.hasOwnProperty);

},{"function-bind":166}],171:[function(require,module,exports){
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
exports.read = function (buffer, offset, isLE, mLen, nBytes) {
  var e, m
  var eLen = (nBytes * 8) - mLen - 1
  var eMax = (1 << eLen) - 1
  var eBias = eMax >> 1
  var nBits = -7
  var i = isLE ? (nBytes - 1) : 0
  var d = isLE ? -1 : 1
  var s = buffer[offset + i]

  i += d

  e = s & ((1 << (-nBits)) - 1)
  s >>= (-nBits)
  nBits += eLen
  for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}

  m = e & ((1 << (-nBits)) - 1)
  e >>= (-nBits)
  nBits += mLen
  for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}

  if (e === 0) {
    e = 1 - eBias
  } else if (e === eMax) {
    return m ? NaN : ((s ? -1 : 1) * Infinity)
  } else {
    m = m + Math.pow(2, mLen)
    e = e - eBias
  }
  return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
}

exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
  var e, m, c
  var eLen = (nBytes * 8) - mLen - 1
  var eMax = (1 << eLen) - 1
  var eBias = eMax >> 1
  var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
  var i = isLE ? 0 : (nBytes - 1)
  var d = isLE ? 1 : -1
  var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0

  value = Math.abs(value)

  if (isNaN(value) || value === Infinity) {
    m = isNaN(value) ? 1 : 0
    e = eMax
  } else {
    e = Math.floor(Math.log(value) / Math.LN2)
    if (value * (c = Math.pow(2, -e)) < 1) {
      e--
      c *= 2
    }
    if (e + eBias >= 1) {
      value += rt / c
    } else {
      value += rt * Math.pow(2, 1 - eBias)
    }
    if (value * c >= 2) {
      e++
      c /= 2
    }

    if (e + eBias >= eMax) {
      m = 0
      e = eMax
    } else if (e + eBias >= 1) {
      m = ((value * c) - 1) * Math.pow(2, mLen)
      e = e + eBias
    } else {
      m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
      e = 0
    }
  }

  for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}

  e = (e << mLen) | m
  eLen += mLen
  for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}

  buffer[offset + i - d] |= s * 128
}

},{}],172:[function(require,module,exports){
if (typeof Object.create === 'function') {
  // implementation from standard node.js 'util' module
  module.exports = function inherits(ctor, superCtor) {
    if (superCtor) {
      ctor.super_ = superCtor
      ctor.prototype = Object.create(superCtor.prototype, {
        constructor: {
          value: ctor,
          enumerable: false,
          writable: true,
          configurable: true
        }
      })
    }
  };
} else {
  // old school shim for old browsers
  module.exports = function inherits(ctor, superCtor) {
    if (superCtor) {
      ctor.super_ = superCtor
      var TempCtor = function () {}
      TempCtor.prototype = superCtor.prototype
      ctor.prototype = new TempCtor()
      ctor.prototype.constructor = ctor
    }
  }
}

},{}],173:[function(require,module,exports){
'use strict';

var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
var callBound = require('call-bind/callBound');

var $toString = callBound('Object.prototype.toString');

var isStandardArguments = function isArguments(value) {
	if (hasToStringTag && value && typeof value === 'object' && Symbol.toStringTag in value) {
		return false;
	}
	return $toString(value) === '[object Arguments]';
};

var isLegacyArguments = function isArguments(value) {
	if (isStandardArguments(value)) {
		return true;
	}
	return value !== null &&
		typeof value === 'object' &&
		typeof value.length === 'number' &&
		value.length >= 0 &&
		$toString(value) !== '[object Array]' &&
		$toString(value.callee) === '[object Function]';
};

var supportsStandardArguments = (function () {
	return isStandardArguments(arguments);
}());

isStandardArguments.isLegacyArguments = isLegacyArguments; // for tests

module.exports = supportsStandardArguments ? isStandardArguments : isLegacyArguments;

},{"call-bind/callBound":160}],174:[function(require,module,exports){
arguments[4][70][0].apply(exports,arguments)
},{"dup":70}],175:[function(require,module,exports){
'use strict';

var toStr = Object.prototype.toString;
var fnToStr = Function.prototype.toString;
var isFnRegex = /^\s*(?:function)?\*/;
var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
var getProto = Object.getPrototypeOf;
var getGeneratorFunc = function () { // eslint-disable-line consistent-return
	if (!hasToStringTag) {
		return false;
	}
	try {
		return Function('return function*() {}')();
	} catch (e) {
	}
};
var generatorFunc = getGeneratorFunc();
var GeneratorFunction = getProto && generatorFunc ? getProto(generatorFunc) : false;

module.exports = function isGeneratorFunction(fn) {
	if (typeof fn !== 'function') {
		return false;
	}
	if (isFnRegex.test(fnToStr.call(fn))) {
		return true;
	}
	if (!hasToStringTag) {
		var str = toStr.call(fn);
		return str === '[object GeneratorFunction]';
	}
	return getProto && getProto(fn) === GeneratorFunction;
};

},{}],176:[function(require,module,exports){
(function (global){(function (){
'use strict';

var forEach = require('foreach');
var availableTypedArrays = require('available-typed-arrays');
var callBound = require('call-bind/callBound');

var $toString = callBound('Object.prototype.toString');
var hasSymbols = require('has-symbols')();
var hasToStringTag = hasSymbols && typeof Symbol.toStringTag === 'symbol';

var typedArrays = availableTypedArrays();

var $indexOf = callBound('Array.prototype.indexOf', true) || function indexOf(array, value) {
	for (var i = 0; i < array.length; i += 1) {
		if (array[i] === value) {
			return i;
		}
	}
	return -1;
};
var $slice = callBound('String.prototype.slice');
var toStrTags = {};
var gOPD = require('es-abstract/helpers/getOwnPropertyDescriptor');
var getPrototypeOf = Object.getPrototypeOf; // require('getprototypeof');
if (hasToStringTag && gOPD && getPrototypeOf) {
	forEach(typedArrays, function (typedArray) {
		var arr = new global[typedArray]();
		if (!(Symbol.toStringTag in arr)) {
			throw new EvalError('this engine has support for Symbol.toStringTag, but ' + typedArray + ' does not have the property! Please report this.');
		}
		var proto = getPrototypeOf(arr);
		var descriptor = gOPD(proto, Symbol.toStringTag);
		if (!descriptor) {
			var superProto = getPrototypeOf(proto);
			descriptor = gOPD(superProto, Symbol.toStringTag);
		}
		toStrTags[typedArray] = descriptor.get;
	});
}

var tryTypedArrays = function tryAllTypedArrays(value) {
	var anyTrue = false;
	forEach(toStrTags, function (getter, typedArray) {
		if (!anyTrue) {
			try {
				anyTrue = getter.call(value) === typedArray;
			} catch (e) { /**/ }
		}
	});
	return anyTrue;
};

module.exports = function isTypedArray(value) {
	if (!value || typeof value !== 'object') { return false; }
	if (!hasToStringTag) {
		var tag = $slice($toString(value), 8, -1);
		return $indexOf(typedArrays, tag) > -1;
	}
	if (!gOPD) { return false; }
	return tryTypedArrays(value);
};

}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"available-typed-arrays":154,"call-bind/callBound":160,"es-abstract/helpers/getOwnPropertyDescriptor":162,"foreach":164,"has-symbols":168}],177:[function(require,module,exports){
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/

'use strict';
/* eslint-disable no-unused-vars */
var getOwnPropertySymbols = Object.getOwnPropertySymbols;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var propIsEnumerable = Object.prototype.propertyIsEnumerable;

function toObject(val) {
	if (val === null || val === undefined) {
		throw new TypeError('Object.assign cannot be called with null or undefined');
	}

	return Object(val);
}

function shouldUseNative() {
	try {
		if (!Object.assign) {
			return false;
		}

		// Detect buggy property enumeration order in older V8 versions.

		// https://bugs.chromium.org/p/v8/issues/detail?id=4118
		var test1 = new String('abc');  // eslint-disable-line no-new-wrappers
		test1[5] = 'de';
		if (Object.getOwnPropertyNames(test1)[0] === '5') {
			return false;
		}

		// https://bugs.chromium.org/p/v8/issues/detail?id=3056
		var test2 = {};
		for (var i = 0; i < 10; i++) {
			test2['_' + String.fromCharCode(i)] = i;
		}
		var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
			return test2[n];
		});
		if (order2.join('') !== '0123456789') {
			return false;
		}

		// https://bugs.chromium.org/p/v8/issues/detail?id=3056
		var test3 = {};
		'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
			test3[letter] = letter;
		});
		if (Object.keys(Object.assign({}, test3)).join('') !==
				'abcdefghijklmnopqrst') {
			return false;
		}

		return true;
	} catch (err) {
		// We don't expect any of the above to throw, but better to be safe.
		return false;
	}
}

module.exports = shouldUseNative() ? Object.assign : function (target, source) {
	var from;
	var to = toObject(target);
	var symbols;

	for (var s = 1; s < arguments.length; s++) {
		from = Object(arguments[s]);

		for (var key in from) {
			if (hasOwnProperty.call(from, key)) {
				to[key] = from[key];
			}
		}

		if (getOwnPropertySymbols) {
			symbols = getOwnPropertySymbols(from);
			for (var i = 0; i < symbols.length; i++) {
				if (propIsEnumerable.call(from, symbols[i])) {
					to[symbols[i]] = from[symbols[i]];
				}
			}
		}
	}

	return to;
};

},{}],178:[function(require,module,exports){
exports.endianness = function () { return 'LE' };

exports.hostname = function () {
    if (typeof location !== 'undefined') {
        return location.hostname
    }
    else return '';
};

exports.loadavg = function () { return [] };

exports.uptime = function () { return 0 };

exports.freemem = function () {
    return Number.MAX_VALUE;
};

exports.totalmem = function () {
    return Number.MAX_VALUE;
};

exports.cpus = function () { return [] };

exports.type = function () { return 'Browser' };

exports.release = function () {
    if (typeof navigator !== 'undefined') {
        return navigator.appVersion;
    }
    return '';
};

exports.networkInterfaces
= exports.getNetworkInterfaces
= function () { return {} };

exports.arch = function () { return 'javascript' };

exports.platform = function () { return 'browser' };

exports.tmpdir = exports.tmpDir = function () {
    return '/tmp';
};

exports.EOL = '\n';

exports.homedir = function () {
	return '/'
};

},{}],179:[function(require,module,exports){
'use strict';


var TYPED_OK =  (typeof Uint8Array !== 'undefined') &&
                (typeof Uint16Array !== 'undefined') &&
                (typeof Int32Array !== 'undefined');

function _has(obj, key) {
  return Object.prototype.hasOwnProperty.call(obj, key);
}

exports.assign = function (obj /*from1, from2, from3, ...*/) {
  var sources = Array.prototype.slice.call(arguments, 1);
  while (sources.length) {
    var source = sources.shift();
    if (!source) { continue; }

    if (typeof source !== 'object') {
      throw new TypeError(source + 'must be non-object');
    }

    for (var p in source) {
      if (_has(source, p)) {
        obj[p] = source[p];
      }
    }
  }

  return obj;
};


// reduce buffer size, avoiding mem copy
exports.shrinkBuf = function (buf, size) {
  if (buf.length === size) { return buf; }
  if (buf.subarray) { return buf.subarray(0, size); }
  buf.length = size;
  return buf;
};


var fnTyped = {
  arraySet: function (dest, src, src_offs, len, dest_offs) {
    if (src.subarray && dest.subarray) {
      dest.set(src.subarray(src_offs, src_offs + len), dest_offs);
      return;
    }
    // Fallback to ordinary array
    for (var i = 0; i < len; i++) {
      dest[dest_offs + i] = src[src_offs + i];
    }
  },
  // Join array of chunks to single array.
  flattenChunks: function (chunks) {
    var i, l, len, pos, chunk, result;

    // calculate data length
    len = 0;
    for (i = 0, l = chunks.length; i < l; i++) {
      len += chunks[i].length;
    }

    // join chunks
    result = new Uint8Array(len);
    pos = 0;
    for (i = 0, l = chunks.length; i < l; i++) {
      chunk = chunks[i];
      result.set(chunk, pos);
      pos += chunk.length;
    }

    return result;
  }
};

var fnUntyped = {
  arraySet: function (dest, src, src_offs, len, dest_offs) {
    for (var i = 0; i < len; i++) {
      dest[dest_offs + i] = src[src_offs + i];
    }
  },
  // Join array of chunks to single array.
  flattenChunks: function (chunks) {
    return [].concat.apply([], chunks);
  }
};


// Enable/Disable typed arrays use, for testing
//
exports.setTyped = function (on) {
  if (on) {
    exports.Buf8  = Uint8Array;
    exports.Buf16 = Uint16Array;
    exports.Buf32 = Int32Array;
    exports.assign(exports, fnTyped);
  } else {
    exports.Buf8  = Array;
    exports.Buf16 = Array;
    exports.Buf32 = Array;
    exports.assign(exports, fnUntyped);
  }
};

exports.setTyped(TYPED_OK);

},{}],180:[function(require,module,exports){
'use strict';

// Note: adler32 takes 12% for level 0 and 2% for level 6.
// It isn't worth it to make additional optimizations as in original.
// Small size is preferable.

// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

function adler32(adler, buf, len, pos) {
  var s1 = (adler & 0xffff) |0,
      s2 = ((adler >>> 16) & 0xffff) |0,
      n = 0;

  while (len !== 0) {
    // Set limit ~ twice less than 5552, to keep
    // s2 in 31-bits, because we force signed ints.
    // in other case %= will fail.
    n = len > 2000 ? 2000 : len;
    len -= n;

    do {
      s1 = (s1 + buf[pos++]) |0;
      s2 = (s2 + s1) |0;
    } while (--n);

    s1 %= 65521;
    s2 %= 65521;
  }

  return (s1 | (s2 << 16)) |0;
}


module.exports = adler32;

},{}],181:[function(require,module,exports){
'use strict';

// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

module.exports = {

  /* Allowed flush values; see deflate() and inflate() below for details */
  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,

  /* Return codes for the compression/decompression functions. Negative values
  * are errors, positive values are used for special but normal events.
  */
  Z_OK:               0,
  Z_STREAM_END:       1,
  Z_NEED_DICT:        2,
  Z_ERRNO:           -1,
  Z_STREAM_ERROR:    -2,
  Z_DATA_ERROR:      -3,
  //Z_MEM_ERROR:     -4,
  Z_BUF_ERROR:       -5,
  //Z_VERSION_ERROR: -6,

  /* compression levels */
  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,

  /* Possible values of the data_type field (though see inflate()) */
  Z_BINARY:                 0,
  Z_TEXT:                   1,
  //Z_ASCII:                1, // = Z_TEXT (deprecated)
  Z_UNKNOWN:                2,

  /* The deflate compression method */
  Z_DEFLATED:               8
  //Z_NULL:                 null // Use -1 or null inline, depending on var type
};

},{}],182:[function(require,module,exports){
'use strict';

// Note: we can't get significant speed boost here.
// So write code to minimize size - no pregenerated tables
// and array tools dependencies.

// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

// Use ordinary array, since untyped makes no boost here
function makeTable() {
  var c, table = [];

  for (var n = 0; n < 256; n++) {
    c = n;
    for (var k = 0; k < 8; k++) {
      c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
    }
    table[n] = c;
  }

  return table;
}

// Create table on load. Just 255 signed longs. Not a problem.
var crcTable = makeTable();


function crc32(crc, buf, len, pos) {
  var t = crcTable,
      end = pos + len;

  crc ^= -1;

  for (var i = pos; i < end; i++) {
    crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
  }

  return (crc ^ (-1)); // >>> 0;
}


module.exports = crc32;

},{}],183:[function(require,module,exports){
'use strict';

// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

var utils   = require('../utils/common');
var trees   = require('./trees');
var adler32 = require('./adler32');
var crc32   = require('./crc32');
var msg     = require('./messages');

/* Public constants ==========================================================*/
/* ===========================================================================*/


/* Allowed flush values; see deflate() and inflate() below for details */
var Z_NO_FLUSH      = 0;
var Z_PARTIAL_FLUSH = 1;
//var Z_SYNC_FLUSH    = 2;
var Z_FULL_FLUSH    = 3;
var Z_FINISH        = 4;
var Z_BLOCK         = 5;
//var Z_TREES         = 6;


/* Return codes for the compression/decompression functions. Negative values
 * are errors, positive values are used for special but normal events.
 */
var Z_OK            = 0;
var Z_STREAM_END    = 1;
//var Z_NEED_DICT     = 2;
//var Z_ERRNO         = -1;
var Z_STREAM_ERROR  = -2;
var Z_DATA_ERROR    = -3;
//var Z_MEM_ERROR     = -4;
var Z_BUF_ERROR     = -5;
//var Z_VERSION_ERROR = -6;


/* compression levels */
//var Z_NO_COMPRESSION      = 0;
//var Z_BEST_SPEED          = 1;
//var Z_BEST_COMPRESSION    = 9;
var Z_DEFAULT_COMPRESSION = -1;


var Z_FILTERED            = 1;
var Z_HUFFMAN_ONLY        = 2;
var Z_RLE                 = 3;
var Z_FIXED               = 4;
var Z_DEFAULT_STRATEGY    = 0;

/* Possible values of the data_type field (though see inflate()) */
//var Z_BINARY              = 0;
//var Z_TEXT                = 1;
//var Z_ASCII               = 1; // = Z_TEXT
var Z_UNKNOWN             = 2;


/* The deflate compression method */
var Z_DEFLATED  = 8;

/*============================================================================*/


var MAX_MEM_LEVEL = 9;
/* Maximum value for memLevel in deflateInit2 */
var MAX_WBITS = 15;
/* 32K LZ77 window */
var DEF_MEM_LEVEL = 8;


var LENGTH_CODES  = 29;
/* number of length codes, not counting the special END_BLOCK code */
var LITERALS      = 256;
/* number of literal bytes 0..255 */
var L_CODES       = LITERALS + 1 + LENGTH_CODES;
/* number of Literal or Length codes, including the END_BLOCK code */
var D_CODES       = 30;
/* number of distance codes */
var BL_CODES      = 19;
/* number of codes used to transfer the bit lengths */
var HEAP_SIZE     = 2 * L_CODES + 1;
/* maximum heap size */
var MAX_BITS  = 15;
/* All codes must not exceed MAX_BITS bits */

var MIN_MATCH = 3;
var MAX_MATCH = 258;
var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1);

var PRESET_DICT = 0x20;

var INIT_STATE = 42;
var EXTRA_STATE = 69;
var NAME_STATE = 73;
var COMMENT_STATE = 91;
var HCRC_STATE = 103;
var BUSY_STATE = 113;
var FINISH_STATE = 666;

var BS_NEED_MORE      = 1; /* block not completed, need more input or more output */
var BS_BLOCK_DONE     = 2; /* block flush performed */
var BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */
var BS_FINISH_DONE    = 4; /* finish done, accept no more input or output */

var OS_CODE = 0x03; // Unix :) . Don't detect, use this default.

function err(strm, errorCode) {
  strm.msg = msg[errorCode];
  return errorCode;
}

function rank(f) {
  return ((f) << 1) - ((f) > 4 ? 9 : 0);
}

function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }


/* =========================================================================
 * Flush as much pending output as possible. All deflate() output goes
 * through this function so some applications may wish to modify it
 * to avoid allocating a large strm->output buffer and copying into it.
 * (See also read_buf()).
 */
function flush_pending(strm) {
  var s = strm.state;

  //_tr_flush_bits(s);
  var len = s.pending;
  if (len > strm.avail_out) {
    len = strm.avail_out;
  }
  if (len === 0) { return; }

  utils.arraySet(strm.output, s.pending_buf, s.pending_out, len, strm.next_out);
  strm.next_out += len;
  s.pending_out += len;
  strm.total_out += len;
  strm.avail_out -= len;
  s.pending -= len;
  if (s.pending === 0) {
    s.pending_out = 0;
  }
}


function flush_block_only(s, last) {
  trees._tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last);
  s.block_start = s.strstart;
  flush_pending(s.strm);
}


function put_byte(s, b) {
  s.pending_buf[s.pending++] = b;
}


/* =========================================================================
 * Put a short in the pending buffer. The 16-bit value is put in MSB order.
 * IN assertion: the stream state is correct and there is enough room in
 * pending_buf.
 */
function putShortMSB(s, b) {
//  put_byte(s, (Byte)(b >> 8));
//  put_byte(s, (Byte)(b & 0xff));
  s.pending_buf[s.pending++] = (b >>> 8) & 0xff;
  s.pending_buf[s.pending++] = b & 0xff;
}


/* ===========================================================================
 * Read a new buffer from the current input stream, update the adler32
 * and total number of bytes read.  All deflate() input goes through
 * this function so some applications may wish to modify it to avoid
 * allocating a large strm->input buffer and copying from it.
 * (See also flush_pending()).
 */
function read_buf(strm, buf, start, size) {
  var len = strm.avail_in;

  if (len > size) { len = size; }
  if (len === 0) { return 0; }

  strm.avail_in -= len;

  // zmemcpy(buf, strm->next_in, len);
  utils.arraySet(buf, strm.input, strm.next_in, len, start);
  if (strm.state.wrap === 1) {
    strm.adler = adler32(strm.adler, buf, len, start);
  }

  else if (strm.state.wrap === 2) {
    strm.adler = crc32(strm.adler, buf, len, start);
  }

  strm.next_in += len;
  strm.total_in += len;

  return len;
}


/* ===========================================================================
 * Set match_start to the longest match starting at the given string and
 * return its length. Matches shorter or equal to prev_length are discarded,
 * in which case the result is equal to prev_length and match_start is
 * garbage.
 * IN assertions: cur_match is the head of the hash chain for the current
 *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
 * OUT assertion: the match length is not greater than s->lookahead.
 */
function longest_match(s, cur_match) {
  var chain_length = s.max_chain_length;      /* max hash chain length */
  var scan = s.strstart; /* current string */
  var match;                       /* matched string */
  var len;                           /* length of current match */
  var best_len = s.prev_length;              /* best match length so far */
  var nice_match = s.nice_match;             /* stop if match long enough */
  var limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ?
      s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/;

  var _win = s.window; // shortcut

  var wmask = s.w_mask;
  var prev  = s.prev;

  /* Stop when cur_match becomes <= limit. To simplify the code,
   * we prevent matches with the string of window index 0.
   */

  var strend = s.strstart + MAX_MATCH;
  var scan_end1  = _win[scan + best_len - 1];
  var scan_end   = _win[scan + best_len];

  /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
   * It is easy to get rid of this optimization if necessary.
   */
  // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");

  /* Do not waste too much time if we already have a good match: */
  if (s.prev_length >= s.good_match) {
    chain_length >>= 2;
  }
  /* Do not look for matches beyond the end of the input. This is necessary
   * to make deflate deterministic.
   */
  if (nice_match > s.lookahead) { nice_match = s.lookahead; }

  // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");

  do {
    // Assert(cur_match < s->strstart, "no future");
    match = cur_match;

    /* Skip to next match if the match length cannot increase
     * or if the match length is less than 2.  Note that the checks below
     * for insufficient lookahead only occur occasionally for performance
     * reasons.  Therefore uninitialized memory will be accessed, and
     * conditional jumps will be made that depend on those values.
     * However the length of the match is limited to the lookahead, so
     * the output of deflate is not affected by the uninitialized values.
     */

    if (_win[match + best_len]     !== scan_end  ||
        _win[match + best_len - 1] !== scan_end1 ||
        _win[match]                !== _win[scan] ||
        _win[++match]              !== _win[scan + 1]) {
      continue;
    }

    /* The check at best_len-1 can be removed because it will be made
     * again later. (This heuristic is not always a win.)
     * It is not necessary to compare scan[2] and match[2] since they
     * are always equal when the other bytes match, given that
     * the hash keys are equal and that HASH_BITS >= 8.
     */
    scan += 2;
    match++;
    // Assert(*scan == *match, "match[2]?");

    /* We check for insufficient lookahead only every 8th comparison;
     * the 256th check will be made at strstart+258.
     */
    do {
      /*jshint noempty:false*/
    } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
             scan < strend);

    // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");

    len = MAX_MATCH - (strend - scan);
    scan = strend - MAX_MATCH;

    if (len > best_len) {
      s.match_start = cur_match;
      best_len = len;
      if (len >= nice_match) {
        break;
      }
      scan_end1  = _win[scan + best_len - 1];
      scan_end   = _win[scan + best_len];
    }
  } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);

  if (best_len <= s.lookahead) {
    return best_len;
  }
  return s.lookahead;
}


/* ===========================================================================
 * Fill the window when the lookahead becomes insufficient.
 * Updates strstart and lookahead.
 *
 * IN assertion: lookahead < MIN_LOOKAHEAD
 * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
 *    At least one byte has been read, or avail_in == 0; reads are
 *    performed for at least two bytes (required for the zip translate_eol
 *    option -- not supported here).
 */
function fill_window(s) {
  var _w_size = s.w_size;
  var p, n, m, more, str;

  //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");

  do {
    more = s.window_size - s.lookahead - s.strstart;

    // JS ints have 32 bit, block below not needed
    /* Deal with !@#$% 64K limit: */
    //if (sizeof(int) <= 2) {
    //    if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
    //        more = wsize;
    //
    //  } else if (more == (unsigned)(-1)) {
    //        /* Very unlikely, but possible on 16 bit machine if
    //         * strstart == 0 && lookahead == 1 (input done a byte at time)
    //         */
    //        more--;
    //    }
    //}


    /* If the window is almost full and there is insufficient lookahead,
     * move the upper half to the lower one to make room in the upper half.
     */
    if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) {

      utils.arraySet(s.window, s.window, _w_size, _w_size, 0);
      s.match_start -= _w_size;
      s.strstart -= _w_size;
      /* we now have strstart >= MAX_DIST */
      s.block_start -= _w_size;

      /* Slide the hash table (could be avoided with 32 bit values
       at the expense of memory usage). We slide even when level == 0
       to keep the hash table consistent if we switch back to level > 0
       later. (Using level 0 permanently is not an optimal usage of
       zlib, so we don't care about this pathological case.)
       */

      n = s.hash_size;
      p = n;
      do {
        m = s.head[--p];
        s.head[p] = (m >= _w_size ? m - _w_size : 0);
      } while (--n);

      n = _w_size;
      p = n;
      do {
        m = s.prev[--p];
        s.prev[p] = (m >= _w_size ? m - _w_size : 0);
        /* If n is not on any hash chain, prev[n] is garbage but
         * its value will never be used.
         */
      } while (--n);

      more += _w_size;
    }
    if (s.strm.avail_in === 0) {
      break;
    }

    /* If there was no sliding:
     *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
     *    more == window_size - lookahead - strstart
     * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
     * => more >= window_size - 2*WSIZE + 2
     * In the BIG_MEM or MMAP case (not yet supported),
     *   window_size == input_size + MIN_LOOKAHEAD  &&
     *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
     * Otherwise, window_size == 2*WSIZE so more >= 2.
     * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
     */
    //Assert(more >= 2, "more < 2");
    n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more);
    s.lookahead += n;

    /* Initialize the hash value now that we have some input: */
    if (s.lookahead + s.insert >= MIN_MATCH) {
      str = s.strstart - s.insert;
      s.ins_h = s.window[str];

      /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */
      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask;
//#if MIN_MATCH != 3
//        Call update_hash() MIN_MATCH-3 more times
//#endif
      while (s.insert) {
        /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
        s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask;

        s.prev[str & s.w_mask] = s.head[s.ins_h];
        s.head[s.ins_h] = str;
        str++;
        s.insert--;
        if (s.lookahead + s.insert < MIN_MATCH) {
          break;
        }
      }
    }
    /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
     * but this is not important since only literal bytes will be emitted.
     */

  } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0);

  /* If the WIN_INIT bytes after the end of the current data have never been
   * written, then zero those bytes in order to avoid memory check reports of
   * the use of uninitialized (or uninitialised as Julian writes) bytes by
   * the longest match routines.  Update the high water mark for the next
   * time through here.  WIN_INIT is set to MAX_MATCH since the longest match
   * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
   */
//  if (s.high_water < s.window_size) {
//    var curr = s.strstart + s.lookahead;
//    var init = 0;
//
//    if (s.high_water < curr) {
//      /* Previous high water mark below current data -- zero WIN_INIT
//       * bytes or up to end of window, whichever is less.
//       */
//      init = s.window_size - curr;
//      if (init > WIN_INIT)
//        init = WIN_INIT;
//      zmemzero(s->window + curr, (unsigned)init);
//      s->high_water = curr + init;
//    }
//    else if (s->high_water < (ulg)curr + WIN_INIT) {
//      /* High water mark at or above current data, but below current data
//       * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
//       * to end of window, whichever is less.
//       */
//      init = (ulg)curr + WIN_INIT - s->high_water;
//      if (init > s->window_size - s->high_water)
//        init = s->window_size - s->high_water;
//      zmemzero(s->window + s->high_water, (unsigned)init);
//      s->high_water += init;
//    }
//  }
//
//  Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
//    "not enough room for search");
}

/* ===========================================================================
 * Copy without compression as much as possible from the input stream, return
 * the current block state.
 * This function does not insert new strings in the dictionary since
 * uncompressible data is probably not useful. This function is used
 * only for the level=0 compression option.
 * NOTE: this function should be optimized to avoid extra copying from
 * window to pending_buf.
 */
function deflate_stored(s, flush) {
  /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
   * to pending_buf_size, and each stored block has a 5 byte header:
   */
  var max_block_size = 0xffff;

  if (max_block_size > s.pending_buf_size - 5) {
    max_block_size = s.pending_buf_size - 5;
  }

  /* Copy as much as possible from input to output: */
  for (;;) {
    /* Fill the window as much as possible: */
    if (s.lookahead <= 1) {

      //Assert(s->strstart < s->w_size+MAX_DIST(s) ||
      //  s->block_start >= (long)s->w_size, "slide too late");
//      if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) ||
//        s.block_start >= s.w_size)) {
//        throw  new Error("slide too late");
//      }

      fill_window(s);
      if (s.lookahead === 0 && flush === Z_NO_FLUSH) {
        return BS_NEED_MORE;
      }

      if (s.lookahead === 0) {
        break;
      }
      /* flush the current block */
    }
    //Assert(s->block_start >= 0L, "block gone");
//    if (s.block_start < 0) throw new Error("block gone");

    s.strstart += s.lookahead;
    s.lookahead = 0;

    /* Emit a stored block if pending_buf will be full: */
    var max_start = s.block_start + max_block_size;

    if (s.strstart === 0 || s.strstart >= max_start) {
      /* strstart == 0 is possible when wraparound on 16-bit machine */
      s.lookahead = s.strstart - max_start;
      s.strstart = max_start;
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/


    }
    /* Flush if we may have to slide, otherwise block_start may become
     * negative and the data will be gone:
     */
    if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) {
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/
    }
  }

  s.insert = 0;

  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }

  if (s.strstart > s.block_start) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }

  return BS_NEED_MORE;
}

/* ===========================================================================
 * Compress as much as possible from the input stream, return the current
 * block state.
 * This function does not perform lazy evaluation of matches and inserts
 * new strings in the dictionary only for unmatched strings or for short
 * matches. It is used only for the fast compression options.
 */
function deflate_fast(s, flush) {
  var hash_head;        /* head of the hash chain */
  var bflush;           /* set if current block must be flushed */

  for (;;) {
    /* Make sure that we always have enough lookahead, except
     * at the end of the input file. We need MAX_MATCH bytes
     * for the next match, plus MIN_MATCH bytes to insert the
     * string following the next match.
     */
    if (s.lookahead < MIN_LOOKAHEAD) {
      fill_window(s);
      if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {
        return BS_NEED_MORE;
      }
      if (s.lookahead === 0) {
        break; /* flush the current block */
      }
    }

    /* Insert the string window[strstart .. strstart+2] in the
     * dictionary, and set hash_head to the head of the hash chain:
     */
    hash_head = 0/*NIL*/;
    if (s.lookahead >= MIN_MATCH) {
      /*** INSERT_STRING(s, s.strstart, hash_head); ***/
      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
      hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
      s.head[s.ins_h] = s.strstart;
      /***/
    }

    /* Find the longest match, discarding those <= prev_length.
     * At this point we have always match_length < MIN_MATCH
     */
    if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) {
      /* To simplify the code, we prevent matches with the string
       * of window index 0 (in particular we have to avoid a match
       * of the string with itself at the start of the input file).
       */
      s.match_length = longest_match(s, hash_head);
      /* longest_match() sets match_start */
    }
    if (s.match_length >= MIN_MATCH) {
      // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only

      /*** _tr_tally_dist(s, s.strstart - s.match_start,
                     s.match_length - MIN_MATCH, bflush); ***/
      bflush = trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH);

      s.lookahead -= s.match_length;

      /* Insert new strings in the hash table only if the match length
       * is not too large. This saves time but degrades compression.
       */
      if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) {
        s.match_length--; /* string at strstart already in table */
        do {
          s.strstart++;
          /*** INSERT_STRING(s, s.strstart, hash_head); ***/
          s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = s.strstart;
          /***/
          /* strstart never exceeds WSIZE-MAX_MATCH, so there are
           * always MIN_MATCH bytes ahead.
           */
        } while (--s.match_length !== 0);
        s.strstart++;
      } else
      {
        s.strstart += s.match_length;
        s.match_length = 0;
        s.ins_h = s.window[s.strstart];
        /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */
        s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask;

//#if MIN_MATCH != 3
//                Call UPDATE_HASH() MIN_MATCH-3 more times
//#endif
        /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
         * matter since it will be recomputed at next deflate call.
         */
      }
    } else {
      /* No match, output a literal byte */
      //Tracevv((stderr,"%c", s.window[s.strstart]));
      /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
      bflush = trees._tr_tally(s, 0, s.window[s.strstart]);

      s.lookahead--;
      s.strstart++;
    }
    if (bflush) {
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/
    }
  }
  s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1);
  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }
  if (s.last_lit) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }
  return BS_BLOCK_DONE;
}

/* ===========================================================================
 * Same as above, but achieves better compression. We use a lazy
 * evaluation for matches: a match is finally adopted only if there is
 * no better match at the next window position.
 */
function deflate_slow(s, flush) {
  var hash_head;          /* head of hash chain */
  var bflush;              /* set if current block must be flushed */

  var max_insert;

  /* Process the input block. */
  for (;;) {
    /* Make sure that we always have enough lookahead, except
     * at the end of the input file. We need MAX_MATCH bytes
     * for the next match, plus MIN_MATCH bytes to insert the
     * string following the next match.
     */
    if (s.lookahead < MIN_LOOKAHEAD) {
      fill_window(s);
      if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {
        return BS_NEED_MORE;
      }
      if (s.lookahead === 0) { break; } /* flush the current block */
    }

    /* Insert the string window[strstart .. strstart+2] in the
     * dictionary, and set hash_head to the head of the hash chain:
     */
    hash_head = 0/*NIL*/;
    if (s.lookahead >= MIN_MATCH) {
      /*** INSERT_STRING(s, s.strstart, hash_head); ***/
      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
      hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
      s.head[s.ins_h] = s.strstart;
      /***/
    }

    /* Find the longest match, discarding those <= prev_length.
     */
    s.prev_length = s.match_length;
    s.prev_match = s.match_start;
    s.match_length = MIN_MATCH - 1;

    if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match &&
        s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) {
      /* To simplify the code, we prevent matches with the string
       * of window index 0 (in particular we have to avoid a match
       * of the string with itself at the start of the input file).
       */
      s.match_length = longest_match(s, hash_head);
      /* longest_match() sets match_start */

      if (s.match_length <= 5 &&
         (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) {

        /* If prev_match is also MIN_MATCH, match_start is garbage
         * but we will ignore the current match anyway.
         */
        s.match_length = MIN_MATCH - 1;
      }
    }
    /* If there was a match at the previous step and the current
     * match is not better, output the previous match:
     */
    if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) {
      max_insert = s.strstart + s.lookahead - MIN_MATCH;
      /* Do not insert strings in hash table beyond this. */

      //check_match(s, s.strstart-1, s.prev_match, s.prev_length);

      /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match,
                     s.prev_length - MIN_MATCH, bflush);***/
      bflush = trees._tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH);
      /* Insert in hash table all strings up to the end of the match.
       * strstart-1 and strstart are already inserted. If there is not
       * enough lookahead, the last two strings are not inserted in
       * the hash table.
       */
      s.lookahead -= s.prev_length - 1;
      s.prev_length -= 2;
      do {
        if (++s.strstart <= max_insert) {
          /*** INSERT_STRING(s, s.strstart, hash_head); ***/
          s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = s.strstart;
          /***/
        }
      } while (--s.prev_length !== 0);
      s.match_available = 0;
      s.match_length = MIN_MATCH - 1;
      s.strstart++;

      if (bflush) {
        /*** FLUSH_BLOCK(s, 0); ***/
        flush_block_only(s, false);
        if (s.strm.avail_out === 0) {
          return BS_NEED_MORE;
        }
        /***/
      }

    } else if (s.match_available) {
      /* If there was no match at the previous position, output a
       * single literal. If there was a match but the current match
       * is longer, truncate the previous match to a single literal.
       */
      //Tracevv((stderr,"%c", s->window[s->strstart-1]));
      /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
      bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]);

      if (bflush) {
        /*** FLUSH_BLOCK_ONLY(s, 0) ***/
        flush_block_only(s, false);
        /***/
      }
      s.strstart++;
      s.lookahead--;
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
    } else {
      /* There is no previous match to compare with, wait for
       * the next step to decide.
       */
      s.match_available = 1;
      s.strstart++;
      s.lookahead--;
    }
  }
  //Assert (flush != Z_NO_FLUSH, "no flush?");
  if (s.match_available) {
    //Tracevv((stderr,"%c", s->window[s->strstart-1]));
    /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
    bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]);

    s.match_available = 0;
  }
  s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;
  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }
  if (s.last_lit) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }

  return BS_BLOCK_DONE;
}


/* ===========================================================================
 * For Z_RLE, simply look for runs of bytes, generate matches only of distance
 * one.  Do not maintain a hash table.  (It will be regenerated if this run of
 * deflate switches away from Z_RLE.)
 */
function deflate_rle(s, flush) {
  var bflush;            /* set if current block must be flushed */
  var prev;              /* byte at distance one to match */
  var scan, strend;      /* scan goes up to strend for length of run */

  var _win = s.window;

  for (;;) {
    /* Make sure that we always have enough lookahead, except
     * at the end of the input file. We need MAX_MATCH bytes
     * for the longest run, plus one for the unrolled loop.
     */
    if (s.lookahead <= MAX_MATCH) {
      fill_window(s);
      if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) {
        return BS_NEED_MORE;
      }
      if (s.lookahead === 0) { break; } /* flush the current block */
    }

    /* See how many times the previous byte repeats */
    s.match_length = 0;
    if (s.lookahead >= MIN_MATCH && s.strstart > 0) {
      scan = s.strstart - 1;
      prev = _win[scan];
      if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {
        strend = s.strstart + MAX_MATCH;
        do {
          /*jshint noempty:false*/
        } while (prev === _win[++scan] && prev === _win[++scan] &&
                 prev === _win[++scan] && prev === _win[++scan] &&
                 prev === _win[++scan] && prev === _win[++scan] &&
                 prev === _win[++scan] && prev === _win[++scan] &&
                 scan < strend);
        s.match_length = MAX_MATCH - (strend - scan);
        if (s.match_length > s.lookahead) {
          s.match_length = s.lookahead;
        }
      }
      //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
    }

    /* Emit match if have run of MIN_MATCH or longer, else emit literal */
    if (s.match_length >= MIN_MATCH) {
      //check_match(s, s.strstart, s.strstart - 1, s.match_length);

      /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/
      bflush = trees._tr_tally(s, 1, s.match_length - MIN_MATCH);

      s.lookahead -= s.match_length;
      s.strstart += s.match_length;
      s.match_length = 0;
    } else {
      /* No match, output a literal byte */
      //Tracevv((stderr,"%c", s->window[s->strstart]));
      /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
      bflush = trees._tr_tally(s, 0, s.window[s.strstart]);

      s.lookahead--;
      s.strstart++;
    }
    if (bflush) {
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/
    }
  }
  s.insert = 0;
  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }
  if (s.last_lit) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }
  return BS_BLOCK_DONE;
}

/* ===========================================================================
 * For Z_HUFFMAN_ONLY, do not look for matches.  Do not maintain a hash table.
 * (It will be regenerated if this run of deflate switches away from Huffman.)
 */
function deflate_huff(s, flush) {
  var bflush;             /* set if current block must be flushed */

  for (;;) {
    /* Make sure that we have a literal to write. */
    if (s.lookahead === 0) {
      fill_window(s);
      if (s.lookahead === 0) {
        if (flush === Z_NO_FLUSH) {
          return BS_NEED_MORE;
        }
        break;      /* flush the current block */
      }
    }

    /* Output a literal byte */
    s.match_length = 0;
    //Tracevv((stderr,"%c", s->window[s->strstart]));
    /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
    bflush = trees._tr_tally(s, 0, s.window[s.strstart]);
    s.lookahead--;
    s.strstart++;
    if (bflush) {
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/
    }
  }
  s.insert = 0;
  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }
  if (s.last_lit) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }
  return BS_BLOCK_DONE;
}

/* Values for max_lazy_match, good_match and max_chain_length, depending on
 * the desired pack level (0..9). The values given below have been tuned to
 * exclude worst case performance for pathological files. Better values may be
 * found for specific files.
 */
function Config(good_length, max_lazy, nice_length, max_chain, func) {
  this.good_length = good_length;
  this.max_lazy = max_lazy;
  this.nice_length = nice_length;
  this.max_chain = max_chain;
  this.func = func;
}

var configuration_table;

configuration_table = [
  /*      good lazy nice chain */
  new Config(0, 0, 0, 0, deflate_stored),          /* 0 store only */
  new Config(4, 4, 8, 4, deflate_fast),            /* 1 max speed, no lazy matches */
  new Config(4, 5, 16, 8, deflate_fast),           /* 2 */
  new Config(4, 6, 32, 32, deflate_fast),          /* 3 */

  new Config(4, 4, 16, 16, deflate_slow),          /* 4 lazy matches */
  new Config(8, 16, 32, 32, deflate_slow),         /* 5 */
  new Config(8, 16, 128, 128, deflate_slow),       /* 6 */
  new Config(8, 32, 128, 256, deflate_slow),       /* 7 */
  new Config(32, 128, 258, 1024, deflate_slow),    /* 8 */
  new Config(32, 258, 258, 4096, deflate_slow)     /* 9 max compression */
];


/* ===========================================================================
 * Initialize the "longest match" routines for a new zlib stream
 */
function lm_init(s) {
  s.window_size = 2 * s.w_size;

  /*** CLEAR_HASH(s); ***/
  zero(s.head); // Fill with NIL (= 0);

  /* Set the default configuration parameters:
   */
  s.max_lazy_match = configuration_table[s.level].max_lazy;
  s.good_match = configuration_table[s.level].good_length;
  s.nice_match = configuration_table[s.level].nice_length;
  s.max_chain_length = configuration_table[s.level].max_chain;

  s.strstart = 0;
  s.block_start = 0;
  s.lookahead = 0;
  s.insert = 0;
  s.match_length = s.prev_length = MIN_MATCH - 1;
  s.match_available = 0;
  s.ins_h = 0;
}


function DeflateState() {
  this.strm = null;            /* pointer back to this zlib stream */
  this.status = 0;            /* as the name implies */
  this.pending_buf = null;      /* output still pending */
  this.pending_buf_size = 0;  /* size of pending_buf */
  this.pending_out = 0;       /* next pending byte to output to the stream */
  this.pending = 0;           /* nb of bytes in the pending buffer */
  this.wrap = 0;              /* bit 0 true for zlib, bit 1 true for gzip */
  this.gzhead = null;         /* gzip header information to write */
  this.gzindex = 0;           /* where in extra, name, or comment */
  this.method = Z_DEFLATED; /* can only be DEFLATED */
  this.last_flush = -1;   /* value of flush param for previous deflate call */

  this.w_size = 0;  /* LZ77 window size (32K by default) */
  this.w_bits = 0;  /* log2(w_size)  (8..16) */
  this.w_mask = 0;  /* w_size - 1 */

  this.window = null;
  /* Sliding window. Input bytes are read into the second half of the window,
   * and move to the first half later to keep a dictionary of at least wSize
   * bytes. With this organization, matches are limited to a distance of
   * wSize-MAX_MATCH bytes, but this ensures that IO is always
   * performed with a length multiple of the block size.
   */

  this.window_size = 0;
  /* Actual size of window: 2*wSize, except when the user input buffer
   * is directly used as sliding window.
   */

  this.prev = null;
  /* Link to older string with same hash index. To limit the size of this
   * array to 64K, this link is maintained only for the last 32K strings.
   * An index in this array is thus a window index modulo 32K.
   */

  this.head = null;   /* Heads of the hash chains or NIL. */

  this.ins_h = 0;       /* hash index of string to be inserted */
  this.hash_size = 0;   /* number of elements in hash table */
  this.hash_bits = 0;   /* log2(hash_size) */
  this.hash_mask = 0;   /* hash_size-1 */

  this.hash_shift = 0;
  /* Number of bits by which ins_h must be shifted at each input
   * step. It must be such that after MIN_MATCH steps, the oldest
   * byte no longer takes part in the hash key, that is:
   *   hash_shift * MIN_MATCH >= hash_bits
   */

  this.block_start = 0;
  /* Window position at the beginning of the current output block. Gets
   * negative when the window is moved backwards.
   */

  this.match_length = 0;      /* length of best match */
  this.prev_match = 0;        /* previous match */
  this.match_available = 0;   /* set if previous match exists */
  this.strstart = 0;          /* start of string to insert */
  this.match_start = 0;       /* start of matching string */
  this.lookahead = 0;         /* number of valid bytes ahead in window */

  this.prev_length = 0;
  /* Length of the best match at previous step. Matches not greater than this
   * are discarded. This is used in the lazy match evaluation.
   */

  this.max_chain_length = 0;
  /* To speed up deflation, hash chains are never searched beyond this
   * length.  A higher limit improves compression ratio but degrades the
   * speed.
   */

  this.max_lazy_match = 0;
  /* Attempt to find a better match only when the current match is strictly
   * smaller than this value. This mechanism is used only for compression
   * levels >= 4.
   */
  // That's alias to max_lazy_match, don't use directly
  //this.max_insert_length = 0;
  /* Insert new strings in the hash table only if the match length is not
   * greater than this length. This saves time but degrades compression.
   * max_insert_length is used only for compression levels <= 3.
   */

  this.level = 0;     /* compression level (1..9) */
  this.strategy = 0;  /* favor or force Huffman coding*/

  this.good_match = 0;
  /* Use a faster search when the previous match is longer than this */

  this.nice_match = 0; /* Stop searching when current match exceeds this */

              /* used by trees.c: */

  /* Didn't use ct_data typedef below to suppress compiler warning */

  // struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */
  // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
  // struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */

  // Use flat array of DOUBLE size, with interleaved fata,
  // because JS does not support effective
  this.dyn_ltree  = new utils.Buf16(HEAP_SIZE * 2);
  this.dyn_dtree  = new utils.Buf16((2 * D_CODES + 1) * 2);
  this.bl_tree    = new utils.Buf16((2 * BL_CODES + 1) * 2);
  zero(this.dyn_ltree);
  zero(this.dyn_dtree);
  zero(this.bl_tree);

  this.l_desc   = null;         /* desc. for literal tree */
  this.d_desc   = null;         /* desc. for distance tree */
  this.bl_desc  = null;         /* desc. for bit length tree */

  //ush bl_count[MAX_BITS+1];
  this.bl_count = new utils.Buf16(MAX_BITS + 1);
  /* number of codes at each bit length for an optimal tree */

  //int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */
  this.heap = new utils.Buf16(2 * L_CODES + 1);  /* heap used to build the Huffman trees */
  zero(this.heap);

  this.heap_len = 0;               /* number of elements in the heap */
  this.heap_max = 0;               /* element of largest frequency */
  /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
   * The same heap array is used to build all trees.
   */

  this.depth = new utils.Buf16(2 * L_CODES + 1); //uch depth[2*L_CODES+1];
  zero(this.depth);
  /* Depth of each subtree used as tie breaker for trees of equal frequency
   */

  this.l_buf = 0;          /* buffer index for literals or lengths */

  this.lit_bufsize = 0;
  /* Size of match buffer for literals/lengths.  There are 4 reasons for
   * limiting lit_bufsize to 64K:
   *   - frequencies can be kept in 16 bit counters
   *   - if compression is not successful for the first block, all input
   *     data is still in the window so we can still emit a stored block even
   *     when input comes from standard input.  (This can also be done for
   *     all blocks if lit_bufsize is not greater than 32K.)
   *   - if compression is not successful for a file smaller than 64K, we can
   *     even emit a stored file instead of a stored block (saving 5 bytes).
   *     This is applicable only for zip (not gzip or zlib).
   *   - creating new Huffman trees less frequently may not provide fast
   *     adaptation to changes in the input data statistics. (Take for
   *     example a binary file with poorly compressible code followed by
   *     a highly compressible string table.) Smaller buffer sizes give
   *     fast adaptation but have of course the overhead of transmitting
   *     trees more frequently.
   *   - I can't count above 4
   */

  this.last_lit = 0;      /* running index in l_buf */

  this.d_buf = 0;
  /* Buffer index for distances. To simplify the code, d_buf and l_buf have
   * the same number of elements. To use different lengths, an extra flag
   * array would be necessary.
   */

  this.opt_len = 0;       /* bit length of current block with optimal trees */
  this.static_len = 0;    /* bit length of current block with static trees */
  this.matches = 0;       /* number of string matches in current block */
  this.insert = 0;        /* bytes at end of window left to insert */


  this.bi_buf = 0;
  /* Output buffer. bits are inserted starting at the bottom (least
   * significant bits).
   */
  this.bi_valid = 0;
  /* Number of valid bits in bi_buf.  All bits above the last valid bit
   * are always zero.
   */

  // Used for window memory init. We safely ignore it for JS. That makes
  // sense only for pointers and memory check tools.
  //this.high_water = 0;
  /* High water mark offset in window for initialized bytes -- bytes above
   * this are set to zero in order to avoid memory check warnings when
   * longest match routines access bytes past the input.  This is then
   * updated to the new high water mark.
   */
}


function deflateResetKeep(strm) {
  var s;

  if (!strm || !strm.state) {
    return err(strm, Z_STREAM_ERROR);
  }

  strm.total_in = strm.total_out = 0;
  strm.data_type = Z_UNKNOWN;

  s = strm.state;
  s.pending = 0;
  s.pending_out = 0;

  if (s.wrap < 0) {
    s.wrap = -s.wrap;
    /* was made negative by deflate(..., Z_FINISH); */
  }
  s.status = (s.wrap ? INIT_STATE : BUSY_STATE);
  strm.adler = (s.wrap === 2) ?
    0  // crc32(0, Z_NULL, 0)
  :
    1; // adler32(0, Z_NULL, 0)
  s.last_flush = Z_NO_FLUSH;
  trees._tr_init(s);
  return Z_OK;
}


function deflateReset(strm) {
  var ret = deflateResetKeep(strm);
  if (ret === Z_OK) {
    lm_init(strm.state);
  }
  return ret;
}


function deflateSetHeader(strm, head) {
  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
  if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; }
  strm.state.gzhead = head;
  return Z_OK;
}


function deflateInit2(strm, level, method, windowBits, memLevel, strategy) {
  if (!strm) { // === Z_NULL
    return Z_STREAM_ERROR;
  }
  var wrap = 1;

  if (level === Z_DEFAULT_COMPRESSION) {
    level = 6;
  }

  if (windowBits < 0) { /* suppress zlib wrapper */
    wrap = 0;
    windowBits = -windowBits;
  }

  else if (windowBits > 15) {
    wrap = 2;           /* write gzip wrapper instead */
    windowBits -= 16;
  }


  if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED ||
    windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
    strategy < 0 || strategy > Z_FIXED) {
    return err(strm, Z_STREAM_ERROR);
  }


  if (windowBits === 8) {
    windowBits = 9;
  }
  /* until 256-byte window bug fixed */

  var s = new DeflateState();

  strm.state = s;
  s.strm = strm;

  s.wrap = wrap;
  s.gzhead = null;
  s.w_bits = windowBits;
  s.w_size = 1 << s.w_bits;
  s.w_mask = s.w_size - 1;

  s.hash_bits = memLevel + 7;
  s.hash_size = 1 << s.hash_bits;
  s.hash_mask = s.hash_size - 1;
  s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH);

  s.window = new utils.Buf8(s.w_size * 2);
  s.head = new utils.Buf16(s.hash_size);
  s.prev = new utils.Buf16(s.w_size);

  // Don't need mem init magic for JS.
  //s.high_water = 0;  /* nothing written to s->window yet */

  s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */

  s.pending_buf_size = s.lit_bufsize * 4;

  //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
  //s->pending_buf = (uchf *) overlay;
  s.pending_buf = new utils.Buf8(s.pending_buf_size);

  // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`)
  //s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
  s.d_buf = 1 * s.lit_bufsize;

  //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
  s.l_buf = (1 + 2) * s.lit_bufsize;

  s.level = level;
  s.strategy = strategy;
  s.method = method;

  return deflateReset(strm);
}

function deflateInit(strm, level) {
  return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
}


function deflate(strm, flush) {
  var old_flush, s;
  var beg, val; // for gzip header write only

  if (!strm || !strm.state ||
    flush > Z_BLOCK || flush < 0) {
    return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR;
  }

  s = strm.state;

  if (!strm.output ||
      (!strm.input && strm.avail_in !== 0) ||
      (s.status === FINISH_STATE && flush !== Z_FINISH)) {
    return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR);
  }

  s.strm = strm; /* just in case */
  old_flush = s.last_flush;
  s.last_flush = flush;

  /* Write the header */
  if (s.status === INIT_STATE) {

    if (s.wrap === 2) { // GZIP header
      strm.adler = 0;  //crc32(0L, Z_NULL, 0);
      put_byte(s, 31);
      put_byte(s, 139);
      put_byte(s, 8);
      if (!s.gzhead) { // s->gzhead == Z_NULL
        put_byte(s, 0);
        put_byte(s, 0);
        put_byte(s, 0);
        put_byte(s, 0);
        put_byte(s, 0);
        put_byte(s, s.level === 9 ? 2 :
                    (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?
                     4 : 0));
        put_byte(s, OS_CODE);
        s.status = BUSY_STATE;
      }
      else {
        put_byte(s, (s.gzhead.text ? 1 : 0) +
                    (s.gzhead.hcrc ? 2 : 0) +
                    (!s.gzhead.extra ? 0 : 4) +
                    (!s.gzhead.name ? 0 : 8) +
                    (!s.gzhead.comment ? 0 : 16)
        );
        put_byte(s, s.gzhead.time & 0xff);
        put_byte(s, (s.gzhead.time >> 8) & 0xff);
        put_byte(s, (s.gzhead.time >> 16) & 0xff);
        put_byte(s, (s.gzhead.time >> 24) & 0xff);
        put_byte(s, s.level === 9 ? 2 :
                    (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?
                     4 : 0));
        put_byte(s, s.gzhead.os & 0xff);
        if (s.gzhead.extra && s.gzhead.extra.length) {
          put_byte(s, s.gzhead.extra.length & 0xff);
          put_byte(s, (s.gzhead.extra.length >> 8) & 0xff);
        }
        if (s.gzhead.hcrc) {
          strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0);
        }
        s.gzindex = 0;
        s.status = EXTRA_STATE;
      }
    }
    else // DEFLATE header
    {
      var header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8;
      var level_flags = -1;

      if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) {
        level_flags = 0;
      } else if (s.level < 6) {
        level_flags = 1;
      } else if (s.level === 6) {
        level_flags = 2;
      } else {
        level_flags = 3;
      }
      header |= (level_flags << 6);
      if (s.strstart !== 0) { header |= PRESET_DICT; }
      header += 31 - (header % 31);

      s.status = BUSY_STATE;
      putShortMSB(s, header);

      /* Save the adler32 of the preset dictionary: */
      if (s.strstart !== 0) {
        putShortMSB(s, strm.adler >>> 16);
        putShortMSB(s, strm.adler & 0xffff);
      }
      strm.adler = 1; // adler32(0L, Z_NULL, 0);
    }
  }

//#ifdef GZIP
  if (s.status === EXTRA_STATE) {
    if (s.gzhead.extra/* != Z_NULL*/) {
      beg = s.pending;  /* start of bytes to update crc */

      while (s.gzindex < (s.gzhead.extra.length & 0xffff)) {
        if (s.pending === s.pending_buf_size) {
          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
          }
          flush_pending(strm);
          beg = s.pending;
          if (s.pending === s.pending_buf_size) {
            break;
          }
        }
        put_byte(s, s.gzhead.extra[s.gzindex] & 0xff);
        s.gzindex++;
      }
      if (s.gzhead.hcrc && s.pending > beg) {
        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
      }
      if (s.gzindex === s.gzhead.extra.length) {
        s.gzindex = 0;
        s.status = NAME_STATE;
      }
    }
    else {
      s.status = NAME_STATE;
    }
  }
  if (s.status === NAME_STATE) {
    if (s.gzhead.name/* != Z_NULL*/) {
      beg = s.pending;  /* start of bytes to update crc */
      //int val;

      do {
        if (s.pending === s.pending_buf_size) {
          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
          }
          flush_pending(strm);
          beg = s.pending;
          if (s.pending === s.pending_buf_size) {
            val = 1;
            break;
          }
        }
        // JS specific: little magic to add zero terminator to end of string
        if (s.gzindex < s.gzhead.name.length) {
          val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;
        } else {
          val = 0;
        }
        put_byte(s, val);
      } while (val !== 0);

      if (s.gzhead.hcrc && s.pending > beg) {
        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
      }
      if (val === 0) {
        s.gzindex = 0;
        s.status = COMMENT_STATE;
      }
    }
    else {
      s.status = COMMENT_STATE;
    }
  }
  if (s.status === COMMENT_STATE) {
    if (s.gzhead.comment/* != Z_NULL*/) {
      beg = s.pending;  /* start of bytes to update crc */
      //int val;

      do {
        if (s.pending === s.pending_buf_size) {
          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
          }
          flush_pending(strm);
          beg = s.pending;
          if (s.pending === s.pending_buf_size) {
            val = 1;
            break;
          }
        }
        // JS specific: little magic to add zero terminator to end of string
        if (s.gzindex < s.gzhead.comment.length) {
          val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;
        } else {
          val = 0;
        }
        put_byte(s, val);
      } while (val !== 0);

      if (s.gzhead.hcrc && s.pending > beg) {
        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
      }
      if (val === 0) {
        s.status = HCRC_STATE;
      }
    }
    else {
      s.status = HCRC_STATE;
    }
  }
  if (s.status === HCRC_STATE) {
    if (s.gzhead.hcrc) {
      if (s.pending + 2 > s.pending_buf_size) {
        flush_pending(strm);
      }
      if (s.pending + 2 <= s.pending_buf_size) {
        put_byte(s, strm.adler & 0xff);
        put_byte(s, (strm.adler >> 8) & 0xff);
        strm.adler = 0; //crc32(0L, Z_NULL, 0);
        s.status = BUSY_STATE;
      }
    }
    else {
      s.status = BUSY_STATE;
    }
  }
//#endif

  /* Flush as much pending output as possible */
  if (s.pending !== 0) {
    flush_pending(strm);
    if (strm.avail_out === 0) {
      /* Since avail_out is 0, deflate will be called again with
       * more output space, but possibly with both pending and
       * avail_in equal to zero. There won't be anything to do,
       * but this is not an error situation so make sure we
       * return OK instead of BUF_ERROR at next call of deflate:
       */
      s.last_flush = -1;
      return Z_OK;
    }

    /* Make sure there is something to do and avoid duplicate consecutive
     * flushes. For repeated and useless calls with Z_FINISH, we keep
     * returning Z_STREAM_END instead of Z_BUF_ERROR.
     */
  } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) &&
    flush !== Z_FINISH) {
    return err(strm, Z_BUF_ERROR);
  }

  /* User must not provide more input after the first FINISH: */
  if (s.status === FINISH_STATE && strm.avail_in !== 0) {
    return err(strm, Z_BUF_ERROR);
  }

  /* Start a new block or continue the current one.
   */
  if (strm.avail_in !== 0 || s.lookahead !== 0 ||
    (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) {
    var bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) :
      (s.strategy === Z_RLE ? deflate_rle(s, flush) :
        configuration_table[s.level].func(s, flush));

    if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) {
      s.status = FINISH_STATE;
    }
    if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) {
      if (strm.avail_out === 0) {
        s.last_flush = -1;
        /* avoid BUF_ERROR next call, see above */
      }
      return Z_OK;
      /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
       * of deflate should use the same flush parameter to make sure
       * that the flush is complete. So we don't have to output an
       * empty block here, this will be done at next call. This also
       * ensures that for a very small output buffer, we emit at most
       * one empty block.
       */
    }
    if (bstate === BS_BLOCK_DONE) {
      if (flush === Z_PARTIAL_FLUSH) {
        trees._tr_align(s);
      }
      else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */

        trees._tr_stored_block(s, 0, 0, false);
        /* For a full flush, this empty block will be recognized
         * as a special marker by inflate_sync().
         */
        if (flush === Z_FULL_FLUSH) {
          /*** CLEAR_HASH(s); ***/             /* forget history */
          zero(s.head); // Fill with NIL (= 0);

          if (s.lookahead === 0) {
            s.strstart = 0;
            s.block_start = 0;
            s.insert = 0;
          }
        }
      }
      flush_pending(strm);
      if (strm.avail_out === 0) {
        s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */
        return Z_OK;
      }
    }
  }
  //Assert(strm->avail_out > 0, "bug2");
  //if (strm.avail_out <= 0) { throw new Error("bug2");}

  if (flush !== Z_FINISH) { return Z_OK; }
  if (s.wrap <= 0) { return Z_STREAM_END; }

  /* Write the trailer */
  if (s.wrap === 2) {
    put_byte(s, strm.adler & 0xff);
    put_byte(s, (strm.adler >> 8) & 0xff);
    put_byte(s, (strm.adler >> 16) & 0xff);
    put_byte(s, (strm.adler >> 24) & 0xff);
    put_byte(s, strm.total_in & 0xff);
    put_byte(s, (strm.total_in >> 8) & 0xff);
    put_byte(s, (strm.total_in >> 16) & 0xff);
    put_byte(s, (strm.total_in >> 24) & 0xff);
  }
  else
  {
    putShortMSB(s, strm.adler >>> 16);
    putShortMSB(s, strm.adler & 0xffff);
  }

  flush_pending(strm);
  /* If avail_out is zero, the application will call deflate again
   * to flush the rest.
   */
  if (s.wrap > 0) { s.wrap = -s.wrap; }
  /* write the trailer only once! */
  return s.pending !== 0 ? Z_OK : Z_STREAM_END;
}

function deflateEnd(strm) {
  var status;

  if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {
    return Z_STREAM_ERROR;
  }

  status = strm.state.status;
  if (status !== INIT_STATE &&
    status !== EXTRA_STATE &&
    status !== NAME_STATE &&
    status !== COMMENT_STATE &&
    status !== HCRC_STATE &&
    status !== BUSY_STATE &&
    status !== FINISH_STATE
  ) {
    return err(strm, Z_STREAM_ERROR);
  }

  strm.state = null;

  return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK;
}


/* =========================================================================
 * Initializes the compression dictionary from the given byte
 * sequence without producing any compressed output.
 */
function deflateSetDictionary(strm, dictionary) {
  var dictLength = dictionary.length;

  var s;
  var str, n;
  var wrap;
  var avail;
  var next;
  var input;
  var tmpDict;

  if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {
    return Z_STREAM_ERROR;
  }

  s = strm.state;
  wrap = s.wrap;

  if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) {
    return Z_STREAM_ERROR;
  }

  /* when using zlib wrappers, compute Adler-32 for provided dictionary */
  if (wrap === 1) {
    /* adler32(strm->adler, dictionary, dictLength); */
    strm.adler = adler32(strm.adler, dictionary, dictLength, 0);
  }

  s.wrap = 0;   /* avoid computing Adler-32 in read_buf */

  /* if dictionary would fill window, just replace the history */
  if (dictLength >= s.w_size) {
    if (wrap === 0) {            /* already empty otherwise */
      /*** CLEAR_HASH(s); ***/
      zero(s.head); // Fill with NIL (= 0);
      s.strstart = 0;
      s.block_start = 0;
      s.insert = 0;
    }
    /* use the tail */
    // dictionary = dictionary.slice(dictLength - s.w_size);
    tmpDict = new utils.Buf8(s.w_size);
    utils.arraySet(tmpDict, dictionary, dictLength - s.w_size, s.w_size, 0);
    dictionary = tmpDict;
    dictLength = s.w_size;
  }
  /* insert dictionary into window and hash */
  avail = strm.avail_in;
  next = strm.next_in;
  input = strm.input;
  strm.avail_in = dictLength;
  strm.next_in = 0;
  strm.input = dictionary;
  fill_window(s);
  while (s.lookahead >= MIN_MATCH) {
    str = s.strstart;
    n = s.lookahead - (MIN_MATCH - 1);
    do {
      /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask;

      s.prev[str & s.w_mask] = s.head[s.ins_h];

      s.head[s.ins_h] = str;
      str++;
    } while (--n);
    s.strstart = str;
    s.lookahead = MIN_MATCH - 1;
    fill_window(s);
  }
  s.strstart += s.lookahead;
  s.block_start = s.strstart;
  s.insert = s.lookahead;
  s.lookahead = 0;
  s.match_length = s.prev_length = MIN_MATCH - 1;
  s.match_available = 0;
  strm.next_in = next;
  strm.input = input;
  strm.avail_in = avail;
  s.wrap = wrap;
  return Z_OK;
}


exports.deflateInit = deflateInit;
exports.deflateInit2 = deflateInit2;
exports.deflateReset = deflateReset;
exports.deflateResetKeep = deflateResetKeep;
exports.deflateSetHeader = deflateSetHeader;
exports.deflate = deflate;
exports.deflateEnd = deflateEnd;
exports.deflateSetDictionary = deflateSetDictionary;
exports.deflateInfo = 'pako deflate (from Nodeca project)';

/* Not implemented
exports.deflateBound = deflateBound;
exports.deflateCopy = deflateCopy;
exports.deflateParams = deflateParams;
exports.deflatePending = deflatePending;
exports.deflatePrime = deflatePrime;
exports.deflateTune = deflateTune;
*/

},{"../utils/common":179,"./adler32":180,"./crc32":182,"./messages":187,"./trees":188}],184:[function(require,module,exports){
'use strict';

// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

// See state defs from inflate.js
var BAD = 30;       /* got a data error -- remain here until reset */
var TYPE = 12;      /* i: waiting for type bits, including last-flag bit */

/*
   Decode literal, length, and distance codes and write out the resulting
   literal and match bytes until either not enough input or output is
   available, an end-of-block is encountered, or a data error is encountered.
   When large enough input and output buffers are supplied to inflate(), for
   example, a 16K input buffer and a 64K output buffer, more than 95% of the
   inflate execution time is spent in this routine.

   Entry assumptions:

        state.mode === LEN
        strm.avail_in >= 6
        strm.avail_out >= 258
        start >= strm.avail_out
        state.bits < 8

   On return, state.mode is one of:

        LEN -- ran out of enough output space or enough available input
        TYPE -- reached end of block code, inflate() to interpret next block
        BAD -- error in block data

   Notes:

    - The maximum input bits used by a length/distance pair is 15 bits for the
      length code, 5 bits for the length extra, 15 bits for the distance code,
      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
      Therefore if strm.avail_in >= 6, then there is enough input to avoid
      checking for available input while decoding.

    - The maximum bytes that a single length/distance pair can output is 258
      bytes, which is the maximum length that can be coded.  inflate_fast()
      requires strm.avail_out >= 258 for each loop to avoid checking for
      output space.
 */
module.exports = function inflate_fast(strm, start) {
  var state;
  var _in;                    /* local strm.input */
  var last;                   /* have enough input while in < last */
  var _out;                   /* local strm.output */
  var beg;                    /* inflate()'s initial strm.output */
  var end;                    /* while out < end, enough space available */
//#ifdef INFLATE_STRICT
  var dmax;                   /* maximum distance from zlib header */
//#endif
  var wsize;                  /* window size or zero if not using window */
  var whave;                  /* valid bytes in the window */
  var wnext;                  /* window write index */
  // Use `s_window` instead `window`, avoid conflict with instrumentation tools
  var s_window;               /* allocated sliding window, if wsize != 0 */
  var hold;                   /* local strm.hold */
  var bits;                   /* local strm.bits */
  var lcode;                  /* local strm.lencode */
  var dcode;                  /* local strm.distcode */
  var lmask;                  /* mask for first level of length codes */
  var dmask;                  /* mask for first level of distance codes */
  var here;                   /* retrieved table entry */
  var op;                     /* code bits, operation, extra bits, or */
                              /*  window position, window bytes to copy */
  var len;                    /* match length, unused bytes */
  var dist;                   /* match distance */
  var from;                   /* where to copy match from */
  var from_source;


  var input, output; // JS specific, because we have no pointers

  /* copy state to local variables */
  state = strm.state;
  //here = state.here;
  _in = strm.next_in;
  input = strm.input;
  last = _in + (strm.avail_in - 5);
  _out = strm.next_out;
  output = strm.output;
  beg = _out - (start - strm.avail_out);
  end = _out + (strm.avail_out - 257);
//#ifdef INFLATE_STRICT
  dmax = state.dmax;
//#endif
  wsize = state.wsize;
  whave = state.whave;
  wnext = state.wnext;
  s_window = state.window;
  hold = state.hold;
  bits = state.bits;
  lcode = state.lencode;
  dcode = state.distcode;
  lmask = (1 << state.lenbits) - 1;
  dmask = (1 << state.distbits) - 1;


  /* decode literals and length/distances until end-of-block or not enough
     input data or output space */

  top:
  do {
    if (bits < 15) {
      hold += input[_in++] << bits;
      bits += 8;
      hold += input[_in++] << bits;
      bits += 8;
    }

    here = lcode[hold & lmask];

    dolen:
    for (;;) { // Goto emulation
      op = here >>> 24/*here.bits*/;
      hold >>>= op;
      bits -= op;
      op = (here >>> 16) & 0xff/*here.op*/;
      if (op === 0) {                          /* literal */
        //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
        //        "inflate:         literal '%c'\n" :
        //        "inflate:         literal 0x%02x\n", here.val));
        output[_out++] = here & 0xffff/*here.val*/;
      }
      else if (op & 16) {                     /* length base */
        len = here & 0xffff/*here.val*/;
        op &= 15;                           /* number of extra bits */
        if (op) {
          if (bits < op) {
            hold += input[_in++] << bits;
            bits += 8;
          }
          len += hold & ((1 << op) - 1);
          hold >>>= op;
          bits -= op;
        }
        //Tracevv((stderr, "inflate:         length %u\n", len));
        if (bits < 15) {
          hold += input[_in++] << bits;
          bits += 8;
          hold += input[_in++] << bits;
          bits += 8;
        }
        here = dcode[hold & dmask];

        dodist:
        for (;;) { // goto emulation
          op = here >>> 24/*here.bits*/;
          hold >>>= op;
          bits -= op;
          op = (here >>> 16) & 0xff/*here.op*/;

          if (op & 16) {                      /* distance base */
            dist = here & 0xffff/*here.val*/;
            op &= 15;                       /* number of extra bits */
            if (bits < op) {
              hold += input[_in++] << bits;
              bits += 8;
              if (bits < op) {
                hold += input[_in++] << bits;
                bits += 8;
              }
            }
            dist += hold & ((1 << op) - 1);
//#ifdef INFLATE_STRICT
            if (dist > dmax) {
              strm.msg = 'invalid distance too far back';
              state.mode = BAD;
              break top;
            }
//#endif
            hold >>>= op;
            bits -= op;
            //Tracevv((stderr, "inflate:         distance %u\n", dist));
            op = _out - beg;                /* max distance in output */
            if (dist > op) {                /* see if copy from window */
              op = dist - op;               /* distance back in window */
              if (op > whave) {
                if (state.sane) {
                  strm.msg = 'invalid distance too far back';
                  state.mode = BAD;
                  break top;
                }

// (!) This block is disabled in zlib defaults,
// don't enable it for binary compatibility
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
//                if (len <= op - whave) {
//                  do {
//                    output[_out++] = 0;
//                  } while (--len);
//                  continue top;
//                }
//                len -= op - whave;
//                do {
//                  output[_out++] = 0;
//                } while (--op > whave);
//                if (op === 0) {
//                  from = _out - dist;
//                  do {
//                    output[_out++] = output[from++];
//                  } while (--len);
//                  continue top;
//                }
//#endif
              }
              from = 0; // window index
              from_source = s_window;
              if (wnext === 0) {           /* very common case */
                from += wsize - op;
                if (op < len) {         /* some from window */
                  len -= op;
                  do {
                    output[_out++] = s_window[from++];
                  } while (--op);
                  from = _out - dist;  /* rest from output */
                  from_source = output;
                }
              }
              else if (wnext < op) {      /* wrap around window */
                from += wsize + wnext - op;
                op -= wnext;
                if (op < len) {         /* some from end of window */
                  len -= op;
                  do {
                    output[_out++] = s_window[from++];
                  } while (--op);
                  from = 0;
                  if (wnext < len) {  /* some from start of window */
                    op = wnext;
                    len -= op;
                    do {
                      output[_out++] = s_window[from++];
                    } while (--op);
                    from = _out - dist;      /* rest from output */
                    from_source = output;
                  }
                }
              }
              else {                      /* contiguous in window */
                from += wnext - op;
                if (op < len) {         /* some from window */
                  len -= op;
                  do {
                    output[_out++] = s_window[from++];
                  } while (--op);
                  from = _out - dist;  /* rest from output */
                  from_source = output;
                }
              }
              while (len > 2) {
                output[_out++] = from_source[from++];
                output[_out++] = from_source[from++];
                output[_out++] = from_source[from++];
                len -= 3;
              }
              if (len) {
                output[_out++] = from_source[from++];
                if (len > 1) {
                  output[_out++] = from_source[from++];
                }
              }
            }
            else {
              from = _out - dist;          /* copy direct from output */
              do {                        /* minimum length is three */
                output[_out++] = output[from++];
                output[_out++] = output[from++];
                output[_out++] = output[from++];
                len -= 3;
              } while (len > 2);
              if (len) {
                output[_out++] = output[from++];
                if (len > 1) {
                  output[_out++] = output[from++];
                }
              }
            }
          }
          else if ((op & 64) === 0) {          /* 2nd level distance code */
            here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
            continue dodist;
          }
          else {
            strm.msg = 'invalid distance code';
            state.mode = BAD;
            break top;
          }

          break; // need to emulate goto via "continue"
        }
      }
      else if ((op & 64) === 0) {              /* 2nd level length code */
        here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
        continue dolen;
      }
      else if (op & 32) {                     /* end-of-block */
        //Tracevv((stderr, "inflate:         end of block\n"));
        state.mode = TYPE;
        break top;
      }
      else {
        strm.msg = 'invalid literal/length code';
        state.mode = BAD;
        break top;
      }

      break; // need to emulate goto via "continue"
    }
  } while (_in < last && _out < end);

  /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
  len = bits >> 3;
  _in -= len;
  bits -= len << 3;
  hold &= (1 << bits) - 1;

  /* update state and return */
  strm.next_in = _in;
  strm.next_out = _out;
  strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last));
  strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
  state.hold = hold;
  state.bits = bits;
  return;
};

},{}],185:[function(require,module,exports){
'use strict';

// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

var utils         = require('../utils/common');
var adler32       = require('./adler32');
var crc32         = require('./crc32');
var inflate_fast  = require('./inffast');
var inflate_table = require('./inftrees');

var CODES = 0;
var LENS = 1;
var DISTS = 2;

/* Public constants ==========================================================*/
/* ===========================================================================*/


/* Allowed flush values; see deflate() and inflate() below for details */
//var Z_NO_FLUSH      = 0;
//var Z_PARTIAL_FLUSH = 1;
//var Z_SYNC_FLUSH    = 2;
//var Z_FULL_FLUSH    = 3;
var Z_FINISH        = 4;
var Z_BLOCK         = 5;
var Z_TREES         = 6;


/* Return codes for the compression/decompression functions. Negative values
 * are errors, positive values are used for special but normal events.
 */
var Z_OK            = 0;
var Z_STREAM_END    = 1;
var Z_NEED_DICT     = 2;
//var Z_ERRNO         = -1;
var Z_STREAM_ERROR  = -2;
var Z_DATA_ERROR    = -3;
var Z_MEM_ERROR     = -4;
var Z_BUF_ERROR     = -5;
//var Z_VERSION_ERROR = -6;

/* The deflate compression method */
var Z_DEFLATED  = 8;


/* STATES ====================================================================*/
/* ===========================================================================*/


var    HEAD = 1;       /* i: waiting for magic header */
var    FLAGS = 2;      /* i: waiting for method and flags (gzip) */
var    TIME = 3;       /* i: waiting for modification time (gzip) */
var    OS = 4;         /* i: waiting for extra flags and operating system (gzip) */
var    EXLEN = 5;      /* i: waiting for extra length (gzip) */
var    EXTRA = 6;      /* i: waiting for extra bytes (gzip) */
var    NAME = 7;       /* i: waiting for end of file name (gzip) */
var    COMMENT = 8;    /* i: waiting for end of comment (gzip) */
var    HCRC = 9;       /* i: waiting for header crc (gzip) */
var    DICTID = 10;    /* i: waiting for dictionary check value */
var    DICT = 11;      /* waiting for inflateSetDictionary() call */
var        TYPE = 12;      /* i: waiting for type bits, including last-flag bit */
var        TYPEDO = 13;    /* i: same, but skip check to exit inflate on new block */
var        STORED = 14;    /* i: waiting for stored size (length and complement) */
var        COPY_ = 15;     /* i/o: same as COPY below, but only first time in */
var        COPY = 16;      /* i/o: waiting for input or output to copy stored block */
var        TABLE = 17;     /* i: waiting for dynamic block table lengths */
var        LENLENS = 18;   /* i: waiting for code length code lengths */
var        CODELENS = 19;  /* i: waiting for length/lit and distance code lengths */
var            LEN_ = 20;      /* i: same as LEN below, but only first time in */
var            LEN = 21;       /* i: waiting for length/lit/eob code */
var            LENEXT = 22;    /* i: waiting for length extra bits */
var            DIST = 23;      /* i: waiting for distance code */
var            DISTEXT = 24;   /* i: waiting for distance extra bits */
var            MATCH = 25;     /* o: waiting for output space to copy string */
var            LIT = 26;       /* o: waiting for output space to write literal */
var    CHECK = 27;     /* i: waiting for 32-bit check value */
var    LENGTH = 28;    /* i: waiting for 32-bit length (gzip) */
var    DONE = 29;      /* finished check, done -- remain here until reset */
var    BAD = 30;       /* got a data error -- remain here until reset */
var    MEM = 31;       /* got an inflate() memory error -- remain here until reset */
var    SYNC = 32;      /* looking for synchronization bytes to restart inflate() */

/* ===========================================================================*/



var ENOUGH_LENS = 852;
var ENOUGH_DISTS = 592;
//var ENOUGH =  (ENOUGH_LENS+ENOUGH_DISTS);

var MAX_WBITS = 15;
/* 32K LZ77 window */
var DEF_WBITS = MAX_WBITS;


function zswap32(q) {
  return  (((q >>> 24) & 0xff) +
          ((q >>> 8) & 0xff00) +
          ((q & 0xff00) << 8) +
          ((q & 0xff) << 24));
}


function InflateState() {
  this.mode = 0;             /* current inflate mode */
  this.last = false;          /* true if processing last block */
  this.wrap = 0;              /* bit 0 true for zlib, bit 1 true for gzip */
  this.havedict = false;      /* true if dictionary provided */
  this.flags = 0;             /* gzip header method and flags (0 if zlib) */
  this.dmax = 0;              /* zlib header max distance (INFLATE_STRICT) */
  this.check = 0;             /* protected copy of check value */
  this.total = 0;             /* protected copy of output count */
  // TODO: may be {}
  this.head = null;           /* where to save gzip header information */

  /* sliding window */
  this.wbits = 0;             /* log base 2 of requested window size */
  this.wsize = 0;             /* window size or zero if not using window */
  this.whave = 0;             /* valid bytes in the window */
  this.wnext = 0;             /* window write index */
  this.window = null;         /* allocated sliding window, if needed */

  /* bit accumulator */
  this.hold = 0;              /* input bit accumulator */
  this.bits = 0;              /* number of bits in "in" */

  /* for string and stored block copying */
  this.length = 0;            /* literal or length of data to copy */
  this.offset = 0;            /* distance back to copy string from */

  /* for table and code decoding */
  this.extra = 0;             /* extra bits needed */

  /* fixed and dynamic code tables */
  this.lencode = null;          /* starting table for length/literal codes */
  this.distcode = null;         /* starting table for distance codes */
  this.lenbits = 0;           /* index bits for lencode */
  this.distbits = 0;          /* index bits for distcode */

  /* dynamic table building */
  this.ncode = 0;             /* number of code length code lengths */
  this.nlen = 0;              /* number of length code lengths */
  this.ndist = 0;             /* number of distance code lengths */
  this.have = 0;              /* number of code lengths in lens[] */
  this.next = null;              /* next available space in codes[] */

  this.lens = new utils.Buf16(320); /* temporary storage for code lengths */
  this.work = new utils.Buf16(288); /* work area for code table building */

  /*
   because we don't have pointers in js, we use lencode and distcode directly
   as buffers so we don't need codes
  */
  //this.codes = new utils.Buf32(ENOUGH);       /* space for code tables */
  this.lendyn = null;              /* dynamic table for length/literal codes (JS specific) */
  this.distdyn = null;             /* dynamic table for distance codes (JS specific) */
  this.sane = 0;                   /* if false, allow invalid distance too far */
  this.back = 0;                   /* bits back of last unprocessed length/lit */
  this.was = 0;                    /* initial length of match */
}

function inflateResetKeep(strm) {
  var state;

  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
  state = strm.state;
  strm.total_in = strm.total_out = state.total = 0;
  strm.msg = ''; /*Z_NULL*/
  if (state.wrap) {       /* to support ill-conceived Java test suite */
    strm.adler = state.wrap & 1;
  }
  state.mode = HEAD;
  state.last = 0;
  state.havedict = 0;
  state.dmax = 32768;
  state.head = null/*Z_NULL*/;
  state.hold = 0;
  state.bits = 0;
  //state.lencode = state.distcode = state.next = state.codes;
  state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS);
  state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS);

  state.sane = 1;
  state.back = -1;
  //Tracev((stderr, "inflate: reset\n"));
  return Z_OK;
}

function inflateReset(strm) {
  var state;

  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
  state = strm.state;
  state.wsize = 0;
  state.whave = 0;
  state.wnext = 0;
  return inflateResetKeep(strm);

}

function inflateReset2(strm, windowBits) {
  var wrap;
  var state;

  /* get the state */
  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
  state = strm.state;

  /* extract wrap request from windowBits parameter */
  if (windowBits < 0) {
    wrap = 0;
    windowBits = -windowBits;
  }
  else {
    wrap = (windowBits >> 4) + 1;
    if (windowBits < 48) {
      windowBits &= 15;
    }
  }

  /* set number of window bits, free window if different */
  if (windowBits && (windowBits < 8 || windowBits > 15)) {
    return Z_STREAM_ERROR;
  }
  if (state.window !== null && state.wbits !== windowBits) {
    state.window = null;
  }

  /* update state and reset the rest of it */
  state.wrap = wrap;
  state.wbits = windowBits;
  return inflateReset(strm);
}

function inflateInit2(strm, windowBits) {
  var ret;
  var state;

  if (!strm) { return Z_STREAM_ERROR; }
  //strm.msg = Z_NULL;                 /* in case we return an error */

  state = new InflateState();

  //if (state === Z_NULL) return Z_MEM_ERROR;
  //Tracev((stderr, "inflate: allocated\n"));
  strm.state = state;
  state.window = null/*Z_NULL*/;
  ret = inflateReset2(strm, windowBits);
  if (ret !== Z_OK) {
    strm.state = null/*Z_NULL*/;
  }
  return ret;
}

function inflateInit(strm) {
  return inflateInit2(strm, DEF_WBITS);
}


/*
 Return state with length and distance decoding tables and index sizes set to
 fixed code decoding.  Normally this returns fixed tables from inffixed.h.
 If BUILDFIXED is defined, then instead this routine builds the tables the
 first time it's called, and returns those tables the first time and
 thereafter.  This reduces the size of the code by about 2K bytes, in
 exchange for a little execution time.  However, BUILDFIXED should not be
 used for threaded applications, since the rewriting of the tables and virgin
 may not be thread-safe.
 */
var virgin = true;

var lenfix, distfix; // We have no pointers in JS, so keep tables separate

function fixedtables(state) {
  /* build fixed huffman tables if first call (may not be thread safe) */
  if (virgin) {
    var sym;

    lenfix = new utils.Buf32(512);
    distfix = new utils.Buf32(32);

    /* literal/length table */
    sym = 0;
    while (sym < 144) { state.lens[sym++] = 8; }
    while (sym < 256) { state.lens[sym++] = 9; }
    while (sym < 280) { state.lens[sym++] = 7; }
    while (sym < 288) { state.lens[sym++] = 8; }

    inflate_table(LENS,  state.lens, 0, 288, lenfix,   0, state.work, { bits: 9 });

    /* distance table */
    sym = 0;
    while (sym < 32) { state.lens[sym++] = 5; }

    inflate_table(DISTS, state.lens, 0, 32,   distfix, 0, state.work, { bits: 5 });

    /* do this just once */
    virgin = false;
  }

  state.lencode = lenfix;
  state.lenbits = 9;
  state.distcode = distfix;
  state.distbits = 5;
}


/*
 Update the window with the last wsize (normally 32K) bytes written before
 returning.  If window does not exist yet, create it.  This is only called
 when a window is already in use, or when output has been written during this
 inflate call, but the end of the deflate stream has not been reached yet.
 It is also called to create a window for dictionary data when a dictionary
 is loaded.

 Providing output buffers larger than 32K to inflate() should provide a speed
 advantage, since only the last 32K of output is copied to the sliding window
 upon return from inflate(), and since all distances after the first 32K of
 output will fall in the output data, making match copies simpler and faster.
 The advantage may be dependent on the size of the processor's data caches.
 */
function updatewindow(strm, src, end, copy) {
  var dist;
  var state = strm.state;

  /* if it hasn't been done already, allocate space for the window */
  if (state.window === null) {
    state.wsize = 1 << state.wbits;
    state.wnext = 0;
    state.whave = 0;

    state.window = new utils.Buf8(state.wsize);
  }

  /* copy state->wsize or less output bytes into the circular window */
  if (copy >= state.wsize) {
    utils.arraySet(state.window, src, end - state.wsize, state.wsize, 0);
    state.wnext = 0;
    state.whave = state.wsize;
  }
  else {
    dist = state.wsize - state.wnext;
    if (dist > copy) {
      dist = copy;
    }
    //zmemcpy(state->window + state->wnext, end - copy, dist);
    utils.arraySet(state.window, src, end - copy, dist, state.wnext);
    copy -= dist;
    if (copy) {
      //zmemcpy(state->window, end - copy, copy);
      utils.arraySet(state.window, src, end - copy, copy, 0);
      state.wnext = copy;
      state.whave = state.wsize;
    }
    else {
      state.wnext += dist;
      if (state.wnext === state.wsize) { state.wnext = 0; }
      if (state.whave < state.wsize) { state.whave += dist; }
    }
  }
  return 0;
}

function inflate(strm, flush) {
  var state;
  var input, output;          // input/output buffers
  var next;                   /* next input INDEX */
  var put;                    /* next output INDEX */
  var have, left;             /* available input and output */
  var hold;                   /* bit buffer */
  var bits;                   /* bits in bit buffer */
  var _in, _out;              /* save starting available input and output */
  var copy;                   /* number of stored or match bytes to copy */
  var from;                   /* where to copy match bytes from */
  var from_source;
  var here = 0;               /* current decoding table entry */
  var here_bits, here_op, here_val; // paked "here" denormalized (JS specific)
  //var last;                   /* parent table entry */
  var last_bits, last_op, last_val; // paked "last" denormalized (JS specific)
  var len;                    /* length to copy for repeats, bits to drop */
  var ret;                    /* return code */
  var hbuf = new utils.Buf8(4);    /* buffer for gzip header crc calculation */
  var opts;

  var n; // temporary var for NEED_BITS

  var order = /* permutation of code lengths */
    [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];


  if (!strm || !strm.state || !strm.output ||
      (!strm.input && strm.avail_in !== 0)) {
    return Z_STREAM_ERROR;
  }

  state = strm.state;
  if (state.mode === TYPE) { state.mode = TYPEDO; }    /* skip check */


  //--- LOAD() ---
  put = strm.next_out;
  output = strm.output;
  left = strm.avail_out;
  next = strm.next_in;
  input = strm.input;
  have = strm.avail_in;
  hold = state.hold;
  bits = state.bits;
  //---

  _in = have;
  _out = left;
  ret = Z_OK;

  inf_leave: // goto emulation
  for (;;) {
    switch (state.mode) {
      case HEAD:
        if (state.wrap === 0) {
          state.mode = TYPEDO;
          break;
        }
        //=== NEEDBITS(16);
        while (bits < 16) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        if ((state.wrap & 2) && hold === 0x8b1f) {  /* gzip header */
          state.check = 0/*crc32(0L, Z_NULL, 0)*/;
          //=== CRC2(state.check, hold);
          hbuf[0] = hold & 0xff;
          hbuf[1] = (hold >>> 8) & 0xff;
          state.check = crc32(state.check, hbuf, 2, 0);
          //===//

          //=== INITBITS();
          hold = 0;
          bits = 0;
          //===//
          state.mode = FLAGS;
          break;
        }
        state.flags = 0;           /* expect zlib header */
        if (state.head) {
          state.head.done = false;
        }
        if (!(state.wrap & 1) ||   /* check if zlib header allowed */
          (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) {
          strm.msg = 'incorrect header check';
          state.mode = BAD;
          break;
        }
        if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) {
          strm.msg = 'unknown compression method';
          state.mode = BAD;
          break;
        }
        //--- DROPBITS(4) ---//
        hold >>>= 4;
        bits -= 4;
        //---//
        len = (hold & 0x0f)/*BITS(4)*/ + 8;
        if (state.wbits === 0) {
          state.wbits = len;
        }
        else if (len > state.wbits) {
          strm.msg = 'invalid window size';
          state.mode = BAD;
          break;
        }
        state.dmax = 1 << len;
        //Tracev((stderr, "inflate:   zlib header ok\n"));
        strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
        state.mode = hold & 0x200 ? DICTID : TYPE;
        //=== INITBITS();
        hold = 0;
        bits = 0;
        //===//
        break;
      case FLAGS:
        //=== NEEDBITS(16); */
        while (bits < 16) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        state.flags = hold;
        if ((state.flags & 0xff) !== Z_DEFLATED) {
          strm.msg = 'unknown compression method';
          state.mode = BAD;
          break;
        }
        if (state.flags & 0xe000) {
          strm.msg = 'unknown header flags set';
          state.mode = BAD;
          break;
        }
        if (state.head) {
          state.head.text = ((hold >> 8) & 1);
        }
        if (state.flags & 0x0200) {
          //=== CRC2(state.check, hold);
          hbuf[0] = hold & 0xff;
          hbuf[1] = (hold >>> 8) & 0xff;
          state.check = crc32(state.check, hbuf, 2, 0);
          //===//
        }
        //=== INITBITS();
        hold = 0;
        bits = 0;
        //===//
        state.mode = TIME;
        /* falls through */
      case TIME:
        //=== NEEDBITS(32); */
        while (bits < 32) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        if (state.head) {
          state.head.time = hold;
        }
        if (state.flags & 0x0200) {
          //=== CRC4(state.check, hold)
          hbuf[0] = hold & 0xff;
          hbuf[1] = (hold >>> 8) & 0xff;
          hbuf[2] = (hold >>> 16) & 0xff;
          hbuf[3] = (hold >>> 24) & 0xff;
          state.check = crc32(state.check, hbuf, 4, 0);
          //===
        }
        //=== INITBITS();
        hold = 0;
        bits = 0;
        //===//
        state.mode = OS;
        /* falls through */
      case OS:
        //=== NEEDBITS(16); */
        while (bits < 16) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        if (state.head) {
          state.head.xflags = (hold & 0xff);
          state.head.os = (hold >> 8);
        }
        if (state.flags & 0x0200) {
          //=== CRC2(state.check, hold);
          hbuf[0] = hold & 0xff;
          hbuf[1] = (hold >>> 8) & 0xff;
          state.check = crc32(state.check, hbuf, 2, 0);
          //===//
        }
        //=== INITBITS();
        hold = 0;
        bits = 0;
        //===//
        state.mode = EXLEN;
        /* falls through */
      case EXLEN:
        if (state.flags & 0x0400) {
          //=== NEEDBITS(16); */
          while (bits < 16) {
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
          }
          //===//
          state.length = hold;
          if (state.head) {
            state.head.extra_len = hold;
          }
          if (state.flags & 0x0200) {
            //=== CRC2(state.check, hold);
            hbuf[0] = hold & 0xff;
            hbuf[1] = (hold >>> 8) & 0xff;
            state.check = crc32(state.check, hbuf, 2, 0);
            //===//
          }
          //=== INITBITS();
          hold = 0;
          bits = 0;
          //===//
        }
        else if (state.head) {
          state.head.extra = null/*Z_NULL*/;
        }
        state.mode = EXTRA;
        /* falls through */
      case EXTRA:
        if (state.flags & 0x0400) {
          copy = state.length;
          if (copy > have) { copy = have; }
          if (copy) {
            if (state.head) {
              len = state.head.extra_len - state.length;
              if (!state.head.extra) {
                // Use untyped array for more convenient processing later
                state.head.extra = new Array(state.head.extra_len);
              }
              utils.arraySet(
                state.head.extra,
                input,
                next,
                // extra field is limited to 65536 bytes
                // - no need for additional size check
                copy,
                /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
                len
              );
              //zmemcpy(state.head.extra + len, next,
              //        len + copy > state.head.extra_max ?
              //        state.head.extra_max - len : copy);
            }
            if (state.flags & 0x0200) {
              state.check = crc32(state.check, input, copy, next);
            }
            have -= copy;
            next += copy;
            state.length -= copy;
          }
          if (state.length) { break inf_leave; }
        }
        state.length = 0;
        state.mode = NAME;
        /* falls through */
      case NAME:
        if (state.flags & 0x0800) {
          if (have === 0) { break inf_leave; }
          copy = 0;
          do {
            // TODO: 2 or 1 bytes?
            len = input[next + copy++];
            /* use constant limit because in js we should not preallocate memory */
            if (state.head && len &&
                (state.length < 65536 /*state.head.name_max*/)) {
              state.head.name += String.fromCharCode(len);
            }
          } while (len && copy < have);

          if (state.flags & 0x0200) {
            state.check = crc32(state.check, input, copy, next);
          }
          have -= copy;
          next += copy;
          if (len) { break inf_leave; }
        }
        else if (state.head) {
          state.head.name = null;
        }
        state.length = 0;
        state.mode = COMMENT;
        /* falls through */
      case COMMENT:
        if (state.flags & 0x1000) {
          if (have === 0) { break inf_leave; }
          copy = 0;
          do {
            len = input[next + copy++];
            /* use constant limit because in js we should not preallocate memory */
            if (state.head && len &&
                (state.length < 65536 /*state.head.comm_max*/)) {
              state.head.comment += String.fromCharCode(len);
            }
          } while (len && copy < have);
          if (state.flags & 0x0200) {
            state.check = crc32(state.check, input, copy, next);
          }
          have -= copy;
          next += copy;
          if (len) { break inf_leave; }
        }
        else if (state.head) {
          state.head.comment = null;
        }
        state.mode = HCRC;
        /* falls through */
      case HCRC:
        if (state.flags & 0x0200) {
          //=== NEEDBITS(16); */
          while (bits < 16) {
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
          }
          //===//
          if (hold !== (state.check & 0xffff)) {
            strm.msg = 'header crc mismatch';
            state.mode = BAD;
            break;
          }
          //=== INITBITS();
          hold = 0;
          bits = 0;
          //===//
        }
        if (state.head) {
          state.head.hcrc = ((state.flags >> 9) & 1);
          state.head.done = true;
        }
        strm.adler = state.check = 0;
        state.mode = TYPE;
        break;
      case DICTID:
        //=== NEEDBITS(32); */
        while (bits < 32) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        strm.adler = state.check = zswap32(hold);
        //=== INITBITS();
        hold = 0;
        bits = 0;
        //===//
        state.mode = DICT;
        /* falls through */
      case DICT:
        if (state.havedict === 0) {
          //--- RESTORE() ---
          strm.next_out = put;
          strm.avail_out = left;
          strm.next_in = next;
          strm.avail_in = have;
          state.hold = hold;
          state.bits = bits;
          //---
          return Z_NEED_DICT;
        }
        strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
        state.mode = TYPE;
        /* falls through */
      case TYPE:
        if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; }
        /* falls through */
      case TYPEDO:
        if (state.last) {
          //--- BYTEBITS() ---//
          hold >>>= bits & 7;
          bits -= bits & 7;
          //---//
          state.mode = CHECK;
          break;
        }
        //=== NEEDBITS(3); */
        while (bits < 3) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        state.last = (hold & 0x01)/*BITS(1)*/;
        //--- DROPBITS(1) ---//
        hold >>>= 1;
        bits -= 1;
        //---//

        switch ((hold & 0x03)/*BITS(2)*/) {
          case 0:                             /* stored block */
            //Tracev((stderr, "inflate:     stored block%s\n",
            //        state.last ? " (last)" : ""));
            state.mode = STORED;
            break;
          case 1:                             /* fixed block */
            fixedtables(state);
            //Tracev((stderr, "inflate:     fixed codes block%s\n",
            //        state.last ? " (last)" : ""));
            state.mode = LEN_;             /* decode codes */
            if (flush === Z_TREES) {
              //--- DROPBITS(2) ---//
              hold >>>= 2;
              bits -= 2;
              //---//
              break inf_leave;
            }
            break;
          case 2:                             /* dynamic block */
            //Tracev((stderr, "inflate:     dynamic codes block%s\n",
            //        state.last ? " (last)" : ""));
            state.mode = TABLE;
            break;
          case 3:
            strm.msg = 'invalid block type';
            state.mode = BAD;
        }
        //--- DROPBITS(2) ---//
        hold >>>= 2;
        bits -= 2;
        //---//
        break;
      case STORED:
        //--- BYTEBITS() ---// /* go to byte boundary */
        hold >>>= bits & 7;
        bits -= bits & 7;
        //---//
        //=== NEEDBITS(32); */
        while (bits < 32) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) {
          strm.msg = 'invalid stored block lengths';
          state.mode = BAD;
          break;
        }
        state.length = hold & 0xffff;
        //Tracev((stderr, "inflate:       stored length %u\n",
        //        state.length));
        //=== INITBITS();
        hold = 0;
        bits = 0;
        //===//
        state.mode = COPY_;
        if (flush === Z_TREES) { break inf_leave; }
        /* falls through */
      case COPY_:
        state.mode = COPY;
        /* falls through */
      case COPY:
        copy = state.length;
        if (copy) {
          if (copy > have) { copy = have; }
          if (copy > left) { copy = left; }
          if (copy === 0) { break inf_leave; }
          //--- zmemcpy(put, next, copy); ---
          utils.arraySet(output, input, next, copy, put);
          //---//
          have -= copy;
          next += copy;
          left -= copy;
          put += copy;
          state.length -= copy;
          break;
        }
        //Tracev((stderr, "inflate:       stored end\n"));
        state.mode = TYPE;
        break;
      case TABLE:
        //=== NEEDBITS(14); */
        while (bits < 14) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257;
        //--- DROPBITS(5) ---//
        hold >>>= 5;
        bits -= 5;
        //---//
        state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1;
        //--- DROPBITS(5) ---//
        hold >>>= 5;
        bits -= 5;
        //---//
        state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4;
        //--- DROPBITS(4) ---//
        hold >>>= 4;
        bits -= 4;
        //---//
//#ifndef PKZIP_BUG_WORKAROUND
        if (state.nlen > 286 || state.ndist > 30) {
          strm.msg = 'too many length or distance symbols';
          state.mode = BAD;
          break;
        }
//#endif
        //Tracev((stderr, "inflate:       table sizes ok\n"));
        state.have = 0;
        state.mode = LENLENS;
        /* falls through */
      case LENLENS:
        while (state.have < state.ncode) {
          //=== NEEDBITS(3);
          while (bits < 3) {
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
          }
          //===//
          state.lens[order[state.have++]] = (hold & 0x07);//BITS(3);
          //--- DROPBITS(3) ---//
          hold >>>= 3;
          bits -= 3;
          //---//
        }
        while (state.have < 19) {
          state.lens[order[state.have++]] = 0;
        }
        // We have separate tables & no pointers. 2 commented lines below not needed.
        //state.next = state.codes;
        //state.lencode = state.next;
        // Switch to use dynamic table
        state.lencode = state.lendyn;
        state.lenbits = 7;

        opts = { bits: state.lenbits };
        ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);
        state.lenbits = opts.bits;

        if (ret) {
          strm.msg = 'invalid code lengths set';
          state.mode = BAD;
          break;
        }
        //Tracev((stderr, "inflate:       code lengths ok\n"));
        state.have = 0;
        state.mode = CODELENS;
        /* falls through */
      case CODELENS:
        while (state.have < state.nlen + state.ndist) {
          for (;;) {
            here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/
            here_bits = here >>> 24;
            here_op = (here >>> 16) & 0xff;
            here_val = here & 0xffff;

            if ((here_bits) <= bits) { break; }
            //--- PULLBYTE() ---//
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
            //---//
          }
          if (here_val < 16) {
            //--- DROPBITS(here.bits) ---//
            hold >>>= here_bits;
            bits -= here_bits;
            //---//
            state.lens[state.have++] = here_val;
          }
          else {
            if (here_val === 16) {
              //=== NEEDBITS(here.bits + 2);
              n = here_bits + 2;
              while (bits < n) {
                if (have === 0) { break inf_leave; }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              //===//
              //--- DROPBITS(here.bits) ---//
              hold >>>= here_bits;
              bits -= here_bits;
              //---//
              if (state.have === 0) {
                strm.msg = 'invalid bit length repeat';
                state.mode = BAD;
                break;
              }
              len = state.lens[state.have - 1];
              copy = 3 + (hold & 0x03);//BITS(2);
              //--- DROPBITS(2) ---//
              hold >>>= 2;
              bits -= 2;
              //---//
            }
            else if (here_val === 17) {
              //=== NEEDBITS(here.bits + 3);
              n = here_bits + 3;
              while (bits < n) {
                if (have === 0) { break inf_leave; }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              //===//
              //--- DROPBITS(here.bits) ---//
              hold >>>= here_bits;
              bits -= here_bits;
              //---//
              len = 0;
              copy = 3 + (hold & 0x07);//BITS(3);
              //--- DROPBITS(3) ---//
              hold >>>= 3;
              bits -= 3;
              //---//
            }
            else {
              //=== NEEDBITS(here.bits + 7);
              n = here_bits + 7;
              while (bits < n) {
                if (have === 0) { break inf_leave; }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              //===//
              //--- DROPBITS(here.bits) ---//
              hold >>>= here_bits;
              bits -= here_bits;
              //---//
              len = 0;
              copy = 11 + (hold & 0x7f);//BITS(7);
              //--- DROPBITS(7) ---//
              hold >>>= 7;
              bits -= 7;
              //---//
            }
            if (state.have + copy > state.nlen + state.ndist) {
              strm.msg = 'invalid bit length repeat';
              state.mode = BAD;
              break;
            }
            while (copy--) {
              state.lens[state.have++] = len;
            }
          }
        }

        /* handle error breaks in while */
        if (state.mode === BAD) { break; }

        /* check for end-of-block code (better have one) */
        if (state.lens[256] === 0) {
          strm.msg = 'invalid code -- missing end-of-block';
          state.mode = BAD;
          break;
        }

        /* build code tables -- note: do not change the lenbits or distbits
           values here (9 and 6) without reading the comments in inftrees.h
           concerning the ENOUGH constants, which depend on those values */
        state.lenbits = 9;

        opts = { bits: state.lenbits };
        ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);
        // We have separate tables & no pointers. 2 commented lines below not needed.
        // state.next_index = opts.table_index;
        state.lenbits = opts.bits;
        // state.lencode = state.next;

        if (ret) {
          strm.msg = 'invalid literal/lengths set';
          state.mode = BAD;
          break;
        }

        state.distbits = 6;
        //state.distcode.copy(state.codes);
        // Switch to use dynamic table
        state.distcode = state.distdyn;
        opts = { bits: state.distbits };
        ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);
        // We have separate tables & no pointers. 2 commented lines below not needed.
        // state.next_index = opts.table_index;
        state.distbits = opts.bits;
        // state.distcode = state.next;

        if (ret) {
          strm.msg = 'invalid distances set';
          state.mode = BAD;
          break;
        }
        //Tracev((stderr, 'inflate:       codes ok\n'));
        state.mode = LEN_;
        if (flush === Z_TREES) { break inf_leave; }
        /* falls through */
      case LEN_:
        state.mode = LEN;
        /* falls through */
      case LEN:
        if (have >= 6 && left >= 258) {
          //--- RESTORE() ---
          strm.next_out = put;
          strm.avail_out = left;
          strm.next_in = next;
          strm.avail_in = have;
          state.hold = hold;
          state.bits = bits;
          //---
          inflate_fast(strm, _out);
          //--- LOAD() ---
          put = strm.next_out;
          output = strm.output;
          left = strm.avail_out;
          next = strm.next_in;
          input = strm.input;
          have = strm.avail_in;
          hold = state.hold;
          bits = state.bits;
          //---

          if (state.mode === TYPE) {
            state.back = -1;
          }
          break;
        }
        state.back = 0;
        for (;;) {
          here = state.lencode[hold & ((1 << state.lenbits) - 1)];  /*BITS(state.lenbits)*/
          here_bits = here >>> 24;
          here_op = (here >>> 16) & 0xff;
          here_val = here & 0xffff;

          if (here_bits <= bits) { break; }
          //--- PULLBYTE() ---//
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
          //---//
        }
        if (here_op && (here_op & 0xf0) === 0) {
          last_bits = here_bits;
          last_op = here_op;
          last_val = here_val;
          for (;;) {
            here = state.lencode[last_val +
                    ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)];
            here_bits = here >>> 24;
            here_op = (here >>> 16) & 0xff;
            here_val = here & 0xffff;

            if ((last_bits + here_bits) <= bits) { break; }
            //--- PULLBYTE() ---//
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
            //---//
          }
          //--- DROPBITS(last.bits) ---//
          hold >>>= last_bits;
          bits -= last_bits;
          //---//
          state.back += last_bits;
        }
        //--- DROPBITS(here.bits) ---//
        hold >>>= here_bits;
        bits -= here_bits;
        //---//
        state.back += here_bits;
        state.length = here_val;
        if (here_op === 0) {
          //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
          //        "inflate:         literal '%c'\n" :
          //        "inflate:         literal 0x%02x\n", here.val));
          state.mode = LIT;
          break;
        }
        if (here_op & 32) {
          //Tracevv((stderr, "inflate:         end of block\n"));
          state.back = -1;
          state.mode = TYPE;
          break;
        }
        if (here_op & 64) {
          strm.msg = 'invalid literal/length code';
          state.mode = BAD;
          break;
        }
        state.extra = here_op & 15;
        state.mode = LENEXT;
        /* falls through */
      case LENEXT:
        if (state.extra) {
          //=== NEEDBITS(state.extra);
          n = state.extra;
          while (bits < n) {
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
          }
          //===//
          state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/;
          //--- DROPBITS(state.extra) ---//
          hold >>>= state.extra;
          bits -= state.extra;
          //---//
          state.back += state.extra;
        }
        //Tracevv((stderr, "inflate:         length %u\n", state.length));
        state.was = state.length;
        state.mode = DIST;
        /* falls through */
      case DIST:
        for (;;) {
          here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/
          here_bits = here >>> 24;
          here_op = (here >>> 16) & 0xff;
          here_val = here & 0xffff;

          if ((here_bits) <= bits) { break; }
          //--- PULLBYTE() ---//
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
          //---//
        }
        if ((here_op & 0xf0) === 0) {
          last_bits = here_bits;
          last_op = here_op;
          last_val = here_val;
          for (;;) {
            here = state.distcode[last_val +
                    ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)];
            here_bits = here >>> 24;
            here_op = (here >>> 16) & 0xff;
            here_val = here & 0xffff;

            if ((last_bits + here_bits) <= bits) { break; }
            //--- PULLBYTE() ---//
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
            //---//
          }
          //--- DROPBITS(last.bits) ---//
          hold >>>= last_bits;
          bits -= last_bits;
          //---//
          state.back += last_bits;
        }
        //--- DROPBITS(here.bits) ---//
        hold >>>= here_bits;
        bits -= here_bits;
        //---//
        state.back += here_bits;
        if (here_op & 64) {
          strm.msg = 'invalid distance code';
          state.mode = BAD;
          break;
        }
        state.offset = here_val;
        state.extra = (here_op) & 15;
        state.mode = DISTEXT;
        /* falls through */
      case DISTEXT:
        if (state.extra) {
          //=== NEEDBITS(state.extra);
          n = state.extra;
          while (bits < n) {
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
          }
          //===//
          state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/;
          //--- DROPBITS(state.extra) ---//
          hold >>>= state.extra;
          bits -= state.extra;
          //---//
          state.back += state.extra;
        }
//#ifdef INFLATE_STRICT
        if (state.offset > state.dmax) {
          strm.msg = 'invalid distance too far back';
          state.mode = BAD;
          break;
        }
//#endif
        //Tracevv((stderr, "inflate:         distance %u\n", state.offset));
        state.mode = MATCH;
        /* falls through */
      case MATCH:
        if (left === 0) { break inf_leave; }
        copy = _out - left;
        if (state.offset > copy) {         /* copy from window */
          copy = state.offset - copy;
          if (copy > state.whave) {
            if (state.sane) {
              strm.msg = 'invalid distance too far back';
              state.mode = BAD;
              break;
            }
// (!) This block is disabled in zlib defaults,
// don't enable it for binary compatibility
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
//          Trace((stderr, "inflate.c too far\n"));
//          copy -= state.whave;
//          if (copy > state.length) { copy = state.length; }
//          if (copy > left) { copy = left; }
//          left -= copy;
//          state.length -= copy;
//          do {
//            output[put++] = 0;
//          } while (--copy);
//          if (state.length === 0) { state.mode = LEN; }
//          break;
//#endif
          }
          if (copy > state.wnext) {
            copy -= state.wnext;
            from = state.wsize - copy;
          }
          else {
            from = state.wnext - copy;
          }
          if (copy > state.length) { copy = state.length; }
          from_source = state.window;
        }
        else {                              /* copy from output */
          from_source = output;
          from = put - state.offset;
          copy = state.length;
        }
        if (copy > left) { copy = left; }
        left -= copy;
        state.length -= copy;
        do {
          output[put++] = from_source[from++];
        } while (--copy);
        if (state.length === 0) { state.mode = LEN; }
        break;
      case LIT:
        if (left === 0) { break inf_leave; }
        output[put++] = state.length;
        left--;
        state.mode = LEN;
        break;
      case CHECK:
        if (state.wrap) {
          //=== NEEDBITS(32);
          while (bits < 32) {
            if (have === 0) { break inf_leave; }
            have--;
            // Use '|' instead of '+' to make sure that result is signed
            hold |= input[next++] << bits;
            bits += 8;
          }
          //===//
          _out -= left;
          strm.total_out += _out;
          state.total += _out;
          if (_out) {
            strm.adler = state.check =
                /*UPDATE(state.check, put - _out, _out);*/
                (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out));

          }
          _out = left;
          // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too
          if ((state.flags ? hold : zswap32(hold)) !== state.check) {
            strm.msg = 'incorrect data check';
            state.mode = BAD;
            break;
          }
          //=== INITBITS();
          hold = 0;
          bits = 0;
          //===//
          //Tracev((stderr, "inflate:   check matches trailer\n"));
        }
        state.mode = LENGTH;
        /* falls through */
      case LENGTH:
        if (state.wrap && state.flags) {
          //=== NEEDBITS(32);
          while (bits < 32) {
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
          }
          //===//
          if (hold !== (state.total & 0xffffffff)) {
            strm.msg = 'incorrect length check';
            state.mode = BAD;
            break;
          }
          //=== INITBITS();
          hold = 0;
          bits = 0;
          //===//
          //Tracev((stderr, "inflate:   length matches trailer\n"));
        }
        state.mode = DONE;
        /* falls through */
      case DONE:
        ret = Z_STREAM_END;
        break inf_leave;
      case BAD:
        ret = Z_DATA_ERROR;
        break inf_leave;
      case MEM:
        return Z_MEM_ERROR;
      case SYNC:
        /* falls through */
      default:
        return Z_STREAM_ERROR;
    }
  }

  // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave"

  /*
     Return from inflate(), updating the total counts and the check value.
     If there was no progress during the inflate() call, return a buffer
     error.  Call updatewindow() to create and/or update the window state.
     Note: a memory error from inflate() is non-recoverable.
   */

  //--- RESTORE() ---
  strm.next_out = put;
  strm.avail_out = left;
  strm.next_in = next;
  strm.avail_in = have;
  state.hold = hold;
  state.bits = bits;
  //---

  if (state.wsize || (_out !== strm.avail_out && state.mode < BAD &&
                      (state.mode < CHECK || flush !== Z_FINISH))) {
    if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) {
      state.mode = MEM;
      return Z_MEM_ERROR;
    }
  }
  _in -= strm.avail_in;
  _out -= strm.avail_out;
  strm.total_in += _in;
  strm.total_out += _out;
  state.total += _out;
  if (state.wrap && _out) {
    strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/
      (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out));
  }
  strm.data_type = state.bits + (state.last ? 64 : 0) +
                    (state.mode === TYPE ? 128 : 0) +
                    (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);
  if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) {
    ret = Z_BUF_ERROR;
  }
  return ret;
}

function inflateEnd(strm) {

  if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) {
    return Z_STREAM_ERROR;
  }

  var state = strm.state;
  if (state.window) {
    state.window = null;
  }
  strm.state = null;
  return Z_OK;
}

function inflateGetHeader(strm, head) {
  var state;

  /* check state */
  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
  state = strm.state;
  if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; }

  /* save header structure */
  state.head = head;
  head.done = false;
  return Z_OK;
}

function inflateSetDictionary(strm, dictionary) {
  var dictLength = dictionary.length;

  var state;
  var dictid;
  var ret;

  /* check state */
  if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR; }
  state = strm.state;

  if (state.wrap !== 0 && state.mode !== DICT) {
    return Z_STREAM_ERROR;
  }

  /* check for correct dictionary identifier */
  if (state.mode === DICT) {
    dictid = 1; /* adler32(0, null, 0)*/
    /* dictid = adler32(dictid, dictionary, dictLength); */
    dictid = adler32(dictid, dictionary, dictLength, 0);
    if (dictid !== state.check) {
      return Z_DATA_ERROR;
    }
  }
  /* copy dictionary to window using updatewindow(), which will amend the
   existing dictionary if appropriate */
  ret = updatewindow(strm, dictionary, dictLength, dictLength);
  if (ret) {
    state.mode = MEM;
    return Z_MEM_ERROR;
  }
  state.havedict = 1;
  // Tracev((stderr, "inflate:   dictionary set\n"));
  return Z_OK;
}

exports.inflateReset = inflateReset;
exports.inflateReset2 = inflateReset2;
exports.inflateResetKeep = inflateResetKeep;
exports.inflateInit = inflateInit;
exports.inflateInit2 = inflateInit2;
exports.inflate = inflate;
exports.inflateEnd = inflateEnd;
exports.inflateGetHeader = inflateGetHeader;
exports.inflateSetDictionary = inflateSetDictionary;
exports.inflateInfo = 'pako inflate (from Nodeca project)';

/* Not implemented
exports.inflateCopy = inflateCopy;
exports.inflateGetDictionary = inflateGetDictionary;
exports.inflateMark = inflateMark;
exports.inflatePrime = inflatePrime;
exports.inflateSync = inflateSync;
exports.inflateSyncPoint = inflateSyncPoint;
exports.inflateUndermine = inflateUndermine;
*/

},{"../utils/common":179,"./adler32":180,"./crc32":182,"./inffast":184,"./inftrees":186}],186:[function(require,module,exports){
'use strict';

// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

var utils = require('../utils/common');

var MAXBITS = 15;
var ENOUGH_LENS = 852;
var ENOUGH_DISTS = 592;
//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);

var CODES = 0;
var LENS = 1;
var DISTS = 2;

var lbase = [ /* Length codes 257..285 base */
  3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
  35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
];

var lext = [ /* Length codes 257..285 extra */
  16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
  19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78
];

var dbase = [ /* Distance codes 0..29 base */
  1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
  257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
  8193, 12289, 16385, 24577, 0, 0
];

var dext = [ /* Distance codes 0..29 extra */
  16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
  23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
  28, 28, 29, 29, 64, 64
];

module.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts)
{
  var bits = opts.bits;
      //here = opts.here; /* table entry for duplication */

  var len = 0;               /* a code's length in bits */
  var sym = 0;               /* index of code symbols */
  var min = 0, max = 0;          /* minimum and maximum code lengths */
  var root = 0;              /* number of index bits for root table */
  var curr = 0;              /* number of index bits for current table */
  var drop = 0;              /* code bits to drop for sub-table */
  var left = 0;                   /* number of prefix codes available */
  var used = 0;              /* code entries in table used */
  var huff = 0;              /* Huffman code */
  var incr;              /* for incrementing code, index */
  var fill;              /* index for replicating entries */
  var low;               /* low bits for current root entry */
  var mask;              /* mask for low root bits */
  var next;             /* next available space in table */
  var base = null;     /* base value table to use */
  var base_index = 0;
//  var shoextra;    /* extra bits table to use */
  var end;                    /* use base and extra for symbol > end */
  var count = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1];    /* number of codes of each length */
  var offs = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1];     /* offsets in table for each length */
  var extra = null;
  var extra_index = 0;

  var here_bits, here_op, here_val;

  /*
   Process a set of code lengths to create a canonical Huffman code.  The
   code lengths are lens[0..codes-1].  Each length corresponds to the
   symbols 0..codes-1.  The Huffman code is generated by first sorting the
   symbols by length from short to long, and retaining the symbol order
   for codes with equal lengths.  Then the code starts with all zero bits
   for the first code of the shortest length, and the codes are integer
   increments for the same length, and zeros are appended as the length
   increases.  For the deflate format, these bits are stored backwards
   from their more natural integer increment ordering, and so when the
   decoding tables are built in the large loop below, the integer codes
   are incremented backwards.

   This routine assumes, but does not check, that all of the entries in
   lens[] are in the range 0..MAXBITS.  The caller must assure this.
   1..MAXBITS is interpreted as that code length.  zero means that that
   symbol does not occur in this code.

   The codes are sorted by computing a count of codes for each length,
   creating from that a table of starting indices for each length in the
   sorted table, and then entering the symbols in order in the sorted
   table.  The sorted table is work[], with that space being provided by
   the caller.

   The length counts are used for other purposes as well, i.e. finding
   the minimum and maximum length codes, determining if there are any
   codes at all, checking for a valid set of lengths, and looking ahead
   at length counts to determine sub-table sizes when building the
   decoding tables.
   */

  /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
  for (len = 0; len <= MAXBITS; len++) {
    count[len] = 0;
  }
  for (sym = 0; sym < codes; sym++) {
    count[lens[lens_index + sym]]++;
  }

  /* bound code lengths, force root to be within code lengths */
  root = bits;
  for (max = MAXBITS; max >= 1; max--) {
    if (count[max] !== 0) { break; }
  }
  if (root > max) {
    root = max;
  }
  if (max === 0) {                     /* no symbols to code at all */
    //table.op[opts.table_index] = 64;  //here.op = (var char)64;    /* invalid code marker */
    //table.bits[opts.table_index] = 1;   //here.bits = (var char)1;
    //table.val[opts.table_index++] = 0;   //here.val = (var short)0;
    table[table_index++] = (1 << 24) | (64 << 16) | 0;


    //table.op[opts.table_index] = 64;
    //table.bits[opts.table_index] = 1;
    //table.val[opts.table_index++] = 0;
    table[table_index++] = (1 << 24) | (64 << 16) | 0;

    opts.bits = 1;
    return 0;     /* no symbols, but wait for decoding to report error */
  }
  for (min = 1; min < max; min++) {
    if (count[min] !== 0) { break; }
  }
  if (root < min) {
    root = min;
  }

  /* check for an over-subscribed or incomplete set of lengths */
  left = 1;
  for (len = 1; len <= MAXBITS; len++) {
    left <<= 1;
    left -= count[len];
    if (left < 0) {
      return -1;
    }        /* over-subscribed */
  }
  if (left > 0 && (type === CODES || max !== 1)) {
    return -1;                      /* incomplete set */
  }

  /* generate offsets into symbol table for each length for sorting */
  offs[1] = 0;
  for (len = 1; len < MAXBITS; len++) {
    offs[len + 1] = offs[len] + count[len];
  }

  /* sort symbols by length, by symbol order within each length */
  for (sym = 0; sym < codes; sym++) {
    if (lens[lens_index + sym] !== 0) {
      work[offs[lens[lens_index + sym]]++] = sym;
    }
  }

  /*
   Create and fill in decoding tables.  In this loop, the table being
   filled is at next and has curr index bits.  The code being used is huff
   with length len.  That code is converted to an index by dropping drop
   bits off of the bottom.  For codes where len is less than drop + curr,
   those top drop + curr - len bits are incremented through all values to
   fill the table with replicated entries.

   root is the number of index bits for the root table.  When len exceeds
   root, sub-tables are created pointed to by the root entry with an index
   of the low root bits of huff.  This is saved in low to check for when a
   new sub-table should be started.  drop is zero when the root table is
   being filled, and drop is root when sub-tables are being filled.

   When a new sub-table is needed, it is necessary to look ahead in the
   code lengths to determine what size sub-table is needed.  The length
   counts are used for this, and so count[] is decremented as codes are
   entered in the tables.

   used keeps track of how many table entries have been allocated from the
   provided *table space.  It is checked for LENS and DIST tables against
   the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
   the initial root table size constants.  See the comments in inftrees.h
   for more information.

   sym increments through all symbols, and the loop terminates when
   all codes of length max, i.e. all codes, have been processed.  This
   routine permits incomplete codes, so another loop after this one fills
   in the rest of the decoding tables with invalid code markers.
   */

  /* set up for code type */
  // poor man optimization - use if-else instead of switch,
  // to avoid deopts in old v8
  if (type === CODES) {
    base = extra = work;    /* dummy value--not used */
    end = 19;

  } else if (type === LENS) {
    base = lbase;
    base_index -= 257;
    extra = lext;
    extra_index -= 257;
    end = 256;

  } else {                    /* DISTS */
    base = dbase;
    extra = dext;
    end = -1;
  }

  /* initialize opts for loop */
  huff = 0;                   /* starting code */
  sym = 0;                    /* starting code symbol */
  len = min;                  /* starting code length */
  next = table_index;              /* current table to fill in */
  curr = root;                /* current table index bits */
  drop = 0;                   /* current bits to drop from code for index */
  low = -1;                   /* trigger new sub-table when len > root */
  used = 1 << root;          /* use root table entries */
  mask = used - 1;            /* mask for comparing low */

  /* check available table space */
  if ((type === LENS && used > ENOUGH_LENS) ||
    (type === DISTS && used > ENOUGH_DISTS)) {
    return 1;
  }

  /* process all codes and make table entries */
  for (;;) {
    /* create table entry */
    here_bits = len - drop;
    if (work[sym] < end) {
      here_op = 0;
      here_val = work[sym];
    }
    else if (work[sym] > end) {
      here_op = extra[extra_index + work[sym]];
      here_val = base[base_index + work[sym]];
    }
    else {
      here_op = 32 + 64;         /* end of block */
      here_val = 0;
    }

    /* replicate for those indices with low len bits equal to huff */
    incr = 1 << (len - drop);
    fill = 1 << curr;
    min = fill;                 /* save offset to next table */
    do {
      fill -= incr;
      table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0;
    } while (fill !== 0);

    /* backwards increment the len-bit code huff */
    incr = 1 << (len - 1);
    while (huff & incr) {
      incr >>= 1;
    }
    if (incr !== 0) {
      huff &= incr - 1;
      huff += incr;
    } else {
      huff = 0;
    }

    /* go to next symbol, update count, len */
    sym++;
    if (--count[len] === 0) {
      if (len === max) { break; }
      len = lens[lens_index + work[sym]];
    }

    /* create new sub-table if needed */
    if (len > root && (huff & mask) !== low) {
      /* if first time, transition to sub-tables */
      if (drop === 0) {
        drop = root;
      }

      /* increment past last table */
      next += min;            /* here min is 1 << curr */

      /* determine length of next table */
      curr = len - drop;
      left = 1 << curr;
      while (curr + drop < max) {
        left -= count[curr + drop];
        if (left <= 0) { break; }
        curr++;
        left <<= 1;
      }

      /* check for enough space */
      used += 1 << curr;
      if ((type === LENS && used > ENOUGH_LENS) ||
        (type === DISTS && used > ENOUGH_DISTS)) {
        return 1;
      }

      /* point entry in root table to sub-table */
      low = huff & mask;
      /*table.op[low] = curr;
      table.bits[low] = root;
      table.val[low] = next - opts.table_index;*/
      table[low] = (root << 24) | (curr << 16) | (next - table_index) |0;
    }
  }

  /* fill in remaining table entry if code is incomplete (guaranteed to have
   at most one remaining entry, since if the code is incomplete, the
   maximum code length that was allowed to get this far is one bit) */
  if (huff !== 0) {
    //table.op[next + huff] = 64;            /* invalid code marker */
    //table.bits[next + huff] = len - drop;
    //table.val[next + huff] = 0;
    table[next + huff] = ((len - drop) << 24) | (64 << 16) |0;
  }

  /* set return parameters */
  //opts.table_index += used;
  opts.bits = root;
  return 0;
};

},{"../utils/common":179}],187:[function(require,module,exports){
'use strict';

// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

module.exports = {
  2:      'need dictionary',     /* Z_NEED_DICT       2  */
  1:      'stream end',          /* Z_STREAM_END      1  */
  0:      '',                    /* Z_OK              0  */
  '-1':   'file error',          /* Z_ERRNO         (-1) */
  '-2':   'stream error',        /* Z_STREAM_ERROR  (-2) */
  '-3':   'data error',          /* Z_DATA_ERROR    (-3) */
  '-4':   'insufficient memory', /* Z_MEM_ERROR     (-4) */
  '-5':   'buffer error',        /* Z_BUF_ERROR     (-5) */
  '-6':   'incompatible version' /* Z_VERSION_ERROR (-6) */
};

},{}],188:[function(require,module,exports){
'use strict';

// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

/* eslint-disable space-unary-ops */

var utils = require('../utils/common');

/* Public constants ==========================================================*/
/* ===========================================================================*/


//var Z_FILTERED          = 1;
//var Z_HUFFMAN_ONLY      = 2;
//var Z_RLE               = 3;
var Z_FIXED               = 4;
//var Z_DEFAULT_STRATEGY  = 0;

/* Possible values of the data_type field (though see inflate()) */
var Z_BINARY              = 0;
var Z_TEXT                = 1;
//var Z_ASCII             = 1; // = Z_TEXT
var Z_UNKNOWN             = 2;

/*============================================================================*/


function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }

// From zutil.h

var STORED_BLOCK = 0;
var STATIC_TREES = 1;
var DYN_TREES    = 2;
/* The three kinds of block type */

var MIN_MATCH    = 3;
var MAX_MATCH    = 258;
/* The minimum and maximum match lengths */

// From deflate.h
/* ===========================================================================
 * Internal compression state.
 */

var LENGTH_CODES  = 29;
/* number of length codes, not counting the special END_BLOCK code */

var LITERALS      = 256;
/* number of literal bytes 0..255 */

var L_CODES       = LITERALS + 1 + LENGTH_CODES;
/* number of Literal or Length codes, including the END_BLOCK code */

var D_CODES       = 30;
/* number of distance codes */

var BL_CODES      = 19;
/* number of codes used to transfer the bit lengths */

var HEAP_SIZE     = 2 * L_CODES + 1;
/* maximum heap size */

var MAX_BITS      = 15;
/* All codes must not exceed MAX_BITS bits */

var Buf_size      = 16;
/* size of bit buffer in bi_buf */


/* ===========================================================================
 * Constants
 */

var MAX_BL_BITS = 7;
/* Bit length codes must not exceed MAX_BL_BITS bits */

var END_BLOCK   = 256;
/* end of block literal code */

var REP_3_6     = 16;
/* repeat previous bit length 3-6 times (2 bits of repeat count) */

var REPZ_3_10   = 17;
/* repeat a zero length 3-10 times  (3 bits of repeat count) */

var REPZ_11_138 = 18;
/* repeat a zero length 11-138 times  (7 bits of repeat count) */

/* eslint-disable comma-spacing,array-bracket-spacing */
var extra_lbits =   /* extra bits for each length code */
  [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];

var extra_dbits =   /* extra bits for each distance code */
  [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];

var extra_blbits =  /* extra bits for each bit length code */
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7];

var bl_order =
  [16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];
/* eslint-enable comma-spacing,array-bracket-spacing */

/* The lengths of the bit length codes are sent in order of decreasing
 * probability, to avoid transmitting the lengths for unused bit length codes.
 */

/* ===========================================================================
 * Local data. These are initialized only once.
 */

// We pre-fill arrays with 0 to avoid uninitialized gaps

var DIST_CODE_LEN = 512; /* see definition of array dist_code below */

// !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1
var static_ltree  = new Array((L_CODES + 2) * 2);
zero(static_ltree);
/* The static literal tree. Since the bit lengths are imposed, there is no
 * need for the L_CODES extra codes used during heap construction. However
 * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
 * below).
 */

var static_dtree  = new Array(D_CODES * 2);
zero(static_dtree);
/* The static distance tree. (Actually a trivial tree since all codes use
 * 5 bits.)
 */

var _dist_code    = new Array(DIST_CODE_LEN);
zero(_dist_code);
/* Distance codes. The first 256 values correspond to the distances
 * 3 .. 258, the last 256 values correspond to the top 8 bits of
 * the 15 bit distances.
 */

var _length_code  = new Array(MAX_MATCH - MIN_MATCH + 1);
zero(_length_code);
/* length code for each normalized match length (0 == MIN_MATCH) */

var base_length   = new Array(LENGTH_CODES);
zero(base_length);
/* First normalized length for each code (0 = MIN_MATCH) */

var base_dist     = new Array(D_CODES);
zero(base_dist);
/* First normalized distance for each code (0 = distance of 1) */


function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) {

  this.static_tree  = static_tree;  /* static tree or NULL */
  this.extra_bits   = extra_bits;   /* extra bits for each code or NULL */
  this.extra_base   = extra_base;   /* base index for extra_bits */
  this.elems        = elems;        /* max number of elements in the tree */
  this.max_length   = max_length;   /* max bit length for the codes */

  // show if `static_tree` has data or dummy - needed for monomorphic objects
  this.has_stree    = static_tree && static_tree.length;
}


var static_l_desc;
var static_d_desc;
var static_bl_desc;


function TreeDesc(dyn_tree, stat_desc) {
  this.dyn_tree = dyn_tree;     /* the dynamic tree */
  this.max_code = 0;            /* largest code with non zero frequency */
  this.stat_desc = stat_desc;   /* the corresponding static tree */
}



function d_code(dist) {
  return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];
}


/* ===========================================================================
 * Output a short LSB first on the stream.
 * IN assertion: there is enough room in pendingBuf.
 */
function put_short(s, w) {
//    put_byte(s, (uch)((w) & 0xff));
//    put_byte(s, (uch)((ush)(w) >> 8));
  s.pending_buf[s.pending++] = (w) & 0xff;
  s.pending_buf[s.pending++] = (w >>> 8) & 0xff;
}


/* ===========================================================================
 * Send a value on a given number of bits.
 * IN assertion: length <= 16 and value fits in length bits.
 */
function send_bits(s, value, length) {
  if (s.bi_valid > (Buf_size - length)) {
    s.bi_buf |= (value << s.bi_valid) & 0xffff;
    put_short(s, s.bi_buf);
    s.bi_buf = value >> (Buf_size - s.bi_valid);
    s.bi_valid += length - Buf_size;
  } else {
    s.bi_buf |= (value << s.bi_valid) & 0xffff;
    s.bi_valid += length;
  }
}


function send_code(s, c, tree) {
  send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/);
}


/* ===========================================================================
 * Reverse the first len bits of a code, using straightforward code (a faster
 * method would use a table)
 * IN assertion: 1 <= len <= 15
 */
function bi_reverse(code, len) {
  var res = 0;
  do {
    res |= code & 1;
    code >>>= 1;
    res <<= 1;
  } while (--len > 0);
  return res >>> 1;
}


/* ===========================================================================
 * Flush the bit buffer, keeping at most 7 bits in it.
 */
function bi_flush(s) {
  if (s.bi_valid === 16) {
    put_short(s, s.bi_buf);
    s.bi_buf = 0;
    s.bi_valid = 0;

  } else if (s.bi_valid >= 8) {
    s.pending_buf[s.pending++] = s.bi_buf & 0xff;
    s.bi_buf >>= 8;
    s.bi_valid -= 8;
  }
}


/* ===========================================================================
 * Compute the optimal bit lengths for a tree and update the total bit length
 * for the current block.
 * IN assertion: the fields freq and dad are set, heap[heap_max] and
 *    above are the tree nodes sorted by increasing frequency.
 * OUT assertions: the field len is set to the optimal bit length, the
 *     array bl_count contains the frequencies for each bit length.
 *     The length opt_len is updated; static_len is also updated if stree is
 *     not null.
 */
function gen_bitlen(s, desc)
//    deflate_state *s;
//    tree_desc *desc;    /* the tree descriptor */
{
  var tree            = desc.dyn_tree;
  var max_code        = desc.max_code;
  var stree           = desc.stat_desc.static_tree;
  var has_stree       = desc.stat_desc.has_stree;
  var extra           = desc.stat_desc.extra_bits;
  var base            = desc.stat_desc.extra_base;
  var max_length      = desc.stat_desc.max_length;
  var h;              /* heap index */
  var n, m;           /* iterate over the tree elements */
  var bits;           /* bit length */
  var xbits;          /* extra bits */
  var f;              /* frequency */
  var overflow = 0;   /* number of elements with bit length too large */

  for (bits = 0; bits <= MAX_BITS; bits++) {
    s.bl_count[bits] = 0;
  }

  /* In a first pass, compute the optimal bit lengths (which may
   * overflow in the case of the bit length tree).
   */
  tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */

  for (h = s.heap_max + 1; h < HEAP_SIZE; h++) {
    n = s.heap[h];
    bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1;
    if (bits > max_length) {
      bits = max_length;
      overflow++;
    }
    tree[n * 2 + 1]/*.Len*/ = bits;
    /* We overwrite tree[n].Dad which is no longer needed */

    if (n > max_code) { continue; } /* not a leaf node */

    s.bl_count[bits]++;
    xbits = 0;
    if (n >= base) {
      xbits = extra[n - base];
    }
    f = tree[n * 2]/*.Freq*/;
    s.opt_len += f * (bits + xbits);
    if (has_stree) {
      s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits);
    }
  }
  if (overflow === 0) { return; }

  // Trace((stderr,"\nbit length overflow\n"));
  /* This happens for example on obj2 and pic of the Calgary corpus */

  /* Find the first bit length which could increase: */
  do {
    bits = max_length - 1;
    while (s.bl_count[bits] === 0) { bits--; }
    s.bl_count[bits]--;      /* move one leaf down the tree */
    s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */
    s.bl_count[max_length]--;
    /* The brother of the overflow item also moves one step up,
     * but this does not affect bl_count[max_length]
     */
    overflow -= 2;
  } while (overflow > 0);

  /* Now recompute all bit lengths, scanning in increasing frequency.
   * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
   * lengths instead of fixing only the wrong ones. This idea is taken
   * from 'ar' written by Haruhiko Okumura.)
   */
  for (bits = max_length; bits !== 0; bits--) {
    n = s.bl_count[bits];
    while (n !== 0) {
      m = s.heap[--h];
      if (m > max_code) { continue; }
      if (tree[m * 2 + 1]/*.Len*/ !== bits) {
        // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
        s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/;
        tree[m * 2 + 1]/*.Len*/ = bits;
      }
      n--;
    }
  }
}


/* ===========================================================================
 * Generate the codes for a given tree and bit counts (which need not be
 * optimal).
 * IN assertion: the array bl_count contains the bit length statistics for
 * the given tree and the field len is set for all tree elements.
 * OUT assertion: the field code is set for all tree elements of non
 *     zero code length.
 */
function gen_codes(tree, max_code, bl_count)
//    ct_data *tree;             /* the tree to decorate */
//    int max_code;              /* largest code with non zero frequency */
//    ushf *bl_count;            /* number of codes at each bit length */
{
  var next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */
  var code = 0;              /* running code value */
  var bits;                  /* bit index */
  var n;                     /* code index */

  /* The distribution counts are first used to generate the code values
   * without bit reversal.
   */
  for (bits = 1; bits <= MAX_BITS; bits++) {
    next_code[bits] = code = (code + bl_count[bits - 1]) << 1;
  }
  /* Check that the bit counts in bl_count are consistent. The last code
   * must be all ones.
   */
  //Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
  //        "inconsistent bit counts");
  //Tracev((stderr,"\ngen_codes: max_code %d ", max_code));

  for (n = 0;  n <= max_code; n++) {
    var len = tree[n * 2 + 1]/*.Len*/;
    if (len === 0) { continue; }
    /* Now reverse the bits */
    tree[n * 2]/*.Code*/ = bi_reverse(next_code[len]++, len);

    //Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
    //     n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
  }
}


/* ===========================================================================
 * Initialize the various 'constant' tables.
 */
function tr_static_init() {
  var n;        /* iterates over tree elements */
  var bits;     /* bit counter */
  var length;   /* length value */
  var code;     /* code value */
  var dist;     /* distance index */
  var bl_count = new Array(MAX_BITS + 1);
  /* number of codes at each bit length for an optimal tree */

  // do check in _tr_init()
  //if (static_init_done) return;

  /* For some embedded targets, global variables are not initialized: */
/*#ifdef NO_INIT_GLOBAL_POINTERS
  static_l_desc.static_tree = static_ltree;
  static_l_desc.extra_bits = extra_lbits;
  static_d_desc.static_tree = static_dtree;
  static_d_desc.extra_bits = extra_dbits;
  static_bl_desc.extra_bits = extra_blbits;
#endif*/

  /* Initialize the mapping length (0..255) -> length code (0..28) */
  length = 0;
  for (code = 0; code < LENGTH_CODES - 1; code++) {
    base_length[code] = length;
    for (n = 0; n < (1 << extra_lbits[code]); n++) {
      _length_code[length++] = code;
    }
  }
  //Assert (length == 256, "tr_static_init: length != 256");
  /* Note that the length 255 (match length 258) can be represented
   * in two different ways: code 284 + 5 bits or code 285, so we
   * overwrite length_code[255] to use the best encoding:
   */
  _length_code[length - 1] = code;

  /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
  dist = 0;
  for (code = 0; code < 16; code++) {
    base_dist[code] = dist;
    for (n = 0; n < (1 << extra_dbits[code]); n++) {
      _dist_code[dist++] = code;
    }
  }
  //Assert (dist == 256, "tr_static_init: dist != 256");
  dist >>= 7; /* from now on, all distances are divided by 128 */
  for (; code < D_CODES; code++) {
    base_dist[code] = dist << 7;
    for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) {
      _dist_code[256 + dist++] = code;
    }
  }
  //Assert (dist == 256, "tr_static_init: 256+dist != 512");

  /* Construct the codes of the static literal tree */
  for (bits = 0; bits <= MAX_BITS; bits++) {
    bl_count[bits] = 0;
  }

  n = 0;
  while (n <= 143) {
    static_ltree[n * 2 + 1]/*.Len*/ = 8;
    n++;
    bl_count[8]++;
  }
  while (n <= 255) {
    static_ltree[n * 2 + 1]/*.Len*/ = 9;
    n++;
    bl_count[9]++;
  }
  while (n <= 279) {
    static_ltree[n * 2 + 1]/*.Len*/ = 7;
    n++;
    bl_count[7]++;
  }
  while (n <= 287) {
    static_ltree[n * 2 + 1]/*.Len*/ = 8;
    n++;
    bl_count[8]++;
  }
  /* Codes 286 and 287 do not exist, but we must include them in the
   * tree construction to get a canonical Huffman tree (longest code
   * all ones)
   */
  gen_codes(static_ltree, L_CODES + 1, bl_count);

  /* The static distance tree is trivial: */
  for (n = 0; n < D_CODES; n++) {
    static_dtree[n * 2 + 1]/*.Len*/ = 5;
    static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5);
  }

  // Now data ready and we can init static trees
  static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS);
  static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS);
  static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0,         BL_CODES, MAX_BL_BITS);

  //static_init_done = true;
}


/* ===========================================================================
 * Initialize a new block.
 */
function init_block(s) {
  var n; /* iterates over tree elements */

  /* Initialize the trees. */
  for (n = 0; n < L_CODES;  n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; }
  for (n = 0; n < D_CODES;  n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; }
  for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; }

  s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1;
  s.opt_len = s.static_len = 0;
  s.last_lit = s.matches = 0;
}


/* ===========================================================================
 * Flush the bit buffer and align the output on a byte boundary
 */
function bi_windup(s)
{
  if (s.bi_valid > 8) {
    put_short(s, s.bi_buf);
  } else if (s.bi_valid > 0) {
    //put_byte(s, (Byte)s->bi_buf);
    s.pending_buf[s.pending++] = s.bi_buf;
  }
  s.bi_buf = 0;
  s.bi_valid = 0;
}

/* ===========================================================================
 * Copy a stored block, storing first the length and its
 * one's complement if requested.
 */
function copy_block(s, buf, len, header)
//DeflateState *s;
//charf    *buf;    /* the input data */
//unsigned len;     /* its length */
//int      header;  /* true if block header must be written */
{
  bi_windup(s);        /* align on byte boundary */

  if (header) {
    put_short(s, len);
    put_short(s, ~len);
  }
//  while (len--) {
//    put_byte(s, *buf++);
//  }
  utils.arraySet(s.pending_buf, s.window, buf, len, s.pending);
  s.pending += len;
}

/* ===========================================================================
 * Compares to subtrees, using the tree depth as tie breaker when
 * the subtrees have equal frequency. This minimizes the worst case length.
 */
function smaller(tree, n, m, depth) {
  var _n2 = n * 2;
  var _m2 = m * 2;
  return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ ||
         (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m]));
}

/* ===========================================================================
 * Restore the heap property by moving down the tree starting at node k,
 * exchanging a node with the smallest of its two sons if necessary, stopping
 * when the heap property is re-established (each father smaller than its
 * two sons).
 */
function pqdownheap(s, tree, k)
//    deflate_state *s;
//    ct_data *tree;  /* the tree to restore */
//    int k;               /* node to move down */
{
  var v = s.heap[k];
  var j = k << 1;  /* left son of k */
  while (j <= s.heap_len) {
    /* Set j to the smallest of the two sons: */
    if (j < s.heap_len &&
      smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) {
      j++;
    }
    /* Exit if v is smaller than both sons */
    if (smaller(tree, v, s.heap[j], s.depth)) { break; }

    /* Exchange v with the smallest son */
    s.heap[k] = s.heap[j];
    k = j;

    /* And continue down the tree, setting j to the left son of k */
    j <<= 1;
  }
  s.heap[k] = v;
}


// inlined manually
// var SMALLEST = 1;

/* ===========================================================================
 * Send the block data compressed using the given Huffman trees
 */
function compress_block(s, ltree, dtree)
//    deflate_state *s;
//    const ct_data *ltree; /* literal tree */
//    const ct_data *dtree; /* distance tree */
{
  var dist;           /* distance of matched string */
  var lc;             /* match length or unmatched char (if dist == 0) */
  var lx = 0;         /* running index in l_buf */
  var code;           /* the code to send */
  var extra;          /* number of extra bits to send */

  if (s.last_lit !== 0) {
    do {
      dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | (s.pending_buf[s.d_buf + lx * 2 + 1]);
      lc = s.pending_buf[s.l_buf + lx];
      lx++;

      if (dist === 0) {
        send_code(s, lc, ltree); /* send a literal byte */
        //Tracecv(isgraph(lc), (stderr," '%c' ", lc));
      } else {
        /* Here, lc is the match length - MIN_MATCH */
        code = _length_code[lc];
        send_code(s, code + LITERALS + 1, ltree); /* send the length code */
        extra = extra_lbits[code];
        if (extra !== 0) {
          lc -= base_length[code];
          send_bits(s, lc, extra);       /* send the extra length bits */
        }
        dist--; /* dist is now the match distance - 1 */
        code = d_code(dist);
        //Assert (code < D_CODES, "bad d_code");

        send_code(s, code, dtree);       /* send the distance code */
        extra = extra_dbits[code];
        if (extra !== 0) {
          dist -= base_dist[code];
          send_bits(s, dist, extra);   /* send the extra distance bits */
        }
      } /* literal or match pair ? */

      /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
      //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
      //       "pendingBuf overflow");

    } while (lx < s.last_lit);
  }

  send_code(s, END_BLOCK, ltree);
}


/* ===========================================================================
 * Construct one Huffman tree and assigns the code bit strings and lengths.
 * Update the total bit length for the current block.
 * IN assertion: the field freq is set for all tree elements.
 * OUT assertions: the fields len and code are set to the optimal bit length
 *     and corresponding code. The length opt_len is updated; static_len is
 *     also updated if stree is not null. The field max_code is set.
 */
function build_tree(s, desc)
//    deflate_state *s;
//    tree_desc *desc; /* the tree descriptor */
{
  var tree     = desc.dyn_tree;
  var stree    = desc.stat_desc.static_tree;
  var has_stree = desc.stat_desc.has_stree;
  var elems    = desc.stat_desc.elems;
  var n, m;          /* iterate over heap elements */
  var max_code = -1; /* largest code with non zero frequency */
  var node;          /* new node being created */

  /* Construct the initial heap, with least frequent element in
   * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
   * heap[0] is not used.
   */
  s.heap_len = 0;
  s.heap_max = HEAP_SIZE;

  for (n = 0; n < elems; n++) {
    if (tree[n * 2]/*.Freq*/ !== 0) {
      s.heap[++s.heap_len] = max_code = n;
      s.depth[n] = 0;

    } else {
      tree[n * 2 + 1]/*.Len*/ = 0;
    }
  }

  /* The pkzip format requires that at least one distance code exists,
   * and that at least one bit should be sent even if there is only one
   * possible code. So to avoid special checks later on we force at least
   * two codes of non zero frequency.
   */
  while (s.heap_len < 2) {
    node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0);
    tree[node * 2]/*.Freq*/ = 1;
    s.depth[node] = 0;
    s.opt_len--;

    if (has_stree) {
      s.static_len -= stree[node * 2 + 1]/*.Len*/;
    }
    /* node is 0 or 1 so it does not have extra bits */
  }
  desc.max_code = max_code;

  /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
   * establish sub-heaps of increasing lengths:
   */
  for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); }

  /* Construct the Huffman tree by repeatedly combining the least two
   * frequent nodes.
   */
  node = elems;              /* next internal node of the tree */
  do {
    //pqremove(s, tree, n);  /* n = node of least frequency */
    /*** pqremove ***/
    n = s.heap[1/*SMALLEST*/];
    s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--];
    pqdownheap(s, tree, 1/*SMALLEST*/);
    /***/

    m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */

    s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */
    s.heap[--s.heap_max] = m;

    /* Create a new node father of n and m */
    tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/;
    s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;
    tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node;

    /* and insert the new node in the heap */
    s.heap[1/*SMALLEST*/] = node++;
    pqdownheap(s, tree, 1/*SMALLEST*/);

  } while (s.heap_len >= 2);

  s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/];

  /* At this point, the fields freq and dad are set. We can now
   * generate the bit lengths.
   */
  gen_bitlen(s, desc);

  /* The field len is now set, we can generate the bit codes */
  gen_codes(tree, max_code, s.bl_count);
}


/* ===========================================================================
 * Scan a literal or distance tree to determine the frequencies of the codes
 * in the bit length tree.
 */
function scan_tree(s, tree, max_code)
//    deflate_state *s;
//    ct_data *tree;   /* the tree to be scanned */
//    int max_code;    /* and its largest code of non zero frequency */
{
  var n;                     /* iterates over all tree elements */
  var prevlen = -1;          /* last emitted length */
  var curlen;                /* length of current code */

  var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */

  var count = 0;             /* repeat count of the current code */
  var max_count = 7;         /* max repeat count */
  var min_count = 4;         /* min repeat count */

  if (nextlen === 0) {
    max_count = 138;
    min_count = 3;
  }
  tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */

  for (n = 0; n <= max_code; n++) {
    curlen = nextlen;
    nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;

    if (++count < max_count && curlen === nextlen) {
      continue;

    } else if (count < min_count) {
      s.bl_tree[curlen * 2]/*.Freq*/ += count;

    } else if (curlen !== 0) {

      if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; }
      s.bl_tree[REP_3_6 * 2]/*.Freq*/++;

    } else if (count <= 10) {
      s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++;

    } else {
      s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++;
    }

    count = 0;
    prevlen = curlen;

    if (nextlen === 0) {
      max_count = 138;
      min_count = 3;

    } else if (curlen === nextlen) {
      max_count = 6;
      min_count = 3;

    } else {
      max_count = 7;
      min_count = 4;
    }
  }
}


/* ===========================================================================
 * Send a literal or distance tree in compressed form, using the codes in
 * bl_tree.
 */
function send_tree(s, tree, max_code)
//    deflate_state *s;
//    ct_data *tree; /* the tree to be scanned */
//    int max_code;       /* and its largest code of non zero frequency */
{
  var n;                     /* iterates over all tree elements */
  var prevlen = -1;          /* last emitted length */
  var curlen;                /* length of current code */

  var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */

  var count = 0;             /* repeat count of the current code */
  var max_count = 7;         /* max repeat count */
  var min_count = 4;         /* min repeat count */

  /* tree[max_code+1].Len = -1; */  /* guard already set */
  if (nextlen === 0) {
    max_count = 138;
    min_count = 3;
  }

  for (n = 0; n <= max_code; n++) {
    curlen = nextlen;
    nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;

    if (++count < max_count && curlen === nextlen) {
      continue;

    } else if (count < min_count) {
      do { send_code(s, curlen, s.bl_tree); } while (--count !== 0);

    } else if (curlen !== 0) {
      if (curlen !== prevlen) {
        send_code(s, curlen, s.bl_tree);
        count--;
      }
      //Assert(count >= 3 && count <= 6, " 3_6?");
      send_code(s, REP_3_6, s.bl_tree);
      send_bits(s, count - 3, 2);

    } else if (count <= 10) {
      send_code(s, REPZ_3_10, s.bl_tree);
      send_bits(s, count - 3, 3);

    } else {
      send_code(s, REPZ_11_138, s.bl_tree);
      send_bits(s, count - 11, 7);
    }

    count = 0;
    prevlen = curlen;
    if (nextlen === 0) {
      max_count = 138;
      min_count = 3;

    } else if (curlen === nextlen) {
      max_count = 6;
      min_count = 3;

    } else {
      max_count = 7;
      min_count = 4;
    }
  }
}


/* ===========================================================================
 * Construct the Huffman tree for the bit lengths and return the index in
 * bl_order of the last bit length code to send.
 */
function build_bl_tree(s) {
  var max_blindex;  /* index of last bit length code of non zero freq */

  /* Determine the bit length frequencies for literal and distance trees */
  scan_tree(s, s.dyn_ltree, s.l_desc.max_code);
  scan_tree(s, s.dyn_dtree, s.d_desc.max_code);

  /* Build the bit length tree: */
  build_tree(s, s.bl_desc);
  /* opt_len now includes the length of the tree representations, except
   * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
   */

  /* Determine the number of bit length codes to send. The pkzip format
   * requires that at least 4 bit length codes be sent. (appnote.txt says
   * 3 but the actual value used is 4.)
   */
  for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) {
    if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) {
      break;
    }
  }
  /* Update opt_len to include the bit length tree and counts */
  s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
  //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
  //        s->opt_len, s->static_len));

  return max_blindex;
}


/* ===========================================================================
 * Send the header for a block using dynamic Huffman trees: the counts, the
 * lengths of the bit length codes, the literal tree and the distance tree.
 * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
 */
function send_all_trees(s, lcodes, dcodes, blcodes)
//    deflate_state *s;
//    int lcodes, dcodes, blcodes; /* number of codes for each tree */
{
  var rank;                    /* index in bl_order */

  //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
  //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
  //        "too many codes");
  //Tracev((stderr, "\nbl counts: "));
  send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */
  send_bits(s, dcodes - 1,   5);
  send_bits(s, blcodes - 4,  4); /* not -3 as stated in appnote.txt */
  for (rank = 0; rank < blcodes; rank++) {
    //Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
    send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3);
  }
  //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));

  send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */
  //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));

  send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */
  //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
}


/* ===========================================================================
 * Check if the data type is TEXT or BINARY, using the following algorithm:
 * - TEXT if the two conditions below are satisfied:
 *    a) There are no non-portable control characters belonging to the
 *       "black list" (0..6, 14..25, 28..31).
 *    b) There is at least one printable character belonging to the
 *       "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
 * - BINARY otherwise.
 * - The following partially-portable control characters form a
 *   "gray list" that is ignored in this detection algorithm:
 *   (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
 * IN assertion: the fields Freq of dyn_ltree are set.
 */
function detect_data_type(s) {
  /* black_mask is the bit mask of black-listed bytes
   * set bits 0..6, 14..25, and 28..31
   * 0xf3ffc07f = binary 11110011111111111100000001111111
   */
  var black_mask = 0xf3ffc07f;
  var n;

  /* Check for non-textual ("black-listed") bytes. */
  for (n = 0; n <= 31; n++, black_mask >>>= 1) {
    if ((black_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) {
      return Z_BINARY;
    }
  }

  /* Check for textual ("white-listed") bytes. */
  if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 ||
      s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) {
    return Z_TEXT;
  }
  for (n = 32; n < LITERALS; n++) {
    if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) {
      return Z_TEXT;
    }
  }

  /* There are no "black-listed" or "white-listed" bytes:
   * this stream either is empty or has tolerated ("gray-listed") bytes only.
   */
  return Z_BINARY;
}


var static_init_done = false;

/* ===========================================================================
 * Initialize the tree data structures for a new zlib stream.
 */
function _tr_init(s)
{

  if (!static_init_done) {
    tr_static_init();
    static_init_done = true;
  }

  s.l_desc  = new TreeDesc(s.dyn_ltree, static_l_desc);
  s.d_desc  = new TreeDesc(s.dyn_dtree, static_d_desc);
  s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc);

  s.bi_buf = 0;
  s.bi_valid = 0;

  /* Initialize the first block of the first file: */
  init_block(s);
}


/* ===========================================================================
 * Send a stored block
 */
function _tr_stored_block(s, buf, stored_len, last)
//DeflateState *s;
//charf *buf;       /* input block */
//ulg stored_len;   /* length of input block */
//int last;         /* one if this is the last block for a file */
{
  send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3);    /* send block type */
  copy_block(s, buf, stored_len, true); /* with header */
}


/* ===========================================================================
 * Send one empty static block to give enough lookahead for inflate.
 * This takes 10 bits, of which 7 may remain in the bit buffer.
 */
function _tr_align(s) {
  send_bits(s, STATIC_TREES << 1, 3);
  send_code(s, END_BLOCK, static_ltree);
  bi_flush(s);
}


/* ===========================================================================
 * Determine the best encoding for the current block: dynamic trees, static
 * trees or store, and output the encoded block to the zip file.
 */
function _tr_flush_block(s, buf, stored_len, last)
//DeflateState *s;
//charf *buf;       /* input block, or NULL if too old */
//ulg stored_len;   /* length of input block */
//int last;         /* one if this is the last block for a file */
{
  var opt_lenb, static_lenb;  /* opt_len and static_len in bytes */
  var max_blindex = 0;        /* index of last bit length code of non zero freq */

  /* Build the Huffman trees unless a stored block is forced */
  if (s.level > 0) {

    /* Check if the file is binary or text */
    if (s.strm.data_type === Z_UNKNOWN) {
      s.strm.data_type = detect_data_type(s);
    }

    /* Construct the literal and distance trees */
    build_tree(s, s.l_desc);
    // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
    //        s->static_len));

    build_tree(s, s.d_desc);
    // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
    //        s->static_len));
    /* At this point, opt_len and static_len are the total bit lengths of
     * the compressed block data, excluding the tree representations.
     */

    /* Build the bit length tree for the above two trees, and get the index
     * in bl_order of the last bit length code to send.
     */
    max_blindex = build_bl_tree(s);

    /* Determine the best encoding. Compute the block lengths in bytes. */
    opt_lenb = (s.opt_len + 3 + 7) >>> 3;
    static_lenb = (s.static_len + 3 + 7) >>> 3;

    // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
    //        opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
    //        s->last_lit));

    if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; }

  } else {
    // Assert(buf != (char*)0, "lost buf");
    opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
  }

  if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) {
    /* 4: two words for the lengths */

    /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
     * Otherwise we can't have processed more than WSIZE input bytes since
     * the last block flush, because compression would have been
     * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
     * transform a block into a stored block.
     */
    _tr_stored_block(s, buf, stored_len, last);

  } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) {

    send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3);
    compress_block(s, static_ltree, static_dtree);

  } else {
    send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3);
    send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1);
    compress_block(s, s.dyn_ltree, s.dyn_dtree);
  }
  // Assert (s->compressed_len == s->bits_sent, "bad compressed size");
  /* The above check is made mod 2^32, for files larger than 512 MB
   * and uLong implemented on 32 bits.
   */
  init_block(s);

  if (last) {
    bi_windup(s);
  }
  // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
  //       s->compressed_len-7*last));
}

/* ===========================================================================
 * Save the match info and tally the frequency counts. Return true if
 * the current block must be flushed.
 */
function _tr_tally(s, dist, lc)
//    deflate_state *s;
//    unsigned dist;  /* distance of matched string */
//    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */
{
  //var out_length, in_length, dcode;

  s.pending_buf[s.d_buf + s.last_lit * 2]     = (dist >>> 8) & 0xff;
  s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff;

  s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff;
  s.last_lit++;

  if (dist === 0) {
    /* lc is the unmatched char */
    s.dyn_ltree[lc * 2]/*.Freq*/++;
  } else {
    s.matches++;
    /* Here, lc is the match length - MIN_MATCH */
    dist--;             /* dist = match distance - 1 */
    //Assert((ush)dist < (ush)MAX_DIST(s) &&
    //       (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
    //       (ush)d_code(dist) < (ush)D_CODES,  "_tr_tally: bad match");

    s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++;
    s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++;
  }

// (!) This block is disabled in zlib defaults,
// don't enable it for binary compatibility

//#ifdef TRUNCATE_BLOCK
//  /* Try to guess if it is profitable to stop the current block here */
//  if ((s.last_lit & 0x1fff) === 0 && s.level > 2) {
//    /* Compute an upper bound for the compressed length */
//    out_length = s.last_lit*8;
//    in_length = s.strstart - s.block_start;
//
//    for (dcode = 0; dcode < D_CODES; dcode++) {
//      out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]);
//    }
//    out_length >>>= 3;
//    //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
//    //       s->last_lit, in_length, out_length,
//    //       100L - out_length*100L/in_length));
//    if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) {
//      return true;
//    }
//  }
//#endif

  return (s.last_lit === s.lit_bufsize - 1);
  /* We avoid equality with lit_bufsize because of wraparound at 64K
   * on 16 bit machines and because stored blocks are restricted to
   * 64K-1 bytes.
   */
}

exports._tr_init  = _tr_init;
exports._tr_stored_block = _tr_stored_block;
exports._tr_flush_block  = _tr_flush_block;
exports._tr_tally = _tr_tally;
exports._tr_align = _tr_align;

},{"../utils/common":179}],189:[function(require,module,exports){
'use strict';

// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

function ZStream() {
  /* next input byte */
  this.input = null; // JS specific, because we have no pointers
  this.next_in = 0;
  /* number of bytes available at input */
  this.avail_in = 0;
  /* total number of input bytes read so far */
  this.total_in = 0;
  /* next output byte should be put there */
  this.output = null; // JS specific, because we have no pointers
  this.next_out = 0;
  /* remaining free space at output */
  this.avail_out = 0;
  /* total number of bytes output so far */
  this.total_out = 0;
  /* last error message, NULL if no error */
  this.msg = ''/*Z_NULL*/;
  /* not visible by applications */
  this.state = null;
  /* best guess about the data type: binary or text */
  this.data_type = 2/*Z_UNKNOWN*/;
  /* adler32 value of the uncompressed data */
  this.adler = 0;
}

module.exports = ZStream;

},{}],190:[function(require,module,exports){
(function (process){(function (){
// 'path' module extracted from Node.js v8.11.1 (only the posix part)
// transplited with Babel

// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

'use strict';

function assertPath(path) {
  if (typeof path !== 'string') {
    throw new TypeError('Path must be a string. Received ' + JSON.stringify(path));
  }
}

// Resolves . and .. elements in a path with directory names
function normalizeStringPosix(path, allowAboveRoot) {
  var res = '';
  var lastSegmentLength = 0;
  var lastSlash = -1;
  var dots = 0;
  var code;
  for (var i = 0; i <= path.length; ++i) {
    if (i < path.length)
      code = path.charCodeAt(i);
    else if (code === 47 /*/*/)
      break;
    else
      code = 47 /*/*/;
    if (code === 47 /*/*/) {
      if (lastSlash === i - 1 || dots === 1) {
        // NOOP
      } else if (lastSlash !== i - 1 && dots === 2) {
        if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 /*.*/ || res.charCodeAt(res.length - 2) !== 46 /*.*/) {
          if (res.length > 2) {
            var lastSlashIndex = res.lastIndexOf('/');
            if (lastSlashIndex !== res.length - 1) {
              if (lastSlashIndex === -1) {
                res = '';
                lastSegmentLength = 0;
              } else {
                res = res.slice(0, lastSlashIndex);
                lastSegmentLength = res.length - 1 - res.lastIndexOf('/');
              }
              lastSlash = i;
              dots = 0;
              continue;
            }
          } else if (res.length === 2 || res.length === 1) {
            res = '';
            lastSegmentLength = 0;
            lastSlash = i;
            dots = 0;
            continue;
          }
        }
        if (allowAboveRoot) {
          if (res.length > 0)
            res += '/..';
          else
            res = '..';
          lastSegmentLength = 2;
        }
      } else {
        if (res.length > 0)
          res += '/' + path.slice(lastSlash + 1, i);
        else
          res = path.slice(lastSlash + 1, i);
        lastSegmentLength = i - lastSlash - 1;
      }
      lastSlash = i;
      dots = 0;
    } else if (code === 46 /*.*/ && dots !== -1) {
      ++dots;
    } else {
      dots = -1;
    }
  }
  return res;
}

function _format(sep, pathObject) {
  var dir = pathObject.dir || pathObject.root;
  var base = pathObject.base || (pathObject.name || '') + (pathObject.ext || '');
  if (!dir) {
    return base;
  }
  if (dir === pathObject.root) {
    return dir + base;
  }
  return dir + sep + base;
}

var posix = {
  // path.resolve([from ...], to)
  resolve: function resolve() {
    var resolvedPath = '';
    var resolvedAbsolute = false;
    var cwd;

    for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
      var path;
      if (i >= 0)
        path = arguments[i];
      else {
        if (cwd === undefined)
          cwd = process.cwd();
        path = cwd;
      }

      assertPath(path);

      // Skip empty entries
      if (path.length === 0) {
        continue;
      }

      resolvedPath = path + '/' + resolvedPath;
      resolvedAbsolute = path.charCodeAt(0) === 47 /*/*/;
    }

    // At this point the path should be resolved to a full absolute path, but
    // handle relative paths to be safe (might happen when process.cwd() fails)

    // Normalize the path
    resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute);

    if (resolvedAbsolute) {
      if (resolvedPath.length > 0)
        return '/' + resolvedPath;
      else
        return '/';
    } else if (resolvedPath.length > 0) {
      return resolvedPath;
    } else {
      return '.';
    }
  },

  normalize: function normalize(path) {
    assertPath(path);

    if (path.length === 0) return '.';

    var isAbsolute = path.charCodeAt(0) === 47 /*/*/;
    var trailingSeparator = path.charCodeAt(path.length - 1) === 47 /*/*/;

    // Normalize the path
    path = normalizeStringPosix(path, !isAbsolute);

    if (path.length === 0 && !isAbsolute) path = '.';
    if (path.length > 0 && trailingSeparator) path += '/';

    if (isAbsolute) return '/' + path;
    return path;
  },

  isAbsolute: function isAbsolute(path) {
    assertPath(path);
    return path.length > 0 && path.charCodeAt(0) === 47 /*/*/;
  },

  join: function join() {
    if (arguments.length === 0)
      return '.';
    var joined;
    for (var i = 0; i < arguments.length; ++i) {
      var arg = arguments[i];
      assertPath(arg);
      if (arg.length > 0) {
        if (joined === undefined)
          joined = arg;
        else
          joined += '/' + arg;
      }
    }
    if (joined === undefined)
      return '.';
    return posix.normalize(joined);
  },

  relative: function relative(from, to) {
    assertPath(from);
    assertPath(to);

    if (from === to) return '';

    from = posix.resolve(from);
    to = posix.resolve(to);

    if (from === to) return '';

    // Trim any leading backslashes
    var fromStart = 1;
    for (; fromStart < from.length; ++fromStart) {
      if (from.charCodeAt(fromStart) !== 47 /*/*/)
        break;
    }
    var fromEnd = from.length;
    var fromLen = fromEnd - fromStart;

    // Trim any leading backslashes
    var toStart = 1;
    for (; toStart < to.length; ++toStart) {
      if (to.charCodeAt(toStart) !== 47 /*/*/)
        break;
    }
    var toEnd = to.length;
    var toLen = toEnd - toStart;

    // Compare paths to find the longest common path from root
    var length = fromLen < toLen ? fromLen : toLen;
    var lastCommonSep = -1;
    var i = 0;
    for (; i <= length; ++i) {
      if (i === length) {
        if (toLen > length) {
          if (to.charCodeAt(toStart + i) === 47 /*/*/) {
            // We get here if `from` is the exact base path for `to`.
            // For example: from='/foo/bar'; to='/foo/bar/baz'
            return to.slice(toStart + i + 1);
          } else if (i === 0) {
            // We get here if `from` is the root
            // For example: from='/'; to='/foo'
            return to.slice(toStart + i);
          }
        } else if (fromLen > length) {
          if (from.charCodeAt(fromStart + i) === 47 /*/*/) {
            // We get here if `to` is the exact base path for `from`.
            // For example: from='/foo/bar/baz'; to='/foo/bar'
            lastCommonSep = i;
          } else if (i === 0) {
            // We get here if `to` is the root.
            // For example: from='/foo'; to='/'
            lastCommonSep = 0;
          }
        }
        break;
      }
      var fromCode = from.charCodeAt(fromStart + i);
      var toCode = to.charCodeAt(toStart + i);
      if (fromCode !== toCode)
        break;
      else if (fromCode === 47 /*/*/)
        lastCommonSep = i;
    }

    var out = '';
    // Generate the relative path based on the path difference between `to`
    // and `from`
    for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
      if (i === fromEnd || from.charCodeAt(i) === 47 /*/*/) {
        if (out.length === 0)
          out += '..';
        else
          out += '/..';
      }
    }

    // Lastly, append the rest of the destination (`to`) path that comes after
    // the common path parts
    if (out.length > 0)
      return out + to.slice(toStart + lastCommonSep);
    else {
      toStart += lastCommonSep;
      if (to.charCodeAt(toStart) === 47 /*/*/)
        ++toStart;
      return to.slice(toStart);
    }
  },

  _makeLong: function _makeLong(path) {
    return path;
  },

  dirname: function dirname(path) {
    assertPath(path);
    if (path.length === 0) return '.';
    var code = path.charCodeAt(0);
    var hasRoot = code === 47 /*/*/;
    var end = -1;
    var matchedSlash = true;
    for (var i = path.length - 1; i >= 1; --i) {
      code = path.charCodeAt(i);
      if (code === 47 /*/*/) {
          if (!matchedSlash) {
            end = i;
            break;
          }
        } else {
        // We saw the first non-path separator
        matchedSlash = false;
      }
    }

    if (end === -1) return hasRoot ? '/' : '.';
    if (hasRoot && end === 1) return '//';
    return path.slice(0, end);
  },

  basename: function basename(path, ext) {
    if (ext !== undefined && typeof ext !== 'string') throw new TypeError('"ext" argument must be a string');
    assertPath(path);

    var start = 0;
    var end = -1;
    var matchedSlash = true;
    var i;

    if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
      if (ext.length === path.length && ext === path) return '';
      var extIdx = ext.length - 1;
      var firstNonSlashEnd = -1;
      for (i = path.length - 1; i >= 0; --i) {
        var code = path.charCodeAt(i);
        if (code === 47 /*/*/) {
            // If we reached a path separator that was not part of a set of path
            // separators at the end of the string, stop now
            if (!matchedSlash) {
              start = i + 1;
              break;
            }
          } else {
          if (firstNonSlashEnd === -1) {
            // We saw the first non-path separator, remember this index in case
            // we need it if the extension ends up not matching
            matchedSlash = false;
            firstNonSlashEnd = i + 1;
          }
          if (extIdx >= 0) {
            // Try to match the explicit extension
            if (code === ext.charCodeAt(extIdx)) {
              if (--extIdx === -1) {
                // We matched the extension, so mark this as the end of our path
                // component
                end = i;
              }
            } else {
              // Extension does not match, so our result is the entire path
              // component
              extIdx = -1;
              end = firstNonSlashEnd;
            }
          }
        }
      }

      if (start === end) end = firstNonSlashEnd;else if (end === -1) end = path.length;
      return path.slice(start, end);
    } else {
      for (i = path.length - 1; i >= 0; --i) {
        if (path.charCodeAt(i) === 47 /*/*/) {
            // If we reached a path separator that was not part of a set of path
            // separators at the end of the string, stop now
            if (!matchedSlash) {
              start = i + 1;
              break;
            }
          } else if (end === -1) {
          // We saw the first non-path separator, mark this as the end of our
          // path component
          matchedSlash = false;
          end = i + 1;
        }
      }

      if (end === -1) return '';
      return path.slice(start, end);
    }
  },

  extname: function extname(path) {
    assertPath(path);
    var startDot = -1;
    var startPart = 0;
    var end = -1;
    var matchedSlash = true;
    // Track the state of characters (if any) we see before our first dot and
    // after any path separator we find
    var preDotState = 0;
    for (var i = path.length - 1; i >= 0; --i) {
      var code = path.charCodeAt(i);
      if (code === 47 /*/*/) {
          // If we reached a path separator that was not part of a set of path
          // separators at the end of the string, stop now
          if (!matchedSlash) {
            startPart = i + 1;
            break;
          }
          continue;
        }
      if (end === -1) {
        // We saw the first non-path separator, mark this as the end of our
        // extension
        matchedSlash = false;
        end = i + 1;
      }
      if (code === 46 /*.*/) {
          // If this is our first dot, mark it as the start of our extension
          if (startDot === -1)
            startDot = i;
          else if (preDotState !== 1)
            preDotState = 1;
      } else if (startDot !== -1) {
        // We saw a non-dot and non-path separator before our dot, so we should
        // have a good chance at having a non-empty extension
        preDotState = -1;
      }
    }

    if (startDot === -1 || end === -1 ||
        // We saw a non-dot character immediately before the dot
        preDotState === 0 ||
        // The (right-most) trimmed path component is exactly '..'
        preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
      return '';
    }
    return path.slice(startDot, end);
  },

  format: function format(pathObject) {
    if (pathObject === null || typeof pathObject !== 'object') {
      throw new TypeError('The "pathObject" argument must be of type Object. Received type ' + typeof pathObject);
    }
    return _format('/', pathObject);
  },

  parse: function parse(path) {
    assertPath(path);

    var ret = { root: '', dir: '', base: '', ext: '', name: '' };
    if (path.length === 0) return ret;
    var code = path.charCodeAt(0);
    var isAbsolute = code === 47 /*/*/;
    var start;
    if (isAbsolute) {
      ret.root = '/';
      start = 1;
    } else {
      start = 0;
    }
    var startDot = -1;
    var startPart = 0;
    var end = -1;
    var matchedSlash = true;
    var i = path.length - 1;

    // Track the state of characters (if any) we see before our first dot and
    // after any path separator we find
    var preDotState = 0;

    // Get non-dir info
    for (; i >= start; --i) {
      code = path.charCodeAt(i);
      if (code === 47 /*/*/) {
          // If we reached a path separator that was not part of a set of path
          // separators at the end of the string, stop now
          if (!matchedSlash) {
            startPart = i + 1;
            break;
          }
          continue;
        }
      if (end === -1) {
        // We saw the first non-path separator, mark this as the end of our
        // extension
        matchedSlash = false;
        end = i + 1;
      }
      if (code === 46 /*.*/) {
          // If this is our first dot, mark it as the start of our extension
          if (startDot === -1) startDot = i;else if (preDotState !== 1) preDotState = 1;
        } else if (startDot !== -1) {
        // We saw a non-dot and non-path separator before our dot, so we should
        // have a good chance at having a non-empty extension
        preDotState = -1;
      }
    }

    if (startDot === -1 || end === -1 ||
    // We saw a non-dot character immediately before the dot
    preDotState === 0 ||
    // The (right-most) trimmed path component is exactly '..'
    preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
      if (end !== -1) {
        if (startPart === 0 && isAbsolute) ret.base = ret.name = path.slice(1, end);else ret.base = ret.name = path.slice(startPart, end);
      }
    } else {
      if (startPart === 0 && isAbsolute) {
        ret.name = path.slice(1, startDot);
        ret.base = path.slice(1, end);
      } else {
        ret.name = path.slice(startPart, startDot);
        ret.base = path.slice(startPart, end);
      }
      ret.ext = path.slice(startDot, end);
    }

    if (startPart > 0) ret.dir = path.slice(0, startPart - 1);else if (isAbsolute) ret.dir = '/';

    return ret;
  },

  sep: '/',
  delimiter: ':',
  win32: null,
  posix: null
};

posix.posix = posix;

module.exports = posix;

}).call(this)}).call(this,require('_process'))
},{"_process":191}],191:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};

// cached from whatever global is present so that test runners that stub it
// don't break things.  But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals.  It's inside a
// function because try/catches deoptimize in certain engines.

var cachedSetTimeout;
var cachedClearTimeout;

function defaultSetTimout() {
    throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
    throw new Error('clearTimeout has not been defined');
}
(function () {
    try {
        if (typeof setTimeout === 'function') {
            cachedSetTimeout = setTimeout;
        } else {
            cachedSetTimeout = defaultSetTimout;
        }
    } catch (e) {
        cachedSetTimeout = defaultSetTimout;
    }
    try {
        if (typeof clearTimeout === 'function') {
            cachedClearTimeout = clearTimeout;
        } else {
            cachedClearTimeout = defaultClearTimeout;
        }
    } catch (e) {
        cachedClearTimeout = defaultClearTimeout;
    }
} ())
function runTimeout(fun) {
    if (cachedSetTimeout === setTimeout) {
        //normal enviroments in sane situations
        return setTimeout(fun, 0);
    }
    // if setTimeout wasn't available but was latter defined
    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
        cachedSetTimeout = setTimeout;
        return setTimeout(fun, 0);
    }
    try {
        // when when somebody has screwed with setTimeout but no I.E. maddness
        return cachedSetTimeout(fun, 0);
    } catch(e){
        try {
            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
            return cachedSetTimeout.call(null, fun, 0);
        } catch(e){
            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
            return cachedSetTimeout.call(this, fun, 0);
        }
    }


}
function runClearTimeout(marker) {
    if (cachedClearTimeout === clearTimeout) {
        //normal enviroments in sane situations
        return clearTimeout(marker);
    }
    // if clearTimeout wasn't available but was latter defined
    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
        cachedClearTimeout = clearTimeout;
        return clearTimeout(marker);
    }
    try {
        // when when somebody has screwed with setTimeout but no I.E. maddness
        return cachedClearTimeout(marker);
    } catch (e){
        try {
            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
            return cachedClearTimeout.call(null, marker);
        } catch (e){
            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
            // Some versions of I.E. have different rules for clearTimeout vs setTimeout
            return cachedClearTimeout.call(this, marker);
        }
    }



}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;

function cleanUpNextTick() {
    if (!draining || !currentQueue) {
        return;
    }
    draining = false;
    if (currentQueue.length) {
        queue = currentQueue.concat(queue);
    } else {
        queueIndex = -1;
    }
    if (queue.length) {
        drainQueue();
    }
}

function drainQueue() {
    if (draining) {
        return;
    }
    var timeout = runTimeout(cleanUpNextTick);
    draining = true;

    var len = queue.length;
    while(len) {
        currentQueue = queue;
        queue = [];
        while (++queueIndex < len) {
            if (currentQueue) {
                currentQueue[queueIndex].run();
            }
        }
        queueIndex = -1;
        len = queue.length;
    }
    currentQueue = null;
    draining = false;
    runClearTimeout(timeout);
}

process.nextTick = function (fun) {
    var args = new Array(arguments.length - 1);
    if (arguments.length > 1) {
        for (var i = 1; i < arguments.length; i++) {
            args[i - 1] = arguments[i];
        }
    }
    queue.push(new Item(fun, args));
    if (queue.length === 1 && !draining) {
        runTimeout(drainQueue);
    }
};

// v8 likes predictible objects
function Item(fun, array) {
    this.fun = fun;
    this.array = array;
}
Item.prototype.run = function () {
    this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};

function noop() {}

process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.prependListener = noop;
process.prependOnceListener = noop;

process.listeners = function (name) { return [] }

process.binding = function (name) {
    throw new Error('process.binding is not supported');
};

process.cwd = function () { return '/' };
process.chdir = function (dir) {
    throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };

},{}],192:[function(require,module,exports){
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
/* eslint-disable node/no-deprecated-api */
var buffer = require('buffer')
var Buffer = buffer.Buffer

// alternative to using Object.keys for old browsers
function copyProps (src, dst) {
  for (var key in src) {
    dst[key] = src[key]
  }
}
if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) {
  module.exports = buffer
} else {
  // Copy properties from require('buffer')
  copyProps(buffer, exports)
  exports.Buffer = SafeBuffer
}

function SafeBuffer (arg, encodingOrOffset, length) {
  return Buffer(arg, encodingOrOffset, length)
}

SafeBuffer.prototype = Object.create(Buffer.prototype)

// Copy static methods from Buffer
copyProps(Buffer, SafeBuffer)

SafeBuffer.from = function (arg, encodingOrOffset, length) {
  if (typeof arg === 'number') {
    throw new TypeError('Argument must not be a number')
  }
  return Buffer(arg, encodingOrOffset, length)
}

SafeBuffer.alloc = function (size, fill, encoding) {
  if (typeof size !== 'number') {
    throw new TypeError('Argument must be a number')
  }
  var buf = Buffer(size)
  if (fill !== undefined) {
    if (typeof encoding === 'string') {
      buf.fill(fill, encoding)
    } else {
      buf.fill(fill)
    }
  } else {
    buf.fill(0)
  }
  return buf
}

SafeBuffer.allocUnsafe = function (size) {
  if (typeof size !== 'number') {
    throw new TypeError('Argument must be a number')
  }
  return Buffer(size)
}

SafeBuffer.allocUnsafeSlow = function (size) {
  if (typeof size !== 'number') {
    throw new TypeError('Argument must be a number')
  }
  return buffer.SlowBuffer(size)
}

},{"buffer":159}],193:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

module.exports = Stream;

var EE = require('events').EventEmitter;
var inherits = require('inherits');

inherits(Stream, EE);
Stream.Readable = require('readable-stream/lib/_stream_readable.js');
Stream.Writable = require('readable-stream/lib/_stream_writable.js');
Stream.Duplex = require('readable-stream/lib/_stream_duplex.js');
Stream.Transform = require('readable-stream/lib/_stream_transform.js');
Stream.PassThrough = require('readable-stream/lib/_stream_passthrough.js');
Stream.finished = require('readable-stream/lib/internal/streams/end-of-stream.js')
Stream.pipeline = require('readable-stream/lib/internal/streams/pipeline.js')

// Backwards-compat with node 0.4.x
Stream.Stream = Stream;



// old-style streams.  Note that the pipe method (the only relevant
// part of this class) is overridden in the Readable class.

function Stream() {
  EE.call(this);
}

Stream.prototype.pipe = function(dest, options) {
  var source = this;

  function ondata(chunk) {
    if (dest.writable) {
      if (false === dest.write(chunk) && source.pause) {
        source.pause();
      }
    }
  }

  source.on('data', ondata);

  function ondrain() {
    if (source.readable && source.resume) {
      source.resume();
    }
  }

  dest.on('drain', ondrain);

  // If the 'end' option is not supplied, dest.end() will be called when
  // source gets the 'end' or 'close' events.  Only dest.end() once.
  if (!dest._isStdio && (!options || options.end !== false)) {
    source.on('end', onend);
    source.on('close', onclose);
  }

  var didOnEnd = false;
  function onend() {
    if (didOnEnd) return;
    didOnEnd = true;

    dest.end();
  }


  function onclose() {
    if (didOnEnd) return;
    didOnEnd = true;

    if (typeof dest.destroy === 'function') dest.destroy();
  }

  // don't leave dangling pipes when there are errors.
  function onerror(er) {
    cleanup();
    if (EE.listenerCount(this, 'error') === 0) {
      throw er; // Unhandled stream error in pipe.
    }
  }

  source.on('error', onerror);
  dest.on('error', onerror);

  // remove all the event listeners that were added.
  function cleanup() {
    source.removeListener('data', ondata);
    dest.removeListener('drain', ondrain);

    source.removeListener('end', onend);
    source.removeListener('close', onclose);

    source.removeListener('error', onerror);
    dest.removeListener('error', onerror);

    source.removeListener('end', cleanup);
    source.removeListener('close', cleanup);

    dest.removeListener('close', cleanup);
  }

  source.on('end', cleanup);
  source.on('close', cleanup);

  dest.on('close', cleanup);

  dest.emit('pipe', source);

  // Allow for unix-like usage: A.pipe(B).pipe(C)
  return dest;
};

},{"events":163,"inherits":172,"readable-stream/lib/_stream_duplex.js":195,"readable-stream/lib/_stream_passthrough.js":196,"readable-stream/lib/_stream_readable.js":197,"readable-stream/lib/_stream_transform.js":198,"readable-stream/lib/_stream_writable.js":199,"readable-stream/lib/internal/streams/end-of-stream.js":203,"readable-stream/lib/internal/streams/pipeline.js":205}],194:[function(require,module,exports){
'use strict';

function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }

var codes = {};

function createErrorType(code, message, Base) {
  if (!Base) {
    Base = Error;
  }

  function getMessage(arg1, arg2, arg3) {
    if (typeof message === 'string') {
      return message;
    } else {
      return message(arg1, arg2, arg3);
    }
  }

  var NodeError =
  /*#__PURE__*/
  function (_Base) {
    _inheritsLoose(NodeError, _Base);

    function NodeError(arg1, arg2, arg3) {
      return _Base.call(this, getMessage(arg1, arg2, arg3)) || this;
    }

    return NodeError;
  }(Base);

  NodeError.prototype.name = Base.name;
  NodeError.prototype.code = code;
  codes[code] = NodeError;
} // https://github.com/nodejs/node/blob/v10.8.0/lib/internal/errors.js


function oneOf(expected, thing) {
  if (Array.isArray(expected)) {
    var len = expected.length;
    expected = expected.map(function (i) {
      return String(i);
    });

    if (len > 2) {
      return "one of ".concat(thing, " ").concat(expected.slice(0, len - 1).join(', '), ", or ") + expected[len - 1];
    } else if (len === 2) {
      return "one of ".concat(thing, " ").concat(expected[0], " or ").concat(expected[1]);
    } else {
      return "of ".concat(thing, " ").concat(expected[0]);
    }
  } else {
    return "of ".concat(thing, " ").concat(String(expected));
  }
} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith


function startsWith(str, search, pos) {
  return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;
} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith


function endsWith(str, search, this_len) {
  if (this_len === undefined || this_len > str.length) {
    this_len = str.length;
  }

  return str.substring(this_len - search.length, this_len) === search;
} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes


function includes(str, search, start) {
  if (typeof start !== 'number') {
    start = 0;
  }

  if (start + search.length > str.length) {
    return false;
  } else {
    return str.indexOf(search, start) !== -1;
  }
}

createErrorType('ERR_INVALID_OPT_VALUE', function (name, value) {
  return 'The value "' + value + '" is invalid for option "' + name + '"';
}, TypeError);
createErrorType('ERR_INVALID_ARG_TYPE', function (name, expected, actual) {
  // determiner: 'must be' or 'must not be'
  var determiner;

  if (typeof expected === 'string' && startsWith(expected, 'not ')) {
    determiner = 'must not be';
    expected = expected.replace(/^not /, '');
  } else {
    determiner = 'must be';
  }

  var msg;

  if (endsWith(name, ' argument')) {
    // For cases like 'first argument'
    msg = "The ".concat(name, " ").concat(determiner, " ").concat(oneOf(expected, 'type'));
  } else {
    var type = includes(name, '.') ? 'property' : 'argument';
    msg = "The \"".concat(name, "\" ").concat(type, " ").concat(determiner, " ").concat(oneOf(expected, 'type'));
  }

  msg += ". Received type ".concat(typeof actual);
  return msg;
}, TypeError);
createErrorType('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF');
createErrorType('ERR_METHOD_NOT_IMPLEMENTED', function (name) {
  return 'The ' + name + ' method is not implemented';
});
createErrorType('ERR_STREAM_PREMATURE_CLOSE', 'Premature close');
createErrorType('ERR_STREAM_DESTROYED', function (name) {
  return 'Cannot call ' + name + ' after a stream was destroyed';
});
createErrorType('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times');
createErrorType('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable');
createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end');
createErrorType('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError);
createErrorType('ERR_UNKNOWN_ENCODING', function (arg) {
  return 'Unknown encoding: ' + arg;
}, TypeError);
createErrorType('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event');
module.exports.codes = codes;

},{}],195:[function(require,module,exports){
(function (process){(function (){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a duplex stream is just a stream that is both readable and writable.
// Since JS doesn't have multiple prototypal inheritance, this class
// prototypally inherits from Readable, and then parasitically from
// Writable.
'use strict';
/*<replacement>*/

var objectKeys = Object.keys || function (obj) {
  var keys = [];

  for (var key in obj) {
    keys.push(key);
  }

  return keys;
};
/*</replacement>*/


module.exports = Duplex;

var Readable = require('./_stream_readable');

var Writable = require('./_stream_writable');

require('inherits')(Duplex, Readable);

{
  // Allow the keys array to be GC'ed.
  var keys = objectKeys(Writable.prototype);

  for (var v = 0; v < keys.length; v++) {
    var method = keys[v];
    if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method];
  }
}

function Duplex(options) {
  if (!(this instanceof Duplex)) return new Duplex(options);
  Readable.call(this, options);
  Writable.call(this, options);
  this.allowHalfOpen = true;

  if (options) {
    if (options.readable === false) this.readable = false;
    if (options.writable === false) this.writable = false;

    if (options.allowHalfOpen === false) {
      this.allowHalfOpen = false;
      this.once('end', onend);
    }
  }
}

Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    return this._writableState.highWaterMark;
  }
});
Object.defineProperty(Duplex.prototype, 'writableBuffer', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    return this._writableState && this._writableState.getBuffer();
  }
});
Object.defineProperty(Duplex.prototype, 'writableLength', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    return this._writableState.length;
  }
}); // the no-half-open enforcer

function onend() {
  // If the writable side ended, then we're ok.
  if (this._writableState.ended) return; // no more data can be written.
  // But allow more writes to happen in this tick.

  process.nextTick(onEndNT, this);
}

function onEndNT(self) {
  self.end();
}

Object.defineProperty(Duplex.prototype, 'destroyed', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    if (this._readableState === undefined || this._writableState === undefined) {
      return false;
    }

    return this._readableState.destroyed && this._writableState.destroyed;
  },
  set: function set(value) {
    // we ignore the value if the stream
    // has not been initialized yet
    if (this._readableState === undefined || this._writableState === undefined) {
      return;
    } // backward compatibility, the user is explicitly
    // managing destroyed


    this._readableState.destroyed = value;
    this._writableState.destroyed = value;
  }
});
}).call(this)}).call(this,require('_process'))
},{"./_stream_readable":197,"./_stream_writable":199,"_process":191,"inherits":172}],196:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a passthrough stream.
// basically just the most minimal sort of Transform stream.
// Every written chunk gets output as-is.
'use strict';

module.exports = PassThrough;

var Transform = require('./_stream_transform');

require('inherits')(PassThrough, Transform);

function PassThrough(options) {
  if (!(this instanceof PassThrough)) return new PassThrough(options);
  Transform.call(this, options);
}

PassThrough.prototype._transform = function (chunk, encoding, cb) {
  cb(null, chunk);
};
},{"./_stream_transform":198,"inherits":172}],197:[function(require,module,exports){
(function (process,global){(function (){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';

module.exports = Readable;
/*<replacement>*/

var Duplex;
/*</replacement>*/

Readable.ReadableState = ReadableState;
/*<replacement>*/

var EE = require('events').EventEmitter;

var EElistenerCount = function EElistenerCount(emitter, type) {
  return emitter.listeners(type).length;
};
/*</replacement>*/

/*<replacement>*/


var Stream = require('./internal/streams/stream');
/*</replacement>*/


var Buffer = require('buffer').Buffer;

var OurUint8Array = global.Uint8Array || function () {};

function _uint8ArrayToBuffer(chunk) {
  return Buffer.from(chunk);
}

function _isUint8Array(obj) {
  return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;
}
/*<replacement>*/


var debugUtil = require('util');

var debug;

if (debugUtil && debugUtil.debuglog) {
  debug = debugUtil.debuglog('stream');
} else {
  debug = function debug() {};
}
/*</replacement>*/


var BufferList = require('./internal/streams/buffer_list');

var destroyImpl = require('./internal/streams/destroy');

var _require = require('./internal/streams/state'),
    getHighWaterMark = _require.getHighWaterMark;

var _require$codes = require('../errors').codes,
    ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE,
    ERR_STREAM_PUSH_AFTER_EOF = _require$codes.ERR_STREAM_PUSH_AFTER_EOF,
    ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED,
    ERR_STREAM_UNSHIFT_AFTER_END_EVENT = _require$codes.ERR_STREAM_UNSHIFT_AFTER_END_EVENT; // Lazy loaded to improve the startup performance.


var StringDecoder;
var createReadableStreamAsyncIterator;
var from;

require('inherits')(Readable, Stream);

var errorOrDestroy = destroyImpl.errorOrDestroy;
var kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume'];

function prependListener(emitter, event, fn) {
  // Sadly this is not cacheable as some libraries bundle their own
  // event emitter implementation with them.
  if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); // This is a hack to make sure that our error handler is attached before any
  // userland ones.  NEVER DO THIS. This is here only because this code needs
  // to continue to work with older versions of Node.js that do not include
  // the prependListener() method. The goal is to eventually remove this hack.

  if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (Array.isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]];
}

function ReadableState(options, stream, isDuplex) {
  Duplex = Duplex || require('./_stream_duplex');
  options = options || {}; // Duplex streams are both readable and writable, but share
  // the same options object.
  // However, some cases require setting options to different
  // values for the readable and the writable sides of the duplex stream.
  // These options can be provided separately as readableXXX and writableXXX.

  if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag. Used to make read(n) ignore n and to
  // make all the buffer merging and length checks go away

  this.objectMode = !!options.objectMode;
  if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; // the point at which it stops calling _read() to fill the buffer
  // Note: 0 is a valid value, means "don't call _read preemptively ever"

  this.highWaterMark = getHighWaterMark(this, options, 'readableHighWaterMark', isDuplex); // A linked list is used to store data chunks instead of an array because the
  // linked list can remove elements from the beginning faster than
  // array.shift()

  this.buffer = new BufferList();
  this.length = 0;
  this.pipes = null;
  this.pipesCount = 0;
  this.flowing = null;
  this.ended = false;
  this.endEmitted = false;
  this.reading = false; // a flag to be able to tell if the event 'readable'/'data' is emitted
  // immediately, or on a later tick.  We set this to true at first, because
  // any actions that shouldn't happen until "later" should generally also
  // not happen before the first read call.

  this.sync = true; // whenever we return null, then we set a flag to say
  // that we're awaiting a 'readable' event emission.

  this.needReadable = false;
  this.emittedReadable = false;
  this.readableListening = false;
  this.resumeScheduled = false;
  this.paused = true; // Should close be emitted on destroy. Defaults to true.

  this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'end' (and potentially 'finish')

  this.autoDestroy = !!options.autoDestroy; // has it been destroyed

  this.destroyed = false; // Crypto is kind of old and crusty.  Historically, its default string
  // encoding is 'binary' so we have to make this configurable.
  // Everything else in the universe uses 'utf8', though.

  this.defaultEncoding = options.defaultEncoding || 'utf8'; // the number of writers that are awaiting a drain event in .pipe()s

  this.awaitDrain = 0; // if true, a maybeReadMore has been scheduled

  this.readingMore = false;
  this.decoder = null;
  this.encoding = null;

  if (options.encoding) {
    if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder;
    this.decoder = new StringDecoder(options.encoding);
    this.encoding = options.encoding;
  }
}

function Readable(options) {
  Duplex = Duplex || require('./_stream_duplex');
  if (!(this instanceof Readable)) return new Readable(options); // Checking for a Stream.Duplex instance is faster here instead of inside
  // the ReadableState constructor, at least with V8 6.5

  var isDuplex = this instanceof Duplex;
  this._readableState = new ReadableState(options, this, isDuplex); // legacy

  this.readable = true;

  if (options) {
    if (typeof options.read === 'function') this._read = options.read;
    if (typeof options.destroy === 'function') this._destroy = options.destroy;
  }

  Stream.call(this);
}

Object.defineProperty(Readable.prototype, 'destroyed', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    if (this._readableState === undefined) {
      return false;
    }

    return this._readableState.destroyed;
  },
  set: function set(value) {
    // we ignore the value if the stream
    // has not been initialized yet
    if (!this._readableState) {
      return;
    } // backward compatibility, the user is explicitly
    // managing destroyed


    this._readableState.destroyed = value;
  }
});
Readable.prototype.destroy = destroyImpl.destroy;
Readable.prototype._undestroy = destroyImpl.undestroy;

Readable.prototype._destroy = function (err, cb) {
  cb(err);
}; // Manually shove something into the read() buffer.
// This returns true if the highWaterMark has not been hit yet,
// similar to how Writable.write() returns true if you should
// write() some more.


Readable.prototype.push = function (chunk, encoding) {
  var state = this._readableState;
  var skipChunkCheck;

  if (!state.objectMode) {
    if (typeof chunk === 'string') {
      encoding = encoding || state.defaultEncoding;

      if (encoding !== state.encoding) {
        chunk = Buffer.from(chunk, encoding);
        encoding = '';
      }

      skipChunkCheck = true;
    }
  } else {
    skipChunkCheck = true;
  }

  return readableAddChunk(this, chunk, encoding, false, skipChunkCheck);
}; // Unshift should *always* be something directly out of read()


Readable.prototype.unshift = function (chunk) {
  return readableAddChunk(this, chunk, null, true, false);
};

function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) {
  debug('readableAddChunk', chunk);
  var state = stream._readableState;

  if (chunk === null) {
    state.reading = false;
    onEofChunk(stream, state);
  } else {
    var er;
    if (!skipChunkCheck) er = chunkInvalid(state, chunk);

    if (er) {
      errorOrDestroy(stream, er);
    } else if (state.objectMode || chunk && chunk.length > 0) {
      if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) {
        chunk = _uint8ArrayToBuffer(chunk);
      }

      if (addToFront) {
        if (state.endEmitted) errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT());else addChunk(stream, state, chunk, true);
      } else if (state.ended) {
        errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF());
      } else if (state.destroyed) {
        return false;
      } else {
        state.reading = false;

        if (state.decoder && !encoding) {
          chunk = state.decoder.write(chunk);
          if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state);
        } else {
          addChunk(stream, state, chunk, false);
        }
      }
    } else if (!addToFront) {
      state.reading = false;
      maybeReadMore(stream, state);
    }
  } // We can push more data if we are below the highWaterMark.
  // Also, if we have no data yet, we can stand some more bytes.
  // This is to work around cases where hwm=0, such as the repl.


  return !state.ended && (state.length < state.highWaterMark || state.length === 0);
}

function addChunk(stream, state, chunk, addToFront) {
  if (state.flowing && state.length === 0 && !state.sync) {
    state.awaitDrain = 0;
    stream.emit('data', chunk);
  } else {
    // update the buffer info.
    state.length += state.objectMode ? 1 : chunk.length;
    if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk);
    if (state.needReadable) emitReadable(stream);
  }

  maybeReadMore(stream, state);
}

function chunkInvalid(state, chunk) {
  var er;

  if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) {
    er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk);
  }

  return er;
}

Readable.prototype.isPaused = function () {
  return this._readableState.flowing === false;
}; // backwards compatibility.


Readable.prototype.setEncoding = function (enc) {
  if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder;
  var decoder = new StringDecoder(enc);
  this._readableState.decoder = decoder; // If setEncoding(null), decoder.encoding equals utf8

  this._readableState.encoding = this._readableState.decoder.encoding; // Iterate over current buffer to convert already stored Buffers:

  var p = this._readableState.buffer.head;
  var content = '';

  while (p !== null) {
    content += decoder.write(p.data);
    p = p.next;
  }

  this._readableState.buffer.clear();

  if (content !== '') this._readableState.buffer.push(content);
  this._readableState.length = content.length;
  return this;
}; // Don't raise the hwm > 1GB


var MAX_HWM = 0x40000000;

function computeNewHighWaterMark(n) {
  if (n >= MAX_HWM) {
    // TODO(ronag): Throw ERR_VALUE_OUT_OF_RANGE.
    n = MAX_HWM;
  } else {
    // Get the next highest power of 2 to prevent increasing hwm excessively in
    // tiny amounts
    n--;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    n++;
  }

  return n;
} // This function is designed to be inlinable, so please take care when making
// changes to the function body.


function howMuchToRead(n, state) {
  if (n <= 0 || state.length === 0 && state.ended) return 0;
  if (state.objectMode) return 1;

  if (n !== n) {
    // Only flow one buffer at a time
    if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length;
  } // If we're asking for more than the current hwm, then raise the hwm.


  if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n);
  if (n <= state.length) return n; // Don't have enough

  if (!state.ended) {
    state.needReadable = true;
    return 0;
  }

  return state.length;
} // you can override either this method, or the async _read(n) below.


Readable.prototype.read = function (n) {
  debug('read', n);
  n = parseInt(n, 10);
  var state = this._readableState;
  var nOrig = n;
  if (n !== 0) state.emittedReadable = false; // if we're doing read(0) to trigger a readable event, but we
  // already have a bunch of data in the buffer, then just trigger
  // the 'readable' event and move on.

  if (n === 0 && state.needReadable && ((state.highWaterMark !== 0 ? state.length >= state.highWaterMark : state.length > 0) || state.ended)) {
    debug('read: emitReadable', state.length, state.ended);
    if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this);
    return null;
  }

  n = howMuchToRead(n, state); // if we've ended, and we're now clear, then finish it up.

  if (n === 0 && state.ended) {
    if (state.length === 0) endReadable(this);
    return null;
  } // All the actual chunk generation logic needs to be
  // *below* the call to _read.  The reason is that in certain
  // synthetic stream cases, such as passthrough streams, _read
  // may be a completely synchronous operation which may change
  // the state of the read buffer, providing enough data when
  // before there was *not* enough.
  //
  // So, the steps are:
  // 1. Figure out what the state of things will be after we do
  // a read from the buffer.
  //
  // 2. If that resulting state will trigger a _read, then call _read.
  // Note that this may be asynchronous, or synchronous.  Yes, it is
  // deeply ugly to write APIs this way, but that still doesn't mean
  // that the Readable class should behave improperly, as streams are
  // designed to be sync/async agnostic.
  // Take note if the _read call is sync or async (ie, if the read call
  // has returned yet), so that we know whether or not it's safe to emit
  // 'readable' etc.
  //
  // 3. Actually pull the requested chunks out of the buffer and return.
  // if we need a readable event, then we need to do some reading.


  var doRead = state.needReadable;
  debug('need readable', doRead); // if we currently have less than the highWaterMark, then also read some

  if (state.length === 0 || state.length - n < state.highWaterMark) {
    doRead = true;
    debug('length less than watermark', doRead);
  } // however, if we've ended, then there's no point, and if we're already
  // reading, then it's unnecessary.


  if (state.ended || state.reading) {
    doRead = false;
    debug('reading or ended', doRead);
  } else if (doRead) {
    debug('do read');
    state.reading = true;
    state.sync = true; // if the length is currently zero, then we *need* a readable event.

    if (state.length === 0) state.needReadable = true; // call internal read method

    this._read(state.highWaterMark);

    state.sync = false; // If _read pushed data synchronously, then `reading` will be false,
    // and we need to re-evaluate how much data we can return to the user.

    if (!state.reading) n = howMuchToRead(nOrig, state);
  }

  var ret;
  if (n > 0) ret = fromList(n, state);else ret = null;

  if (ret === null) {
    state.needReadable = state.length <= state.highWaterMark;
    n = 0;
  } else {
    state.length -= n;
    state.awaitDrain = 0;
  }

  if (state.length === 0) {
    // If we have nothing in the buffer, then we want to know
    // as soon as we *do* get something into the buffer.
    if (!state.ended) state.needReadable = true; // If we tried to read() past the EOF, then emit end on the next tick.

    if (nOrig !== n && state.ended) endReadable(this);
  }

  if (ret !== null) this.emit('data', ret);
  return ret;
};

function onEofChunk(stream, state) {
  debug('onEofChunk');
  if (state.ended) return;

  if (state.decoder) {
    var chunk = state.decoder.end();

    if (chunk && chunk.length) {
      state.buffer.push(chunk);
      state.length += state.objectMode ? 1 : chunk.length;
    }
  }

  state.ended = true;

  if (state.sync) {
    // if we are sync, wait until next tick to emit the data.
    // Otherwise we risk emitting data in the flow()
    // the readable code triggers during a read() call
    emitReadable(stream);
  } else {
    // emit 'readable' now to make sure it gets picked up.
    state.needReadable = false;

    if (!state.emittedReadable) {
      state.emittedReadable = true;
      emitReadable_(stream);
    }
  }
} // Don't emit readable right away in sync mode, because this can trigger
// another read() call => stack overflow.  This way, it might trigger
// a nextTick recursion warning, but that's not so bad.


function emitReadable(stream) {
  var state = stream._readableState;
  debug('emitReadable', state.needReadable, state.emittedReadable);
  state.needReadable = false;

  if (!state.emittedReadable) {
    debug('emitReadable', state.flowing);
    state.emittedReadable = true;
    process.nextTick(emitReadable_, stream);
  }
}

function emitReadable_(stream) {
  var state = stream._readableState;
  debug('emitReadable_', state.destroyed, state.length, state.ended);

  if (!state.destroyed && (state.length || state.ended)) {
    stream.emit('readable');
    state.emittedReadable = false;
  } // The stream needs another readable event if
  // 1. It is not flowing, as the flow mechanism will take
  //    care of it.
  // 2. It is not ended.
  // 3. It is below the highWaterMark, so we can schedule
  //    another readable later.


  state.needReadable = !state.flowing && !state.ended && state.length <= state.highWaterMark;
  flow(stream);
} // at this point, the user has presumably seen the 'readable' event,
// and called read() to consume some data.  that may have triggered
// in turn another _read(n) call, in which case reading = true if
// it's in progress.
// However, if we're not ended, or reading, and the length < hwm,
// then go ahead and try to read some more preemptively.


function maybeReadMore(stream, state) {
  if (!state.readingMore) {
    state.readingMore = true;
    process.nextTick(maybeReadMore_, stream, state);
  }
}

function maybeReadMore_(stream, state) {
  // Attempt to read more data if we should.
  //
  // The conditions for reading more data are (one of):
  // - Not enough data buffered (state.length < state.highWaterMark). The loop
  //   is responsible for filling the buffer with enough data if such data
  //   is available. If highWaterMark is 0 and we are not in the flowing mode
  //   we should _not_ attempt to buffer any extra data. We'll get more data
  //   when the stream consumer calls read() instead.
  // - No data in the buffer, and the stream is in flowing mode. In this mode
  //   the loop below is responsible for ensuring read() is called. Failing to
  //   call read here would abort the flow and there's no other mechanism for
  //   continuing the flow if the stream consumer has just subscribed to the
  //   'data' event.
  //
  // In addition to the above conditions to keep reading data, the following
  // conditions prevent the data from being read:
  // - The stream has ended (state.ended).
  // - There is already a pending 'read' operation (state.reading). This is a
  //   case where the the stream has called the implementation defined _read()
  //   method, but they are processing the call asynchronously and have _not_
  //   called push() with new data. In this case we skip performing more
  //   read()s. The execution ends in this method again after the _read() ends
  //   up calling push() with more data.
  while (!state.reading && !state.ended && (state.length < state.highWaterMark || state.flowing && state.length === 0)) {
    var len = state.length;
    debug('maybeReadMore read 0');
    stream.read(0);
    if (len === state.length) // didn't get any data, stop spinning.
      break;
  }

  state.readingMore = false;
} // abstract method.  to be overridden in specific implementation classes.
// call cb(er, data) where data is <= n in length.
// for virtual (non-string, non-buffer) streams, "length" is somewhat
// arbitrary, and perhaps not very meaningful.


Readable.prototype._read = function (n) {
  errorOrDestroy(this, new ERR_METHOD_NOT_IMPLEMENTED('_read()'));
};

Readable.prototype.pipe = function (dest, pipeOpts) {
  var src = this;
  var state = this._readableState;

  switch (state.pipesCount) {
    case 0:
      state.pipes = dest;
      break;

    case 1:
      state.pipes = [state.pipes, dest];
      break;

    default:
      state.pipes.push(dest);
      break;
  }

  state.pipesCount += 1;
  debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts);
  var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr;
  var endFn = doEnd ? onend : unpipe;
  if (state.endEmitted) process.nextTick(endFn);else src.once('end', endFn);
  dest.on('unpipe', onunpipe);

  function onunpipe(readable, unpipeInfo) {
    debug('onunpipe');

    if (readable === src) {
      if (unpipeInfo && unpipeInfo.hasUnpiped === false) {
        unpipeInfo.hasUnpiped = true;
        cleanup();
      }
    }
  }

  function onend() {
    debug('onend');
    dest.end();
  } // when the dest drains, it reduces the awaitDrain counter
  // on the source.  This would be more elegant with a .once()
  // handler in flow(), but adding and removing repeatedly is
  // too slow.


  var ondrain = pipeOnDrain(src);
  dest.on('drain', ondrain);
  var cleanedUp = false;

  function cleanup() {
    debug('cleanup'); // cleanup event handlers once the pipe is broken

    dest.removeListener('close', onclose);
    dest.removeListener('finish', onfinish);
    dest.removeListener('drain', ondrain);
    dest.removeListener('error', onerror);
    dest.removeListener('unpipe', onunpipe);
    src.removeListener('end', onend);
    src.removeListener('end', unpipe);
    src.removeListener('data', ondata);
    cleanedUp = true; // if the reader is waiting for a drain event from this
    // specific writer, then it would cause it to never start
    // flowing again.
    // So, if this is awaiting a drain, then we just call it now.
    // If we don't know, then assume that we are waiting for one.

    if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain();
  }

  src.on('data', ondata);

  function ondata(chunk) {
    debug('ondata');
    var ret = dest.write(chunk);
    debug('dest.write', ret);

    if (ret === false) {
      // If the user unpiped during `dest.write()`, it is possible
      // to get stuck in a permanently paused state if that write
      // also returned false.
      // => Check whether `dest` is still a piping destination.
      if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) {
        debug('false write response, pause', state.awaitDrain);
        state.awaitDrain++;
      }

      src.pause();
    }
  } // if the dest has an error, then stop piping into it.
  // however, don't suppress the throwing behavior for this.


  function onerror(er) {
    debug('onerror', er);
    unpipe();
    dest.removeListener('error', onerror);
    if (EElistenerCount(dest, 'error') === 0) errorOrDestroy(dest, er);
  } // Make sure our error handler is attached before userland ones.


  prependListener(dest, 'error', onerror); // Both close and finish should trigger unpipe, but only once.

  function onclose() {
    dest.removeListener('finish', onfinish);
    unpipe();
  }

  dest.once('close', onclose);

  function onfinish() {
    debug('onfinish');
    dest.removeListener('close', onclose);
    unpipe();
  }

  dest.once('finish', onfinish);

  function unpipe() {
    debug('unpipe');
    src.unpipe(dest);
  } // tell the dest that it's being piped to


  dest.emit('pipe', src); // start the flow if it hasn't been started already.

  if (!state.flowing) {
    debug('pipe resume');
    src.resume();
  }

  return dest;
};

function pipeOnDrain(src) {
  return function pipeOnDrainFunctionResult() {
    var state = src._readableState;
    debug('pipeOnDrain', state.awaitDrain);
    if (state.awaitDrain) state.awaitDrain--;

    if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) {
      state.flowing = true;
      flow(src);
    }
  };
}

Readable.prototype.unpipe = function (dest) {
  var state = this._readableState;
  var unpipeInfo = {
    hasUnpiped: false
  }; // if we're not piping anywhere, then do nothing.

  if (state.pipesCount === 0) return this; // just one destination.  most common case.

  if (state.pipesCount === 1) {
    // passed in one, but it's not the right one.
    if (dest && dest !== state.pipes) return this;
    if (!dest) dest = state.pipes; // got a match.

    state.pipes = null;
    state.pipesCount = 0;
    state.flowing = false;
    if (dest) dest.emit('unpipe', this, unpipeInfo);
    return this;
  } // slow case. multiple pipe destinations.


  if (!dest) {
    // remove all.
    var dests = state.pipes;
    var len = state.pipesCount;
    state.pipes = null;
    state.pipesCount = 0;
    state.flowing = false;

    for (var i = 0; i < len; i++) {
      dests[i].emit('unpipe', this, {
        hasUnpiped: false
      });
    }

    return this;
  } // try to find the right one.


  var index = indexOf(state.pipes, dest);
  if (index === -1) return this;
  state.pipes.splice(index, 1);
  state.pipesCount -= 1;
  if (state.pipesCount === 1) state.pipes = state.pipes[0];
  dest.emit('unpipe', this, unpipeInfo);
  return this;
}; // set up data events if they are asked for
// Ensure readable listeners eventually get something


Readable.prototype.on = function (ev, fn) {
  var res = Stream.prototype.on.call(this, ev, fn);
  var state = this._readableState;

  if (ev === 'data') {
    // update readableListening so that resume() may be a no-op
    // a few lines down. This is needed to support once('readable').
    state.readableListening = this.listenerCount('readable') > 0; // Try start flowing on next tick if stream isn't explicitly paused

    if (state.flowing !== false) this.resume();
  } else if (ev === 'readable') {
    if (!state.endEmitted && !state.readableListening) {
      state.readableListening = state.needReadable = true;
      state.flowing = false;
      state.emittedReadable = false;
      debug('on readable', state.length, state.reading);

      if (state.length) {
        emitReadable(this);
      } else if (!state.reading) {
        process.nextTick(nReadingNextTick, this);
      }
    }
  }

  return res;
};

Readable.prototype.addListener = Readable.prototype.on;

Readable.prototype.removeListener = function (ev, fn) {
  var res = Stream.prototype.removeListener.call(this, ev, fn);

  if (ev === 'readable') {
    // We need to check if there is someone still listening to
    // readable and reset the state. However this needs to happen
    // after readable has been emitted but before I/O (nextTick) to
    // support once('readable', fn) cycles. This means that calling
    // resume within the same tick will have no
    // effect.
    process.nextTick(updateReadableListening, this);
  }

  return res;
};

Readable.prototype.removeAllListeners = function (ev) {
  var res = Stream.prototype.removeAllListeners.apply(this, arguments);

  if (ev === 'readable' || ev === undefined) {
    // We need to check if there is someone still listening to
    // readable and reset the state. However this needs to happen
    // after readable has been emitted but before I/O (nextTick) to
    // support once('readable', fn) cycles. This means that calling
    // resume within the same tick will have no
    // effect.
    process.nextTick(updateReadableListening, this);
  }

  return res;
};

function updateReadableListening(self) {
  var state = self._readableState;
  state.readableListening = self.listenerCount('readable') > 0;

  if (state.resumeScheduled && !state.paused) {
    // flowing needs to be set to true now, otherwise
    // the upcoming resume will not flow.
    state.flowing = true; // crude way to check if we should resume
  } else if (self.listenerCount('data') > 0) {
    self.resume();
  }
}

function nReadingNextTick(self) {
  debug('readable nexttick read 0');
  self.read(0);
} // pause() and resume() are remnants of the legacy readable stream API
// If the user uses them, then switch into old mode.


Readable.prototype.resume = function () {
  var state = this._readableState;

  if (!state.flowing) {
    debug('resume'); // we flow only if there is no one listening
    // for readable, but we still have to call
    // resume()

    state.flowing = !state.readableListening;
    resume(this, state);
  }

  state.paused = false;
  return this;
};

function resume(stream, state) {
  if (!state.resumeScheduled) {
    state.resumeScheduled = true;
    process.nextTick(resume_, stream, state);
  }
}

function resume_(stream, state) {
  debug('resume', state.reading);

  if (!state.reading) {
    stream.read(0);
  }

  state.resumeScheduled = false;
  stream.emit('resume');
  flow(stream);
  if (state.flowing && !state.reading) stream.read(0);
}

Readable.prototype.pause = function () {
  debug('call pause flowing=%j', this._readableState.flowing);

  if (this._readableState.flowing !== false) {
    debug('pause');
    this._readableState.flowing = false;
    this.emit('pause');
  }

  this._readableState.paused = true;
  return this;
};

function flow(stream) {
  var state = stream._readableState;
  debug('flow', state.flowing);

  while (state.flowing && stream.read() !== null) {
    ;
  }
} // wrap an old-style stream as the async data source.
// This is *not* part of the readable stream interface.
// It is an ugly unfortunate mess of history.


Readable.prototype.wrap = function (stream) {
  var _this = this;

  var state = this._readableState;
  var paused = false;
  stream.on('end', function () {
    debug('wrapped end');

    if (state.decoder && !state.ended) {
      var chunk = state.decoder.end();
      if (chunk && chunk.length) _this.push(chunk);
    }

    _this.push(null);
  });
  stream.on('data', function (chunk) {
    debug('wrapped data');
    if (state.decoder) chunk = state.decoder.write(chunk); // don't skip over falsy values in objectMode

    if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return;

    var ret = _this.push(chunk);

    if (!ret) {
      paused = true;
      stream.pause();
    }
  }); // proxy all the other methods.
  // important when wrapping filters and duplexes.

  for (var i in stream) {
    if (this[i] === undefined && typeof stream[i] === 'function') {
      this[i] = function methodWrap(method) {
        return function methodWrapReturnFunction() {
          return stream[method].apply(stream, arguments);
        };
      }(i);
    }
  } // proxy certain important events.


  for (var n = 0; n < kProxyEvents.length; n++) {
    stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n]));
  } // when we try to consume some more bytes, simply unpause the
  // underlying stream.


  this._read = function (n) {
    debug('wrapped _read', n);

    if (paused) {
      paused = false;
      stream.resume();
    }
  };

  return this;
};

if (typeof Symbol === 'function') {
  Readable.prototype[Symbol.asyncIterator] = function () {
    if (createReadableStreamAsyncIterator === undefined) {
      createReadableStreamAsyncIterator = require('./internal/streams/async_iterator');
    }

    return createReadableStreamAsyncIterator(this);
  };
}

Object.defineProperty(Readable.prototype, 'readableHighWaterMark', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    return this._readableState.highWaterMark;
  }
});
Object.defineProperty(Readable.prototype, 'readableBuffer', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    return this._readableState && this._readableState.buffer;
  }
});
Object.defineProperty(Readable.prototype, 'readableFlowing', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    return this._readableState.flowing;
  },
  set: function set(state) {
    if (this._readableState) {
      this._readableState.flowing = state;
    }
  }
}); // exposed for testing purposes only.

Readable._fromList = fromList;
Object.defineProperty(Readable.prototype, 'readableLength', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    return this._readableState.length;
  }
}); // Pluck off n bytes from an array of buffers.
// Length is the combined lengths of all the buffers in the list.
// This function is designed to be inlinable, so please take care when making
// changes to the function body.

function fromList(n, state) {
  // nothing buffered
  if (state.length === 0) return null;
  var ret;
  if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) {
    // read it all, truncate the list
    if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.first();else ret = state.buffer.concat(state.length);
    state.buffer.clear();
  } else {
    // read part of list
    ret = state.buffer.consume(n, state.decoder);
  }
  return ret;
}

function endReadable(stream) {
  var state = stream._readableState;
  debug('endReadable', state.endEmitted);

  if (!state.endEmitted) {
    state.ended = true;
    process.nextTick(endReadableNT, state, stream);
  }
}

function endReadableNT(state, stream) {
  debug('endReadableNT', state.endEmitted, state.length); // Check that we didn't get one last unshift.

  if (!state.endEmitted && state.length === 0) {
    state.endEmitted = true;
    stream.readable = false;
    stream.emit('end');

    if (state.autoDestroy) {
      // In case of duplex streams we need a way to detect
      // if the writable side is ready for autoDestroy as well
      var wState = stream._writableState;

      if (!wState || wState.autoDestroy && wState.finished) {
        stream.destroy();
      }
    }
  }
}

if (typeof Symbol === 'function') {
  Readable.from = function (iterable, opts) {
    if (from === undefined) {
      from = require('./internal/streams/from');
    }

    return from(Readable, iterable, opts);
  };
}

function indexOf(xs, x) {
  for (var i = 0, l = xs.length; i < l; i++) {
    if (xs[i] === x) return i;
  }

  return -1;
}
}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"../errors":194,"./_stream_duplex":195,"./internal/streams/async_iterator":200,"./internal/streams/buffer_list":201,"./internal/streams/destroy":202,"./internal/streams/from":204,"./internal/streams/state":206,"./internal/streams/stream":207,"_process":191,"buffer":159,"events":163,"inherits":172,"string_decoder/":208,"util":156}],198:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a transform stream is a readable/writable stream where you do
// something with the data.  Sometimes it's called a "filter",
// but that's not a great name for it, since that implies a thing where
// some bits pass through, and others are simply ignored.  (That would
// be a valid example of a transform, of course.)
//
// While the output is causally related to the input, it's not a
// necessarily symmetric or synchronous transformation.  For example,
// a zlib stream might take multiple plain-text writes(), and then
// emit a single compressed chunk some time in the future.
//
// Here's how this works:
//
// The Transform stream has all the aspects of the readable and writable
// stream classes.  When you write(chunk), that calls _write(chunk,cb)
// internally, and returns false if there's a lot of pending writes
// buffered up.  When you call read(), that calls _read(n) until
// there's enough pending readable data buffered up.
//
// In a transform stream, the written data is placed in a buffer.  When
// _read(n) is called, it transforms the queued up data, calling the
// buffered _write cb's as it consumes chunks.  If consuming a single
// written chunk would result in multiple output chunks, then the first
// outputted bit calls the readcb, and subsequent chunks just go into
// the read buffer, and will cause it to emit 'readable' if necessary.
//
// This way, back-pressure is actually determined by the reading side,
// since _read has to be called to start processing a new chunk.  However,
// a pathological inflate type of transform can cause excessive buffering
// here.  For example, imagine a stream where every byte of input is
// interpreted as an integer from 0-255, and then results in that many
// bytes of output.  Writing the 4 bytes {ff,ff,ff,ff} would result in
// 1kb of data being output.  In this case, you could write a very small
// amount of input, and end up with a very large amount of output.  In
// such a pathological inflating mechanism, there'd be no way to tell
// the system to stop doing the transform.  A single 4MB write could
// cause the system to run out of memory.
//
// However, even in such a pathological case, only a single written chunk
// would be consumed, and then the rest would wait (un-transformed) until
// the results of the previous transformed chunk were consumed.
'use strict';

module.exports = Transform;

var _require$codes = require('../errors').codes,
    ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED,
    ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK,
    ERR_TRANSFORM_ALREADY_TRANSFORMING = _require$codes.ERR_TRANSFORM_ALREADY_TRANSFORMING,
    ERR_TRANSFORM_WITH_LENGTH_0 = _require$codes.ERR_TRANSFORM_WITH_LENGTH_0;

var Duplex = require('./_stream_duplex');

require('inherits')(Transform, Duplex);

function afterTransform(er, data) {
  var ts = this._transformState;
  ts.transforming = false;
  var cb = ts.writecb;

  if (cb === null) {
    return this.emit('error', new ERR_MULTIPLE_CALLBACK());
  }

  ts.writechunk = null;
  ts.writecb = null;
  if (data != null) // single equals check for both `null` and `undefined`
    this.push(data);
  cb(er);
  var rs = this._readableState;
  rs.reading = false;

  if (rs.needReadable || rs.length < rs.highWaterMark) {
    this._read(rs.highWaterMark);
  }
}

function Transform(options) {
  if (!(this instanceof Transform)) return new Transform(options);
  Duplex.call(this, options);
  this._transformState = {
    afterTransform: afterTransform.bind(this),
    needTransform: false,
    transforming: false,
    writecb: null,
    writechunk: null,
    writeencoding: null
  }; // start out asking for a readable event once data is transformed.

  this._readableState.needReadable = true; // we have implemented the _read method, and done the other things
  // that Readable wants before the first _read call, so unset the
  // sync guard flag.

  this._readableState.sync = false;

  if (options) {
    if (typeof options.transform === 'function') this._transform = options.transform;
    if (typeof options.flush === 'function') this._flush = options.flush;
  } // When the writable side finishes, then flush out anything remaining.


  this.on('prefinish', prefinish);
}

function prefinish() {
  var _this = this;

  if (typeof this._flush === 'function' && !this._readableState.destroyed) {
    this._flush(function (er, data) {
      done(_this, er, data);
    });
  } else {
    done(this, null, null);
  }
}

Transform.prototype.push = function (chunk, encoding) {
  this._transformState.needTransform = false;
  return Duplex.prototype.push.call(this, chunk, encoding);
}; // This is the part where you do stuff!
// override this function in implementation classes.
// 'chunk' is an input chunk.
//
// Call `push(newChunk)` to pass along transformed output
// to the readable side.  You may call 'push' zero or more times.
//
// Call `cb(err)` when you are done with this chunk.  If you pass
// an error, then that'll put the hurt on the whole operation.  If you
// never call cb(), then you'll never get another chunk.


Transform.prototype._transform = function (chunk, encoding, cb) {
  cb(new ERR_METHOD_NOT_IMPLEMENTED('_transform()'));
};

Transform.prototype._write = function (chunk, encoding, cb) {
  var ts = this._transformState;
  ts.writecb = cb;
  ts.writechunk = chunk;
  ts.writeencoding = encoding;

  if (!ts.transforming) {
    var rs = this._readableState;
    if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark);
  }
}; // Doesn't matter what the args are here.
// _transform does all the work.
// That we got here means that the readable side wants more data.


Transform.prototype._read = function (n) {
  var ts = this._transformState;

  if (ts.writechunk !== null && !ts.transforming) {
    ts.transforming = true;

    this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
  } else {
    // mark that we need a transform, so that any data that comes in
    // will get processed, now that we've asked for it.
    ts.needTransform = true;
  }
};

Transform.prototype._destroy = function (err, cb) {
  Duplex.prototype._destroy.call(this, err, function (err2) {
    cb(err2);
  });
};

function done(stream, er, data) {
  if (er) return stream.emit('error', er);
  if (data != null) // single equals check for both `null` and `undefined`
    stream.push(data); // TODO(BridgeAR): Write a test for these two error cases
  // if there's nothing in the write buffer, then that means
  // that nothing more will ever be provided

  if (stream._writableState.length) throw new ERR_TRANSFORM_WITH_LENGTH_0();
  if (stream._transformState.transforming) throw new ERR_TRANSFORM_ALREADY_TRANSFORMING();
  return stream.push(null);
}
},{"../errors":194,"./_stream_duplex":195,"inherits":172}],199:[function(require,module,exports){
(function (process,global){(function (){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// A bit simpler than readable streams.
// Implement an async ._write(chunk, encoding, cb), and it'll handle all
// the drain event emission and buffering.
'use strict';

module.exports = Writable;
/* <replacement> */

function WriteReq(chunk, encoding, cb) {
  this.chunk = chunk;
  this.encoding = encoding;
  this.callback = cb;
  this.next = null;
} // It seems a linked list but it is not
// there will be only 2 of these for each stream


function CorkedRequest(state) {
  var _this = this;

  this.next = null;
  this.entry = null;

  this.finish = function () {
    onCorkedFinish(_this, state);
  };
}
/* </replacement> */

/*<replacement>*/


var Duplex;
/*</replacement>*/

Writable.WritableState = WritableState;
/*<replacement>*/

var internalUtil = {
  deprecate: require('util-deprecate')
};
/*</replacement>*/

/*<replacement>*/

var Stream = require('./internal/streams/stream');
/*</replacement>*/


var Buffer = require('buffer').Buffer;

var OurUint8Array = global.Uint8Array || function () {};

function _uint8ArrayToBuffer(chunk) {
  return Buffer.from(chunk);
}

function _isUint8Array(obj) {
  return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;
}

var destroyImpl = require('./internal/streams/destroy');

var _require = require('./internal/streams/state'),
    getHighWaterMark = _require.getHighWaterMark;

var _require$codes = require('../errors').codes,
    ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE,
    ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED,
    ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK,
    ERR_STREAM_CANNOT_PIPE = _require$codes.ERR_STREAM_CANNOT_PIPE,
    ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED,
    ERR_STREAM_NULL_VALUES = _require$codes.ERR_STREAM_NULL_VALUES,
    ERR_STREAM_WRITE_AFTER_END = _require$codes.ERR_STREAM_WRITE_AFTER_END,
    ERR_UNKNOWN_ENCODING = _require$codes.ERR_UNKNOWN_ENCODING;

var errorOrDestroy = destroyImpl.errorOrDestroy;

require('inherits')(Writable, Stream);

function nop() {}

function WritableState(options, stream, isDuplex) {
  Duplex = Duplex || require('./_stream_duplex');
  options = options || {}; // Duplex streams are both readable and writable, but share
  // the same options object.
  // However, some cases require setting options to different
  // values for the readable and the writable sides of the duplex stream,
  // e.g. options.readableObjectMode vs. options.writableObjectMode, etc.

  if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag to indicate whether or not this stream
  // contains buffers or objects.

  this.objectMode = !!options.objectMode;
  if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; // the point at which write() starts returning false
  // Note: 0 is a valid value, means that we always return false if
  // the entire buffer is not flushed immediately on write()

  this.highWaterMark = getHighWaterMark(this, options, 'writableHighWaterMark', isDuplex); // if _final has been called

  this.finalCalled = false; // drain event flag.

  this.needDrain = false; // at the start of calling end()

  this.ending = false; // when end() has been called, and returned

  this.ended = false; // when 'finish' is emitted

  this.finished = false; // has it been destroyed

  this.destroyed = false; // should we decode strings into buffers before passing to _write?
  // this is here so that some node-core streams can optimize string
  // handling at a lower level.

  var noDecode = options.decodeStrings === false;
  this.decodeStrings = !noDecode; // Crypto is kind of old and crusty.  Historically, its default string
  // encoding is 'binary' so we have to make this configurable.
  // Everything else in the universe uses 'utf8', though.

  this.defaultEncoding = options.defaultEncoding || 'utf8'; // not an actual buffer we keep track of, but a measurement
  // of how much we're waiting to get pushed to some underlying
  // socket or file.

  this.length = 0; // a flag to see when we're in the middle of a write.

  this.writing = false; // when true all writes will be buffered until .uncork() call

  this.corked = 0; // a flag to be able to tell if the onwrite cb is called immediately,
  // or on a later tick.  We set this to true at first, because any
  // actions that shouldn't happen until "later" should generally also
  // not happen before the first write call.

  this.sync = true; // a flag to know if we're processing previously buffered items, which
  // may call the _write() callback in the same tick, so that we don't
  // end up in an overlapped onwrite situation.

  this.bufferProcessing = false; // the callback that's passed to _write(chunk,cb)

  this.onwrite = function (er) {
    onwrite(stream, er);
  }; // the callback that the user supplies to write(chunk,encoding,cb)


  this.writecb = null; // the amount that is being written when _write is called.

  this.writelen = 0;
  this.bufferedRequest = null;
  this.lastBufferedRequest = null; // number of pending user-supplied write callbacks
  // this must be 0 before 'finish' can be emitted

  this.pendingcb = 0; // emit prefinish if the only thing we're waiting for is _write cbs
  // This is relevant for synchronous Transform streams

  this.prefinished = false; // True if the error was already emitted and should not be thrown again

  this.errorEmitted = false; // Should close be emitted on destroy. Defaults to true.

  this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'finish' (and potentially 'end')

  this.autoDestroy = !!options.autoDestroy; // count buffered requests

  this.bufferedRequestCount = 0; // allocate the first CorkedRequest, there is always
  // one allocated and free to use, and we maintain at most two

  this.corkedRequestsFree = new CorkedRequest(this);
}

WritableState.prototype.getBuffer = function getBuffer() {
  var current = this.bufferedRequest;
  var out = [];

  while (current) {
    out.push(current);
    current = current.next;
  }

  return out;
};

(function () {
  try {
    Object.defineProperty(WritableState.prototype, 'buffer', {
      get: internalUtil.deprecate(function writableStateBufferGetter() {
        return this.getBuffer();
      }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003')
    });
  } catch (_) {}
})(); // Test _writableState for inheritance to account for Duplex streams,
// whose prototype chain only points to Readable.


var realHasInstance;

if (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') {
  realHasInstance = Function.prototype[Symbol.hasInstance];
  Object.defineProperty(Writable, Symbol.hasInstance, {
    value: function value(object) {
      if (realHasInstance.call(this, object)) return true;
      if (this !== Writable) return false;
      return object && object._writableState instanceof WritableState;
    }
  });
} else {
  realHasInstance = function realHasInstance(object) {
    return object instanceof this;
  };
}

function Writable(options) {
  Duplex = Duplex || require('./_stream_duplex'); // Writable ctor is applied to Duplexes, too.
  // `realHasInstance` is necessary because using plain `instanceof`
  // would return false, as no `_writableState` property is attached.
  // Trying to use the custom `instanceof` for Writable here will also break the
  // Node.js LazyTransform implementation, which has a non-trivial getter for
  // `_writableState` that would lead to infinite recursion.
  // Checking for a Stream.Duplex instance is faster here instead of inside
  // the WritableState constructor, at least with V8 6.5

  var isDuplex = this instanceof Duplex;
  if (!isDuplex && !realHasInstance.call(Writable, this)) return new Writable(options);
  this._writableState = new WritableState(options, this, isDuplex); // legacy.

  this.writable = true;

  if (options) {
    if (typeof options.write === 'function') this._write = options.write;
    if (typeof options.writev === 'function') this._writev = options.writev;
    if (typeof options.destroy === 'function') this._destroy = options.destroy;
    if (typeof options.final === 'function') this._final = options.final;
  }

  Stream.call(this);
} // Otherwise people can pipe Writable streams, which is just wrong.


Writable.prototype.pipe = function () {
  errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE());
};

function writeAfterEnd(stream, cb) {
  var er = new ERR_STREAM_WRITE_AFTER_END(); // TODO: defer error events consistently everywhere, not just the cb

  errorOrDestroy(stream, er);
  process.nextTick(cb, er);
} // Checks that a user-supplied chunk is valid, especially for the particular
// mode the stream is in. Currently this means that `null` is never accepted
// and undefined/non-string values are only allowed in object mode.


function validChunk(stream, state, chunk, cb) {
  var er;

  if (chunk === null) {
    er = new ERR_STREAM_NULL_VALUES();
  } else if (typeof chunk !== 'string' && !state.objectMode) {
    er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk);
  }

  if (er) {
    errorOrDestroy(stream, er);
    process.nextTick(cb, er);
    return false;
  }

  return true;
}

Writable.prototype.write = function (chunk, encoding, cb) {
  var state = this._writableState;
  var ret = false;

  var isBuf = !state.objectMode && _isUint8Array(chunk);

  if (isBuf && !Buffer.isBuffer(chunk)) {
    chunk = _uint8ArrayToBuffer(chunk);
  }

  if (typeof encoding === 'function') {
    cb = encoding;
    encoding = null;
  }

  if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding;
  if (typeof cb !== 'function') cb = nop;
  if (state.ending) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) {
    state.pendingcb++;
    ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb);
  }
  return ret;
};

Writable.prototype.cork = function () {
  this._writableState.corked++;
};

Writable.prototype.uncork = function () {
  var state = this._writableState;

  if (state.corked) {
    state.corked--;
    if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state);
  }
};

Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {
  // node::ParseEncoding() requires lower case.
  if (typeof encoding === 'string') encoding = encoding.toLowerCase();
  if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new ERR_UNKNOWN_ENCODING(encoding);
  this._writableState.defaultEncoding = encoding;
  return this;
};

Object.defineProperty(Writable.prototype, 'writableBuffer', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    return this._writableState && this._writableState.getBuffer();
  }
});

function decodeChunk(state, chunk, encoding) {
  if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') {
    chunk = Buffer.from(chunk, encoding);
  }

  return chunk;
}

Object.defineProperty(Writable.prototype, 'writableHighWaterMark', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    return this._writableState.highWaterMark;
  }
}); // if we're already writing something, then just put this
// in the queue, and wait our turn.  Otherwise, call _write
// If we return false, then we need a drain event, so set that flag.

function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) {
  if (!isBuf) {
    var newChunk = decodeChunk(state, chunk, encoding);

    if (chunk !== newChunk) {
      isBuf = true;
      encoding = 'buffer';
      chunk = newChunk;
    }
  }

  var len = state.objectMode ? 1 : chunk.length;
  state.length += len;
  var ret = state.length < state.highWaterMark; // we must ensure that previous needDrain will not be reset to false.

  if (!ret) state.needDrain = true;

  if (state.writing || state.corked) {
    var last = state.lastBufferedRequest;
    state.lastBufferedRequest = {
      chunk: chunk,
      encoding: encoding,
      isBuf: isBuf,
      callback: cb,
      next: null
    };

    if (last) {
      last.next = state.lastBufferedRequest;
    } else {
      state.bufferedRequest = state.lastBufferedRequest;
    }

    state.bufferedRequestCount += 1;
  } else {
    doWrite(stream, state, false, len, chunk, encoding, cb);
  }

  return ret;
}

function doWrite(stream, state, writev, len, chunk, encoding, cb) {
  state.writelen = len;
  state.writecb = cb;
  state.writing = true;
  state.sync = true;
  if (state.destroyed) state.onwrite(new ERR_STREAM_DESTROYED('write'));else if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite);
  state.sync = false;
}

function onwriteError(stream, state, sync, er, cb) {
  --state.pendingcb;

  if (sync) {
    // defer the callback if we are being called synchronously
    // to avoid piling up things on the stack
    process.nextTick(cb, er); // this can emit finish, and it will always happen
    // after error

    process.nextTick(finishMaybe, stream, state);
    stream._writableState.errorEmitted = true;
    errorOrDestroy(stream, er);
  } else {
    // the caller expect this to happen before if
    // it is async
    cb(er);
    stream._writableState.errorEmitted = true;
    errorOrDestroy(stream, er); // this can emit finish, but finish must
    // always follow error

    finishMaybe(stream, state);
  }
}

function onwriteStateUpdate(state) {
  state.writing = false;
  state.writecb = null;
  state.length -= state.writelen;
  state.writelen = 0;
}

function onwrite(stream, er) {
  var state = stream._writableState;
  var sync = state.sync;
  var cb = state.writecb;
  if (typeof cb !== 'function') throw new ERR_MULTIPLE_CALLBACK();
  onwriteStateUpdate(state);
  if (er) onwriteError(stream, state, sync, er, cb);else {
    // Check if we're actually ready to finish, but don't emit yet
    var finished = needFinish(state) || stream.destroyed;

    if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) {
      clearBuffer(stream, state);
    }

    if (sync) {
      process.nextTick(afterWrite, stream, state, finished, cb);
    } else {
      afterWrite(stream, state, finished, cb);
    }
  }
}

function afterWrite(stream, state, finished, cb) {
  if (!finished) onwriteDrain(stream, state);
  state.pendingcb--;
  cb();
  finishMaybe(stream, state);
} // Must force callback to be called on nextTick, so that we don't
// emit 'drain' before the write() consumer gets the 'false' return
// value, and has a chance to attach a 'drain' listener.


function onwriteDrain(stream, state) {
  if (state.length === 0 && state.needDrain) {
    state.needDrain = false;
    stream.emit('drain');
  }
} // if there's something in the buffer waiting, then process it


function clearBuffer(stream, state) {
  state.bufferProcessing = true;
  var entry = state.bufferedRequest;

  if (stream._writev && entry && entry.next) {
    // Fast case, write everything using _writev()
    var l = state.bufferedRequestCount;
    var buffer = new Array(l);
    var holder = state.corkedRequestsFree;
    holder.entry = entry;
    var count = 0;
    var allBuffers = true;

    while (entry) {
      buffer[count] = entry;
      if (!entry.isBuf) allBuffers = false;
      entry = entry.next;
      count += 1;
    }

    buffer.allBuffers = allBuffers;
    doWrite(stream, state, true, state.length, buffer, '', holder.finish); // doWrite is almost always async, defer these to save a bit of time
    // as the hot path ends with doWrite

    state.pendingcb++;
    state.lastBufferedRequest = null;

    if (holder.next) {
      state.corkedRequestsFree = holder.next;
      holder.next = null;
    } else {
      state.corkedRequestsFree = new CorkedRequest(state);
    }

    state.bufferedRequestCount = 0;
  } else {
    // Slow case, write chunks one-by-one
    while (entry) {
      var chunk = entry.chunk;
      var encoding = entry.encoding;
      var cb = entry.callback;
      var len = state.objectMode ? 1 : chunk.length;
      doWrite(stream, state, false, len, chunk, encoding, cb);
      entry = entry.next;
      state.bufferedRequestCount--; // if we didn't call the onwrite immediately, then
      // it means that we need to wait until it does.
      // also, that means that the chunk and cb are currently
      // being processed, so move the buffer counter past them.

      if (state.writing) {
        break;
      }
    }

    if (entry === null) state.lastBufferedRequest = null;
  }

  state.bufferedRequest = entry;
  state.bufferProcessing = false;
}

Writable.prototype._write = function (chunk, encoding, cb) {
  cb(new ERR_METHOD_NOT_IMPLEMENTED('_write()'));
};

Writable.prototype._writev = null;

Writable.prototype.end = function (chunk, encoding, cb) {
  var state = this._writableState;

  if (typeof chunk === 'function') {
    cb = chunk;
    chunk = null;
    encoding = null;
  } else if (typeof encoding === 'function') {
    cb = encoding;
    encoding = null;
  }

  if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); // .end() fully uncorks

  if (state.corked) {
    state.corked = 1;
    this.uncork();
  } // ignore unnecessary end() calls.


  if (!state.ending) endWritable(this, state, cb);
  return this;
};

Object.defineProperty(Writable.prototype, 'writableLength', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    return this._writableState.length;
  }
});

function needFinish(state) {
  return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing;
}

function callFinal(stream, state) {
  stream._final(function (err) {
    state.pendingcb--;

    if (err) {
      errorOrDestroy(stream, err);
    }

    state.prefinished = true;
    stream.emit('prefinish');
    finishMaybe(stream, state);
  });
}

function prefinish(stream, state) {
  if (!state.prefinished && !state.finalCalled) {
    if (typeof stream._final === 'function' && !state.destroyed) {
      state.pendingcb++;
      state.finalCalled = true;
      process.nextTick(callFinal, stream, state);
    } else {
      state.prefinished = true;
      stream.emit('prefinish');
    }
  }
}

function finishMaybe(stream, state) {
  var need = needFinish(state);

  if (need) {
    prefinish(stream, state);

    if (state.pendingcb === 0) {
      state.finished = true;
      stream.emit('finish');

      if (state.autoDestroy) {
        // In case of duplex streams we need a way to detect
        // if the readable side is ready for autoDestroy as well
        var rState = stream._readableState;

        if (!rState || rState.autoDestroy && rState.endEmitted) {
          stream.destroy();
        }
      }
    }
  }

  return need;
}

function endWritable(stream, state, cb) {
  state.ending = true;
  finishMaybe(stream, state);

  if (cb) {
    if (state.finished) process.nextTick(cb);else stream.once('finish', cb);
  }

  state.ended = true;
  stream.writable = false;
}

function onCorkedFinish(corkReq, state, err) {
  var entry = corkReq.entry;
  corkReq.entry = null;

  while (entry) {
    var cb = entry.callback;
    state.pendingcb--;
    cb(err);
    entry = entry.next;
  } // reuse the free corkReq.


  state.corkedRequestsFree.next = corkReq;
}

Object.defineProperty(Writable.prototype, 'destroyed', {
  // making it explicit this property is not enumerable
  // because otherwise some prototype manipulation in
  // userland will fail
  enumerable: false,
  get: function get() {
    if (this._writableState === undefined) {
      return false;
    }

    return this._writableState.destroyed;
  },
  set: function set(value) {
    // we ignore the value if the stream
    // has not been initialized yet
    if (!this._writableState) {
      return;
    } // backward compatibility, the user is explicitly
    // managing destroyed


    this._writableState.destroyed = value;
  }
});
Writable.prototype.destroy = destroyImpl.destroy;
Writable.prototype._undestroy = destroyImpl.undestroy;

Writable.prototype._destroy = function (err, cb) {
  cb(err);
};
}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"../errors":194,"./_stream_duplex":195,"./internal/streams/destroy":202,"./internal/streams/state":206,"./internal/streams/stream":207,"_process":191,"buffer":159,"inherits":172,"util-deprecate":211}],200:[function(require,module,exports){
(function (process){(function (){
'use strict';

var _Object$setPrototypeO;

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var finished = require('./end-of-stream');

var kLastResolve = Symbol('lastResolve');
var kLastReject = Symbol('lastReject');
var kError = Symbol('error');
var kEnded = Symbol('ended');
var kLastPromise = Symbol('lastPromise');
var kHandlePromise = Symbol('handlePromise');
var kStream = Symbol('stream');

function createIterResult(value, done) {
  return {
    value: value,
    done: done
  };
}

function readAndResolve(iter) {
  var resolve = iter[kLastResolve];

  if (resolve !== null) {
    var data = iter[kStream].read(); // we defer if data is null
    // we can be expecting either 'end' or
    // 'error'

    if (data !== null) {
      iter[kLastPromise] = null;
      iter[kLastResolve] = null;
      iter[kLastReject] = null;
      resolve(createIterResult(data, false));
    }
  }
}

function onReadable(iter) {
  // we wait for the next tick, because it might
  // emit an error with process.nextTick
  process.nextTick(readAndResolve, iter);
}

function wrapForNext(lastPromise, iter) {
  return function (resolve, reject) {
    lastPromise.then(function () {
      if (iter[kEnded]) {
        resolve(createIterResult(undefined, true));
        return;
      }

      iter[kHandlePromise](resolve, reject);
    }, reject);
  };
}

var AsyncIteratorPrototype = Object.getPrototypeOf(function () {});
var ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf((_Object$setPrototypeO = {
  get stream() {
    return this[kStream];
  },

  next: function next() {
    var _this = this;

    // if we have detected an error in the meanwhile
    // reject straight away
    var error = this[kError];

    if (error !== null) {
      return Promise.reject(error);
    }

    if (this[kEnded]) {
      return Promise.resolve(createIterResult(undefined, true));
    }

    if (this[kStream].destroyed) {
      // We need to defer via nextTick because if .destroy(err) is
      // called, the error will be emitted via nextTick, and
      // we cannot guarantee that there is no error lingering around
      // waiting to be emitted.
      return new Promise(function (resolve, reject) {
        process.nextTick(function () {
          if (_this[kError]) {
            reject(_this[kError]);
          } else {
            resolve(createIterResult(undefined, true));
          }
        });
      });
    } // if we have multiple next() calls
    // we will wait for the previous Promise to finish
    // this logic is optimized to support for await loops,
    // where next() is only called once at a time


    var lastPromise = this[kLastPromise];
    var promise;

    if (lastPromise) {
      promise = new Promise(wrapForNext(lastPromise, this));
    } else {
      // fast path needed to support multiple this.push()
      // without triggering the next() queue
      var data = this[kStream].read();

      if (data !== null) {
        return Promise.resolve(createIterResult(data, false));
      }

      promise = new Promise(this[kHandlePromise]);
    }

    this[kLastPromise] = promise;
    return promise;
  }
}, _defineProperty(_Object$setPrototypeO, Symbol.asyncIterator, function () {
  return this;
}), _defineProperty(_Object$setPrototypeO, "return", function _return() {
  var _this2 = this;

  // destroy(err, cb) is a private API
  // we can guarantee we have that here, because we control the
  // Readable class this is attached to
  return new Promise(function (resolve, reject) {
    _this2[kStream].destroy(null, function (err) {
      if (err) {
        reject(err);
        return;
      }

      resolve(createIterResult(undefined, true));
    });
  });
}), _Object$setPrototypeO), AsyncIteratorPrototype);

var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) {
  var _Object$create;

  var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, {
    value: stream,
    writable: true
  }), _defineProperty(_Object$create, kLastResolve, {
    value: null,
    writable: true
  }), _defineProperty(_Object$create, kLastReject, {
    value: null,
    writable: true
  }), _defineProperty(_Object$create, kError, {
    value: null,
    writable: true
  }), _defineProperty(_Object$create, kEnded, {
    value: stream._readableState.endEmitted,
    writable: true
  }), _defineProperty(_Object$create, kHandlePromise, {
    value: function value(resolve, reject) {
      var data = iterator[kStream].read();

      if (data) {
        iterator[kLastPromise] = null;
        iterator[kLastResolve] = null;
        iterator[kLastReject] = null;
        resolve(createIterResult(data, false));
      } else {
        iterator[kLastResolve] = resolve;
        iterator[kLastReject] = reject;
      }
    },
    writable: true
  }), _Object$create));
  iterator[kLastPromise] = null;
  finished(stream, function (err) {
    if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') {
      var reject = iterator[kLastReject]; // reject if we are waiting for data in the Promise
      // returned by next() and store the error

      if (reject !== null) {
        iterator[kLastPromise] = null;
        iterator[kLastResolve] = null;
        iterator[kLastReject] = null;
        reject(err);
      }

      iterator[kError] = err;
      return;
    }

    var resolve = iterator[kLastResolve];

    if (resolve !== null) {
      iterator[kLastPromise] = null;
      iterator[kLastResolve] = null;
      iterator[kLastReject] = null;
      resolve(createIterResult(undefined, true));
    }

    iterator[kEnded] = true;
  });
  stream.on('readable', onReadable.bind(null, iterator));
  return iterator;
};

module.exports = createReadableStreamAsyncIterator;
}).call(this)}).call(this,require('_process'))
},{"./end-of-stream":203,"_process":191}],201:[function(require,module,exports){
'use strict';

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var _require = require('buffer'),
    Buffer = _require.Buffer;

var _require2 = require('util'),
    inspect = _require2.inspect;

var custom = inspect && inspect.custom || 'inspect';

function copyBuffer(src, target, offset) {
  Buffer.prototype.copy.call(src, target, offset);
}

module.exports =
/*#__PURE__*/
function () {
  function BufferList() {
    _classCallCheck(this, BufferList);

    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  _createClass(BufferList, [{
    key: "push",
    value: function push(v) {
      var entry = {
        data: v,
        next: null
      };
      if (this.length > 0) this.tail.next = entry;else this.head = entry;
      this.tail = entry;
      ++this.length;
    }
  }, {
    key: "unshift",
    value: function unshift(v) {
      var entry = {
        data: v,
        next: this.head
      };
      if (this.length === 0) this.tail = entry;
      this.head = entry;
      ++this.length;
    }
  }, {
    key: "shift",
    value: function shift() {
      if (this.length === 0) return;
      var ret = this.head.data;
      if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next;
      --this.length;
      return ret;
    }
  }, {
    key: "clear",
    value: function clear() {
      this.head = this.tail = null;
      this.length = 0;
    }
  }, {
    key: "join",
    value: function join(s) {
      if (this.length === 0) return '';
      var p = this.head;
      var ret = '' + p.data;

      while (p = p.next) {
        ret += s + p.data;
      }

      return ret;
    }
  }, {
    key: "concat",
    value: function concat(n) {
      if (this.length === 0) return Buffer.alloc(0);
      var ret = Buffer.allocUnsafe(n >>> 0);
      var p = this.head;
      var i = 0;

      while (p) {
        copyBuffer(p.data, ret, i);
        i += p.data.length;
        p = p.next;
      }

      return ret;
    } // Consumes a specified amount of bytes or characters from the buffered data.

  }, {
    key: "consume",
    value: function consume(n, hasStrings) {
      var ret;

      if (n < this.head.data.length) {
        // `slice` is the same for buffers and strings.
        ret = this.head.data.slice(0, n);
        this.head.data = this.head.data.slice(n);
      } else if (n === this.head.data.length) {
        // First chunk is a perfect match.
        ret = this.shift();
      } else {
        // Result spans more than one buffer.
        ret = hasStrings ? this._getString(n) : this._getBuffer(n);
      }

      return ret;
    }
  }, {
    key: "first",
    value: function first() {
      return this.head.data;
    } // Consumes a specified amount of characters from the buffered data.

  }, {
    key: "_getString",
    value: function _getString(n) {
      var p = this.head;
      var c = 1;
      var ret = p.data;
      n -= ret.length;

      while (p = p.next) {
        var str = p.data;
        var nb = n > str.length ? str.length : n;
        if (nb === str.length) ret += str;else ret += str.slice(0, n);
        n -= nb;

        if (n === 0) {
          if (nb === str.length) {
            ++c;
            if (p.next) this.head = p.next;else this.head = this.tail = null;
          } else {
            this.head = p;
            p.data = str.slice(nb);
          }

          break;
        }

        ++c;
      }

      this.length -= c;
      return ret;
    } // Consumes a specified amount of bytes from the buffered data.

  }, {
    key: "_getBuffer",
    value: function _getBuffer(n) {
      var ret = Buffer.allocUnsafe(n);
      var p = this.head;
      var c = 1;
      p.data.copy(ret);
      n -= p.data.length;

      while (p = p.next) {
        var buf = p.data;
        var nb = n > buf.length ? buf.length : n;
        buf.copy(ret, ret.length - n, 0, nb);
        n -= nb;

        if (n === 0) {
          if (nb === buf.length) {
            ++c;
            if (p.next) this.head = p.next;else this.head = this.tail = null;
          } else {
            this.head = p;
            p.data = buf.slice(nb);
          }

          break;
        }

        ++c;
      }

      this.length -= c;
      return ret;
    } // Make sure the linked list only shows the minimal necessary information.

  }, {
    key: custom,
    value: function value(_, options) {
      return inspect(this, _objectSpread({}, options, {
        // Only inspect one level.
        depth: 0,
        // It should not recurse.
        customInspect: false
      }));
    }
  }]);

  return BufferList;
}();
},{"buffer":159,"util":156}],202:[function(require,module,exports){
(function (process){(function (){
'use strict'; // undocumented cb() API, needed for core, not for public API

function destroy(err, cb) {
  var _this = this;

  var readableDestroyed = this._readableState && this._readableState.destroyed;
  var writableDestroyed = this._writableState && this._writableState.destroyed;

  if (readableDestroyed || writableDestroyed) {
    if (cb) {
      cb(err);
    } else if (err) {
      if (!this._writableState) {
        process.nextTick(emitErrorNT, this, err);
      } else if (!this._writableState.errorEmitted) {
        this._writableState.errorEmitted = true;
        process.nextTick(emitErrorNT, this, err);
      }
    }

    return this;
  } // we set destroyed to true before firing error callbacks in order
  // to make it re-entrance safe in case destroy() is called within callbacks


  if (this._readableState) {
    this._readableState.destroyed = true;
  } // if this is a duplex stream mark the writable part as destroyed as well


  if (this._writableState) {
    this._writableState.destroyed = true;
  }

  this._destroy(err || null, function (err) {
    if (!cb && err) {
      if (!_this._writableState) {
        process.nextTick(emitErrorAndCloseNT, _this, err);
      } else if (!_this._writableState.errorEmitted) {
        _this._writableState.errorEmitted = true;
        process.nextTick(emitErrorAndCloseNT, _this, err);
      } else {
        process.nextTick(emitCloseNT, _this);
      }
    } else if (cb) {
      process.nextTick(emitCloseNT, _this);
      cb(err);
    } else {
      process.nextTick(emitCloseNT, _this);
    }
  });

  return this;
}

function emitErrorAndCloseNT(self, err) {
  emitErrorNT(self, err);
  emitCloseNT(self);
}

function emitCloseNT(self) {
  if (self._writableState && !self._writableState.emitClose) return;
  if (self._readableState && !self._readableState.emitClose) return;
  self.emit('close');
}

function undestroy() {
  if (this._readableState) {
    this._readableState.destroyed = false;
    this._readableState.reading = false;
    this._readableState.ended = false;
    this._readableState.endEmitted = false;
  }

  if (this._writableState) {
    this._writableState.destroyed = false;
    this._writableState.ended = false;
    this._writableState.ending = false;
    this._writableState.finalCalled = false;
    this._writableState.prefinished = false;
    this._writableState.finished = false;
    this._writableState.errorEmitted = false;
  }
}

function emitErrorNT(self, err) {
  self.emit('error', err);
}

function errorOrDestroy(stream, err) {
  // We have tests that rely on errors being emitted
  // in the same tick, so changing this is semver major.
  // For now when you opt-in to autoDestroy we allow
  // the error to be emitted nextTick. In a future
  // semver major update we should change the default to this.
  var rState = stream._readableState;
  var wState = stream._writableState;
  if (rState && rState.autoDestroy || wState && wState.autoDestroy) stream.destroy(err);else stream.emit('error', err);
}

module.exports = {
  destroy: destroy,
  undestroy: undestroy,
  errorOrDestroy: errorOrDestroy
};
}).call(this)}).call(this,require('_process'))
},{"_process":191}],203:[function(require,module,exports){
// Ported from https://github.com/mafintosh/end-of-stream with
// permission from the author, Mathias Buus (@mafintosh).
'use strict';

var ERR_STREAM_PREMATURE_CLOSE = require('../../../errors').codes.ERR_STREAM_PREMATURE_CLOSE;

function once(callback) {
  var called = false;
  return function () {
    if (called) return;
    called = true;

    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    callback.apply(this, args);
  };
}

function noop() {}

function isRequest(stream) {
  return stream.setHeader && typeof stream.abort === 'function';
}

function eos(stream, opts, callback) {
  if (typeof opts === 'function') return eos(stream, null, opts);
  if (!opts) opts = {};
  callback = once(callback || noop);
  var readable = opts.readable || opts.readable !== false && stream.readable;
  var writable = opts.writable || opts.writable !== false && stream.writable;

  var onlegacyfinish = function onlegacyfinish() {
    if (!stream.writable) onfinish();
  };

  var writableEnded = stream._writableState && stream._writableState.finished;

  var onfinish = function onfinish() {
    writable = false;
    writableEnded = true;
    if (!readable) callback.call(stream);
  };

  var readableEnded = stream._readableState && stream._readableState.endEmitted;

  var onend = function onend() {
    readable = false;
    readableEnded = true;
    if (!writable) callback.call(stream);
  };

  var onerror = function onerror(err) {
    callback.call(stream, err);
  };

  var onclose = function onclose() {
    var err;

    if (readable && !readableEnded) {
      if (!stream._readableState || !stream._readableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE();
      return callback.call(stream, err);
    }

    if (writable && !writableEnded) {
      if (!stream._writableState || !stream._writableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE();
      return callback.call(stream, err);
    }
  };

  var onrequest = function onrequest() {
    stream.req.on('finish', onfinish);
  };

  if (isRequest(stream)) {
    stream.on('complete', onfinish);
    stream.on('abort', onclose);
    if (stream.req) onrequest();else stream.on('request', onrequest);
  } else if (writable && !stream._writableState) {
    // legacy streams
    stream.on('end', onlegacyfinish);
    stream.on('close', onlegacyfinish);
  }

  stream.on('end', onend);
  stream.on('finish', onfinish);
  if (opts.error !== false) stream.on('error', onerror);
  stream.on('close', onclose);
  return function () {
    stream.removeListener('complete', onfinish);
    stream.removeListener('abort', onclose);
    stream.removeListener('request', onrequest);
    if (stream.req) stream.req.removeListener('finish', onfinish);
    stream.removeListener('end', onlegacyfinish);
    stream.removeListener('close', onlegacyfinish);
    stream.removeListener('finish', onfinish);
    stream.removeListener('end', onend);
    stream.removeListener('error', onerror);
    stream.removeListener('close', onclose);
  };
}

module.exports = eos;
},{"../../../errors":194}],204:[function(require,module,exports){
module.exports = function () {
  throw new Error('Readable.from is not available in the browser')
};

},{}],205:[function(require,module,exports){
// Ported from https://github.com/mafintosh/pump with
// permission from the author, Mathias Buus (@mafintosh).
'use strict';

var eos;

function once(callback) {
  var called = false;
  return function () {
    if (called) return;
    called = true;
    callback.apply(void 0, arguments);
  };
}

var _require$codes = require('../../../errors').codes,
    ERR_MISSING_ARGS = _require$codes.ERR_MISSING_ARGS,
    ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED;

function noop(err) {
  // Rethrow the error if it exists to avoid swallowing it
  if (err) throw err;
}

function isRequest(stream) {
  return stream.setHeader && typeof stream.abort === 'function';
}

function destroyer(stream, reading, writing, callback) {
  callback = once(callback);
  var closed = false;
  stream.on('close', function () {
    closed = true;
  });
  if (eos === undefined) eos = require('./end-of-stream');
  eos(stream, {
    readable: reading,
    writable: writing
  }, function (err) {
    if (err) return callback(err);
    closed = true;
    callback();
  });
  var destroyed = false;
  return function (err) {
    if (closed) return;
    if (destroyed) return;
    destroyed = true; // request.destroy just do .end - .abort is what we want

    if (isRequest(stream)) return stream.abort();
    if (typeof stream.destroy === 'function') return stream.destroy();
    callback(err || new ERR_STREAM_DESTROYED('pipe'));
  };
}

function call(fn) {
  fn();
}

function pipe(from, to) {
  return from.pipe(to);
}

function popCallback(streams) {
  if (!streams.length) return noop;
  if (typeof streams[streams.length - 1] !== 'function') return noop;
  return streams.pop();
}

function pipeline() {
  for (var _len = arguments.length, streams = new Array(_len), _key = 0; _key < _len; _key++) {
    streams[_key] = arguments[_key];
  }

  var callback = popCallback(streams);
  if (Array.isArray(streams[0])) streams = streams[0];

  if (streams.length < 2) {
    throw new ERR_MISSING_ARGS('streams');
  }

  var error;
  var destroys = streams.map(function (stream, i) {
    var reading = i < streams.length - 1;
    var writing = i > 0;
    return destroyer(stream, reading, writing, function (err) {
      if (!error) error = err;
      if (err) destroys.forEach(call);
      if (reading) return;
      destroys.forEach(call);
      callback(error);
    });
  });
  return streams.reduce(pipe);
}

module.exports = pipeline;
},{"../../../errors":194,"./end-of-stream":203}],206:[function(require,module,exports){
'use strict';

var ERR_INVALID_OPT_VALUE = require('../../../errors').codes.ERR_INVALID_OPT_VALUE;

function highWaterMarkFrom(options, isDuplex, duplexKey) {
  return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null;
}

function getHighWaterMark(state, options, duplexKey, isDuplex) {
  var hwm = highWaterMarkFrom(options, isDuplex, duplexKey);

  if (hwm != null) {
    if (!(isFinite(hwm) && Math.floor(hwm) === hwm) || hwm < 0) {
      var name = isDuplex ? duplexKey : 'highWaterMark';
      throw new ERR_INVALID_OPT_VALUE(name, hwm);
    }

    return Math.floor(hwm);
  } // Default value


  return state.objectMode ? 16 : 16 * 1024;
}

module.exports = {
  getHighWaterMark: getHighWaterMark
};
},{"../../../errors":194}],207:[function(require,module,exports){
module.exports = require('events').EventEmitter;

},{"events":163}],208:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

'use strict';

/*<replacement>*/

var Buffer = require('safe-buffer').Buffer;
/*</replacement>*/

var isEncoding = Buffer.isEncoding || function (encoding) {
  encoding = '' + encoding;
  switch (encoding && encoding.toLowerCase()) {
    case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw':
      return true;
    default:
      return false;
  }
};

function _normalizeEncoding(enc) {
  if (!enc) return 'utf8';
  var retried;
  while (true) {
    switch (enc) {
      case 'utf8':
      case 'utf-8':
        return 'utf8';
      case 'ucs2':
      case 'ucs-2':
      case 'utf16le':
      case 'utf-16le':
        return 'utf16le';
      case 'latin1':
      case 'binary':
        return 'latin1';
      case 'base64':
      case 'ascii':
      case 'hex':
        return enc;
      default:
        if (retried) return; // undefined
        enc = ('' + enc).toLowerCase();
        retried = true;
    }
  }
};

// Do not cache `Buffer.isEncoding` when checking encoding names as some
// modules monkey-patch it to support additional encodings
function normalizeEncoding(enc) {
  var nenc = _normalizeEncoding(enc);
  if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc);
  return nenc || enc;
}

// StringDecoder provides an interface for efficiently splitting a series of
// buffers into a series of JS strings without breaking apart multi-byte
// characters.
exports.StringDecoder = StringDecoder;
function StringDecoder(encoding) {
  this.encoding = normalizeEncoding(encoding);
  var nb;
  switch (this.encoding) {
    case 'utf16le':
      this.text = utf16Text;
      this.end = utf16End;
      nb = 4;
      break;
    case 'utf8':
      this.fillLast = utf8FillLast;
      nb = 4;
      break;
    case 'base64':
      this.text = base64Text;
      this.end = base64End;
      nb = 3;
      break;
    default:
      this.write = simpleWrite;
      this.end = simpleEnd;
      return;
  }
  this.lastNeed = 0;
  this.lastTotal = 0;
  this.lastChar = Buffer.allocUnsafe(nb);
}

StringDecoder.prototype.write = function (buf) {
  if (buf.length === 0) return '';
  var r;
  var i;
  if (this.lastNeed) {
    r = this.fillLast(buf);
    if (r === undefined) return '';
    i = this.lastNeed;
    this.lastNeed = 0;
  } else {
    i = 0;
  }
  if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i);
  return r || '';
};

StringDecoder.prototype.end = utf8End;

// Returns only complete characters in a Buffer
StringDecoder.prototype.text = utf8Text;

// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer
StringDecoder.prototype.fillLast = function (buf) {
  if (this.lastNeed <= buf.length) {
    buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed);
    return this.lastChar.toString(this.encoding, 0, this.lastTotal);
  }
  buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length);
  this.lastNeed -= buf.length;
};

// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a
// continuation byte. If an invalid byte is detected, -2 is returned.
function utf8CheckByte(byte) {
  if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4;
  return byte >> 6 === 0x02 ? -1 : -2;
}

// Checks at most 3 bytes at the end of a Buffer in order to detect an
// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4)
// needed to complete the UTF-8 character (if applicable) are returned.
function utf8CheckIncomplete(self, buf, i) {
  var j = buf.length - 1;
  if (j < i) return 0;
  var nb = utf8CheckByte(buf[j]);
  if (nb >= 0) {
    if (nb > 0) self.lastNeed = nb - 1;
    return nb;
  }
  if (--j < i || nb === -2) return 0;
  nb = utf8CheckByte(buf[j]);
  if (nb >= 0) {
    if (nb > 0) self.lastNeed = nb - 2;
    return nb;
  }
  if (--j < i || nb === -2) return 0;
  nb = utf8CheckByte(buf[j]);
  if (nb >= 0) {
    if (nb > 0) {
      if (nb === 2) nb = 0;else self.lastNeed = nb - 3;
    }
    return nb;
  }
  return 0;
}

// Validates as many continuation bytes for a multi-byte UTF-8 character as
// needed or are available. If we see a non-continuation byte where we expect
// one, we "replace" the validated continuation bytes we've seen so far with
// a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding
// behavior. The continuation byte check is included three times in the case
// where all of the continuation bytes for a character exist in the same buffer.
// It is also done this way as a slight performance increase instead of using a
// loop.
function utf8CheckExtraBytes(self, buf, p) {
  if ((buf[0] & 0xC0) !== 0x80) {
    self.lastNeed = 0;
    return '\ufffd';
  }
  if (self.lastNeed > 1 && buf.length > 1) {
    if ((buf[1] & 0xC0) !== 0x80) {
      self.lastNeed = 1;
      return '\ufffd';
    }
    if (self.lastNeed > 2 && buf.length > 2) {
      if ((buf[2] & 0xC0) !== 0x80) {
        self.lastNeed = 2;
        return '\ufffd';
      }
    }
  }
}

// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer.
function utf8FillLast(buf) {
  var p = this.lastTotal - this.lastNeed;
  var r = utf8CheckExtraBytes(this, buf, p);
  if (r !== undefined) return r;
  if (this.lastNeed <= buf.length) {
    buf.copy(this.lastChar, p, 0, this.lastNeed);
    return this.lastChar.toString(this.encoding, 0, this.lastTotal);
  }
  buf.copy(this.lastChar, p, 0, buf.length);
  this.lastNeed -= buf.length;
}

// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a
// partial character, the character's bytes are buffered until the required
// number of bytes are available.
function utf8Text(buf, i) {
  var total = utf8CheckIncomplete(this, buf, i);
  if (!this.lastNeed) return buf.toString('utf8', i);
  this.lastTotal = total;
  var end = buf.length - (total - this.lastNeed);
  buf.copy(this.lastChar, 0, end);
  return buf.toString('utf8', i, end);
}

// For UTF-8, a replacement character is added when ending on a partial
// character.
function utf8End(buf) {
  var r = buf && buf.length ? this.write(buf) : '';
  if (this.lastNeed) return r + '\ufffd';
  return r;
}

// UTF-16LE typically needs two bytes per character, but even if we have an even
// number of bytes available, we need to check if we end on a leading/high
// surrogate. In that case, we need to wait for the next two bytes in order to
// decode the last character properly.
function utf16Text(buf, i) {
  if ((buf.length - i) % 2 === 0) {
    var r = buf.toString('utf16le', i);
    if (r) {
      var c = r.charCodeAt(r.length - 1);
      if (c >= 0xD800 && c <= 0xDBFF) {
        this.lastNeed = 2;
        this.lastTotal = 4;
        this.lastChar[0] = buf[buf.length - 2];
        this.lastChar[1] = buf[buf.length - 1];
        return r.slice(0, -1);
      }
    }
    return r;
  }
  this.lastNeed = 1;
  this.lastTotal = 2;
  this.lastChar[0] = buf[buf.length - 1];
  return buf.toString('utf16le', i, buf.length - 1);
}

// For UTF-16LE we do not explicitly append special replacement characters if we
// end on a partial character, we simply let v8 handle that.
function utf16End(buf) {
  var r = buf && buf.length ? this.write(buf) : '';
  if (this.lastNeed) {
    var end = this.lastTotal - this.lastNeed;
    return r + this.lastChar.toString('utf16le', 0, end);
  }
  return r;
}

function base64Text(buf, i) {
  var n = (buf.length - i) % 3;
  if (n === 0) return buf.toString('base64', i);
  this.lastNeed = 3 - n;
  this.lastTotal = 3;
  if (n === 1) {
    this.lastChar[0] = buf[buf.length - 1];
  } else {
    this.lastChar[0] = buf[buf.length - 2];
    this.lastChar[1] = buf[buf.length - 1];
  }
  return buf.toString('base64', i, buf.length - n);
}

function base64End(buf) {
  var r = buf && buf.length ? this.write(buf) : '';
  if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed);
  return r;
}

// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex)
function simpleWrite(buf) {
  return buf.toString(this.encoding);
}

function simpleEnd(buf) {
  return buf && buf.length ? this.write(buf) : '';
}
},{"safe-buffer":192}],209:[function(require,module,exports){
(function (setImmediate,clearImmediate){(function (){
var nextTick = require('process/browser.js').nextTick;
var apply = Function.prototype.apply;
var slice = Array.prototype.slice;
var immediateIds = {};
var nextImmediateId = 0;

// DOM APIs, for completeness

exports.setTimeout = function() {
  return new Timeout(apply.call(setTimeout, window, arguments), clearTimeout);
};
exports.setInterval = function() {
  return new Timeout(apply.call(setInterval, window, arguments), clearInterval);
};
exports.clearTimeout =
exports.clearInterval = function(timeout) { timeout.close(); };

function Timeout(id, clearFn) {
  this._id = id;
  this._clearFn = clearFn;
}
Timeout.prototype.unref = Timeout.prototype.ref = function() {};
Timeout.prototype.close = function() {
  this._clearFn.call(window, this._id);
};

// Does not start the time, just sets up the members needed.
exports.enroll = function(item, msecs) {
  clearTimeout(item._idleTimeoutId);
  item._idleTimeout = msecs;
};

exports.unenroll = function(item) {
  clearTimeout(item._idleTimeoutId);
  item._idleTimeout = -1;
};

exports._unrefActive = exports.active = function(item) {
  clearTimeout(item._idleTimeoutId);

  var msecs = item._idleTimeout;
  if (msecs >= 0) {
    item._idleTimeoutId = setTimeout(function onTimeout() {
      if (item._onTimeout)
        item._onTimeout();
    }, msecs);
  }
};

// That's not how node.js implements it but the exposed api is the same.
exports.setImmediate = typeof setImmediate === "function" ? setImmediate : function(fn) {
  var id = nextImmediateId++;
  var args = arguments.length < 2 ? false : slice.call(arguments, 1);

  immediateIds[id] = true;

  nextTick(function onNextTick() {
    if (immediateIds[id]) {
      // fn.call() is faster so we optimize for the common use-case
      // @see http://jsperf.com/call-apply-segu
      if (args) {
        fn.apply(null, args);
      } else {
        fn.call(null);
      }
      // Prevent ids from leaking
      exports.clearImmediate(id);
    }
  });

  return id;
};

exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate : function(id) {
  delete immediateIds[id];
};
}).call(this)}).call(this,require("timers").setImmediate,require("timers").clearImmediate)
},{"process/browser.js":191,"timers":209}],210:[function(require,module,exports){
exports.isatty = function () { return false; };

function ReadStream() {
  throw new Error('tty.ReadStream is not implemented');
}
exports.ReadStream = ReadStream;

function WriteStream() {
  throw new Error('tty.WriteStream is not implemented');
}
exports.WriteStream = WriteStream;

},{}],211:[function(require,module,exports){
(function (global){(function (){

/**
 * Module exports.
 */

module.exports = deprecate;

/**
 * Mark that a method should not be used.
 * Returns a modified function which warns once by default.
 *
 * If `localStorage.noDeprecation = true` is set, then it is a no-op.
 *
 * If `localStorage.throwDeprecation = true` is set, then deprecated functions
 * will throw an Error when invoked.
 *
 * If `localStorage.traceDeprecation = true` is set, then deprecated functions
 * will invoke `console.trace()` instead of `console.error()`.
 *
 * @param {Function} fn - the function to deprecate
 * @param {String} msg - the string to print to the console when `fn` is invoked
 * @returns {Function} a new "deprecated" version of `fn`
 * @api public
 */

function deprecate (fn, msg) {
  if (config('noDeprecation')) {
    return fn;
  }

  var warned = false;
  function deprecated() {
    if (!warned) {
      if (config('throwDeprecation')) {
        throw new Error(msg);
      } else if (config('traceDeprecation')) {
        console.trace(msg);
      } else {
        console.warn(msg);
      }
      warned = true;
    }
    return fn.apply(this, arguments);
  }

  return deprecated;
}

/**
 * Checks `localStorage` for boolean values for the given `name`.
 *
 * @param {String} name
 * @returns {Boolean}
 * @api private
 */

function config (name) {
  // accessing global.localStorage can trigger a DOMException in sandboxed iframes
  try {
    if (!global.localStorage) return false;
  } catch (_) {
    return false;
  }
  var val = global.localStorage[name];
  if (null == val) return false;
  return String(val).toLowerCase() === 'true';
}

}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],212:[function(require,module,exports){
arguments[4][152][0].apply(exports,arguments)
},{"dup":152}],213:[function(require,module,exports){
// Currently in sync with Node.js lib/internal/util/types.js
// https://github.com/nodejs/node/commit/112cc7c27551254aa2b17098fb774867f05ed0d9

'use strict';

var isArgumentsObject = require('is-arguments');
var isGeneratorFunction = require('is-generator-function');
var whichTypedArray = require('which-typed-array');
var isTypedArray = require('is-typed-array');

function uncurryThis(f) {
  return f.call.bind(f);
}

var BigIntSupported = typeof BigInt !== 'undefined';
var SymbolSupported = typeof Symbol !== 'undefined';

var ObjectToString = uncurryThis(Object.prototype.toString);

var numberValue = uncurryThis(Number.prototype.valueOf);
var stringValue = uncurryThis(String.prototype.valueOf);
var booleanValue = uncurryThis(Boolean.prototype.valueOf);

if (BigIntSupported) {
  var bigIntValue = uncurryThis(BigInt.prototype.valueOf);
}

if (SymbolSupported) {
  var symbolValue = uncurryThis(Symbol.prototype.valueOf);
}

function checkBoxedPrimitive(value, prototypeValueOf) {
  if (typeof value !== 'object') {
    return false;
  }
  try {
    prototypeValueOf(value);
    return true;
  } catch(e) {
    return false;
  }
}

exports.isArgumentsObject = isArgumentsObject;
exports.isGeneratorFunction = isGeneratorFunction;
exports.isTypedArray = isTypedArray;

// Taken from here and modified for better browser support
// https://github.com/sindresorhus/p-is-promise/blob/cda35a513bda03f977ad5cde3a079d237e82d7ef/index.js
function isPromise(input) {
	return (
		(
			typeof Promise !== 'undefined' &&
			input instanceof Promise
		) ||
		(
			input !== null &&
			typeof input === 'object' &&
			typeof input.then === 'function' &&
			typeof input.catch === 'function'
		)
	);
}
exports.isPromise = isPromise;

function isArrayBufferView(value) {
  if (typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView) {
    return ArrayBuffer.isView(value);
  }

  return (
    isTypedArray(value) ||
    isDataView(value)
  );
}
exports.isArrayBufferView = isArrayBufferView;


function isUint8Array(value) {
  return whichTypedArray(value) === 'Uint8Array';
}
exports.isUint8Array = isUint8Array;

function isUint8ClampedArray(value) {
  return whichTypedArray(value) === 'Uint8ClampedArray';
}
exports.isUint8ClampedArray = isUint8ClampedArray;

function isUint16Array(value) {
  return whichTypedArray(value) === 'Uint16Array';
}
exports.isUint16Array = isUint16Array;

function isUint32Array(value) {
  return whichTypedArray(value) === 'Uint32Array';
}
exports.isUint32Array = isUint32Array;

function isInt8Array(value) {
  return whichTypedArray(value) === 'Int8Array';
}
exports.isInt8Array = isInt8Array;

function isInt16Array(value) {
  return whichTypedArray(value) === 'Int16Array';
}
exports.isInt16Array = isInt16Array;

function isInt32Array(value) {
  return whichTypedArray(value) === 'Int32Array';
}
exports.isInt32Array = isInt32Array;

function isFloat32Array(value) {
  return whichTypedArray(value) === 'Float32Array';
}
exports.isFloat32Array = isFloat32Array;

function isFloat64Array(value) {
  return whichTypedArray(value) === 'Float64Array';
}
exports.isFloat64Array = isFloat64Array;

function isBigInt64Array(value) {
  return whichTypedArray(value) === 'BigInt64Array';
}
exports.isBigInt64Array = isBigInt64Array;

function isBigUint64Array(value) {
  return whichTypedArray(value) === 'BigUint64Array';
}
exports.isBigUint64Array = isBigUint64Array;

function isMapToString(value) {
  return ObjectToString(value) === '[object Map]';
}
isMapToString.working = (
  typeof Map !== 'undefined' &&
  isMapToString(new Map())
);

function isMap(value) {
  if (typeof Map === 'undefined') {
    return false;
  }

  return isMapToString.working
    ? isMapToString(value)
    : value instanceof Map;
}
exports.isMap = isMap;

function isSetToString(value) {
  return ObjectToString(value) === '[object Set]';
}
isSetToString.working = (
  typeof Set !== 'undefined' &&
  isSetToString(new Set())
);
function isSet(value) {
  if (typeof Set === 'undefined') {
    return false;
  }

  return isSetToString.working
    ? isSetToString(value)
    : value instanceof Set;
}
exports.isSet = isSet;

function isWeakMapToString(value) {
  return ObjectToString(value) === '[object WeakMap]';
}
isWeakMapToString.working = (
  typeof WeakMap !== 'undefined' &&
  isWeakMapToString(new WeakMap())
);
function isWeakMap(value) {
  if (typeof WeakMap === 'undefined') {
    return false;
  }

  return isWeakMapToString.working
    ? isWeakMapToString(value)
    : value instanceof WeakMap;
}
exports.isWeakMap = isWeakMap;

function isWeakSetToString(value) {
  return ObjectToString(value) === '[object WeakSet]';
}
isWeakSetToString.working = (
  typeof WeakSet !== 'undefined' &&
  isWeakSetToString(new WeakSet())
);
function isWeakSet(value) {
  return isWeakSetToString(value);
}
exports.isWeakSet = isWeakSet;

function isArrayBufferToString(value) {
  return ObjectToString(value) === '[object ArrayBuffer]';
}
isArrayBufferToString.working = (
  typeof ArrayBuffer !== 'undefined' &&
  isArrayBufferToString(new ArrayBuffer())
);
function isArrayBuffer(value) {
  if (typeof ArrayBuffer === 'undefined') {
    return false;
  }

  return isArrayBufferToString.working
    ? isArrayBufferToString(value)
    : value instanceof ArrayBuffer;
}
exports.isArrayBuffer = isArrayBuffer;

function isDataViewToString(value) {
  return ObjectToString(value) === '[object DataView]';
}
isDataViewToString.working = (
  typeof ArrayBuffer !== 'undefined' &&
  typeof DataView !== 'undefined' &&
  isDataViewToString(new DataView(new ArrayBuffer(1), 0, 1))
);
function isDataView(value) {
  if (typeof DataView === 'undefined') {
    return false;
  }

  return isDataViewToString.working
    ? isDataViewToString(value)
    : value instanceof DataView;
}
exports.isDataView = isDataView;

function isSharedArrayBufferToString(value) {
  return ObjectToString(value) === '[object SharedArrayBuffer]';
}
isSharedArrayBufferToString.working = (
  typeof SharedArrayBuffer !== 'undefined' &&
  isSharedArrayBufferToString(new SharedArrayBuffer())
);
function isSharedArrayBuffer(value) {
  if (typeof SharedArrayBuffer === 'undefined') {
    return false;
  }

  return isSharedArrayBufferToString.working
    ? isSharedArrayBufferToString(value)
    : value instanceof SharedArrayBuffer;
}
exports.isSharedArrayBuffer = isSharedArrayBuffer;

function isAsyncFunction(value) {
  return ObjectToString(value) === '[object AsyncFunction]';
}
exports.isAsyncFunction = isAsyncFunction;

function isMapIterator(value) {
  return ObjectToString(value) === '[object Map Iterator]';
}
exports.isMapIterator = isMapIterator;

function isSetIterator(value) {
  return ObjectToString(value) === '[object Set Iterator]';
}
exports.isSetIterator = isSetIterator;

function isGeneratorObject(value) {
  return ObjectToString(value) === '[object Generator]';
}
exports.isGeneratorObject = isGeneratorObject;

function isWebAssemblyCompiledModule(value) {
  return ObjectToString(value) === '[object WebAssembly.Module]';
}
exports.isWebAssemblyCompiledModule = isWebAssemblyCompiledModule;

function isNumberObject(value) {
  return checkBoxedPrimitive(value, numberValue);
}
exports.isNumberObject = isNumberObject;

function isStringObject(value) {
  return checkBoxedPrimitive(value, stringValue);
}
exports.isStringObject = isStringObject;

function isBooleanObject(value) {
  return checkBoxedPrimitive(value, booleanValue);
}
exports.isBooleanObject = isBooleanObject;

function isBigIntObject(value) {
  return BigIntSupported && checkBoxedPrimitive(value, bigIntValue);
}
exports.isBigIntObject = isBigIntObject;

function isSymbolObject(value) {
  return SymbolSupported && checkBoxedPrimitive(value, symbolValue);
}
exports.isSymbolObject = isSymbolObject;

function isBoxedPrimitive(value) {
  return (
    isNumberObject(value) ||
    isStringObject(value) ||
    isBooleanObject(value) ||
    isBigIntObject(value) ||
    isSymbolObject(value)
  );
}
exports.isBoxedPrimitive = isBoxedPrimitive;

function isAnyArrayBuffer(value) {
  return typeof Uint8Array !== 'undefined' && (
    isArrayBuffer(value) ||
    isSharedArrayBuffer(value)
  );
}
exports.isAnyArrayBuffer = isAnyArrayBuffer;

['isProxy', 'isExternal', 'isModuleNamespaceObject'].forEach(function(method) {
  Object.defineProperty(exports, method, {
    enumerable: false,
    value: function() {
      throw new Error(method + ' is not supported in userland');
    }
  });
});

},{"is-arguments":173,"is-generator-function":175,"is-typed-array":176,"which-typed-array":215}],214:[function(require,module,exports){
(function (process){(function (){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors ||
  function getOwnPropertyDescriptors(obj) {
    var keys = Object.keys(obj);
    var descriptors = {};
    for (var i = 0; i < keys.length; i++) {
      descriptors[keys[i]] = Object.getOwnPropertyDescriptor(obj, keys[i]);
    }
    return descriptors;
  };

var formatRegExp = /%[sdj%]/g;
exports.format = function(f) {
  if (!isString(f)) {
    var objects = [];
    for (var i = 0; i < arguments.length; i++) {
      objects.push(inspect(arguments[i]));
    }
    return objects.join(' ');
  }

  var i = 1;
  var args = arguments;
  var len = args.length;
  var str = String(f).replace(formatRegExp, function(x) {
    if (x === '%%') return '%';
    if (i >= len) return x;
    switch (x) {
      case '%s': return String(args[i++]);
      case '%d': return Number(args[i++]);
      case '%j':
        try {
          return JSON.stringify(args[i++]);
        } catch (_) {
          return '[Circular]';
        }
      default:
        return x;
    }
  });
  for (var x = args[i]; i < len; x = args[++i]) {
    if (isNull(x) || !isObject(x)) {
      str += ' ' + x;
    } else {
      str += ' ' + inspect(x);
    }
  }
  return str;
};


// Mark that a method should not be used.
// Returns a modified function which warns once by default.
// If --no-deprecation is set, then it is a no-op.
exports.deprecate = function(fn, msg) {
  if (typeof process !== 'undefined' && process.noDeprecation === true) {
    return fn;
  }

  // Allow for deprecating things in the process of starting up.
  if (typeof process === 'undefined') {
    return function() {
      return exports.deprecate(fn, msg).apply(this, arguments);
    };
  }

  var warned = false;
  function deprecated() {
    if (!warned) {
      if (process.throwDeprecation) {
        throw new Error(msg);
      } else if (process.traceDeprecation) {
        console.trace(msg);
      } else {
        console.error(msg);
      }
      warned = true;
    }
    return fn.apply(this, arguments);
  }

  return deprecated;
};


var debugs = {};
var debugEnvRegex = /^$/;

if (process.env.NODE_DEBUG) {
  var debugEnv = process.env.NODE_DEBUG;
  debugEnv = debugEnv.replace(/[|\\{}()[\]^$+?.]/g, '\\$&')
    .replace(/\*/g, '.*')
    .replace(/,/g, '$|^')
    .toUpperCase();
  debugEnvRegex = new RegExp('^' + debugEnv + '$', 'i');
}
exports.debuglog = function(set) {
  set = set.toUpperCase();
  if (!debugs[set]) {
    if (debugEnvRegex.test(set)) {
      var pid = process.pid;
      debugs[set] = function() {
        var msg = exports.format.apply(exports, arguments);
        console.error('%s %d: %s', set, pid, msg);
      };
    } else {
      debugs[set] = function() {};
    }
  }
  return debugs[set];
};


/**
 * Echos the value of a value. Trys to print the value out
 * in the best way possible given the different types.
 *
 * @param {Object} obj The object to print out.
 * @param {Object} opts Optional options object that alters the output.
 */
/* legacy: obj, showHidden, depth, colors*/
function inspect(obj, opts) {
  // default options
  var ctx = {
    seen: [],
    stylize: stylizeNoColor
  };
  // legacy...
  if (arguments.length >= 3) ctx.depth = arguments[2];
  if (arguments.length >= 4) ctx.colors = arguments[3];
  if (isBoolean(opts)) {
    // legacy...
    ctx.showHidden = opts;
  } else if (opts) {
    // got an "options" object
    exports._extend(ctx, opts);
  }
  // set default options
  if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
  if (isUndefined(ctx.depth)) ctx.depth = 2;
  if (isUndefined(ctx.colors)) ctx.colors = false;
  if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
  if (ctx.colors) ctx.stylize = stylizeWithColor;
  return formatValue(ctx, obj, ctx.depth);
}
exports.inspect = inspect;


// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
inspect.colors = {
  'bold' : [1, 22],
  'italic' : [3, 23],
  'underline' : [4, 24],
  'inverse' : [7, 27],
  'white' : [37, 39],
  'grey' : [90, 39],
  'black' : [30, 39],
  'blue' : [34, 39],
  'cyan' : [36, 39],
  'green' : [32, 39],
  'magenta' : [35, 39],
  'red' : [31, 39],
  'yellow' : [33, 39]
};

// Don't use 'blue' not visible on cmd.exe
inspect.styles = {
  'special': 'cyan',
  'number': 'yellow',
  'boolean': 'yellow',
  'undefined': 'grey',
  'null': 'bold',
  'string': 'green',
  'date': 'magenta',
  // "name": intentionally not styling
  'regexp': 'red'
};


function stylizeWithColor(str, styleType) {
  var style = inspect.styles[styleType];

  if (style) {
    return '\u001b[' + inspect.colors[style][0] + 'm' + str +
           '\u001b[' + inspect.colors[style][1] + 'm';
  } else {
    return str;
  }
}


function stylizeNoColor(str, styleType) {
  return str;
}


function arrayToHash(array) {
  var hash = {};

  array.forEach(function(val, idx) {
    hash[val] = true;
  });

  return hash;
}


function formatValue(ctx, value, recurseTimes) {
  // Provide a hook for user-specified inspect functions.
  // Check that value is an object with an inspect function on it
  if (ctx.customInspect &&
      value &&
      isFunction(value.inspect) &&
      // Filter out the util module, it's inspect function is special
      value.inspect !== exports.inspect &&
      // Also filter out any prototype objects using the circular check.
      !(value.constructor && value.constructor.prototype === value)) {
    var ret = value.inspect(recurseTimes, ctx);
    if (!isString(ret)) {
      ret = formatValue(ctx, ret, recurseTimes);
    }
    return ret;
  }

  // Primitive types cannot have properties
  var primitive = formatPrimitive(ctx, value);
  if (primitive) {
    return primitive;
  }

  // Look up the keys of the object.
  var keys = Object.keys(value);
  var visibleKeys = arrayToHash(keys);

  if (ctx.showHidden) {
    keys = Object.getOwnPropertyNames(value);
  }

  // IE doesn't make error fields non-enumerable
  // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
  if (isError(value)
      && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
    return formatError(value);
  }

  // Some type of object without properties can be shortcutted.
  if (keys.length === 0) {
    if (isFunction(value)) {
      var name = value.name ? ': ' + value.name : '';
      return ctx.stylize('[Function' + name + ']', 'special');
    }
    if (isRegExp(value)) {
      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
    }
    if (isDate(value)) {
      return ctx.stylize(Date.prototype.toString.call(value), 'date');
    }
    if (isError(value)) {
      return formatError(value);
    }
  }

  var base = '', array = false, braces = ['{', '}'];

  // Make Array say that they are Array
  if (isArray(value)) {
    array = true;
    braces = ['[', ']'];
  }

  // Make functions say that they are functions
  if (isFunction(value)) {
    var n = value.name ? ': ' + value.name : '';
    base = ' [Function' + n + ']';
  }

  // Make RegExps say that they are RegExps
  if (isRegExp(value)) {
    base = ' ' + RegExp.prototype.toString.call(value);
  }

  // Make dates with properties first say the date
  if (isDate(value)) {
    base = ' ' + Date.prototype.toUTCString.call(value);
  }

  // Make error with message first say the error
  if (isError(value)) {
    base = ' ' + formatError(value);
  }

  if (keys.length === 0 && (!array || value.length == 0)) {
    return braces[0] + base + braces[1];
  }

  if (recurseTimes < 0) {
    if (isRegExp(value)) {
      return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
    } else {
      return ctx.stylize('[Object]', 'special');
    }
  }

  ctx.seen.push(value);

  var output;
  if (array) {
    output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
  } else {
    output = keys.map(function(key) {
      return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
    });
  }

  ctx.seen.pop();

  return reduceToSingleString(output, base, braces);
}


function formatPrimitive(ctx, value) {
  if (isUndefined(value))
    return ctx.stylize('undefined', 'undefined');
  if (isString(value)) {
    var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
                                             .replace(/'/g, "\\'")
                                             .replace(/\\"/g, '"') + '\'';
    return ctx.stylize(simple, 'string');
  }
  if (isNumber(value))
    return ctx.stylize('' + value, 'number');
  if (isBoolean(value))
    return ctx.stylize('' + value, 'boolean');
  // For some reason typeof null is "object", so special case here.
  if (isNull(value))
    return ctx.stylize('null', 'null');
}


function formatError(value) {
  return '[' + Error.prototype.toString.call(value) + ']';
}


function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
  var output = [];
  for (var i = 0, l = value.length; i < l; ++i) {
    if (hasOwnProperty(value, String(i))) {
      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
          String(i), true));
    } else {
      output.push('');
    }
  }
  keys.forEach(function(key) {
    if (!key.match(/^\d+$/)) {
      output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
          key, true));
    }
  });
  return output;
}


function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
  var name, str, desc;
  desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
  if (desc.get) {
    if (desc.set) {
      str = ctx.stylize('[Getter/Setter]', 'special');
    } else {
      str = ctx.stylize('[Getter]', 'special');
    }
  } else {
    if (desc.set) {
      str = ctx.stylize('[Setter]', 'special');
    }
  }
  if (!hasOwnProperty(visibleKeys, key)) {
    name = '[' + key + ']';
  }
  if (!str) {
    if (ctx.seen.indexOf(desc.value) < 0) {
      if (isNull(recurseTimes)) {
        str = formatValue(ctx, desc.value, null);
      } else {
        str = formatValue(ctx, desc.value, recurseTimes - 1);
      }
      if (str.indexOf('\n') > -1) {
        if (array) {
          str = str.split('\n').map(function(line) {
            return '  ' + line;
          }).join('\n').substr(2);
        } else {
          str = '\n' + str.split('\n').map(function(line) {
            return '   ' + line;
          }).join('\n');
        }
      }
    } else {
      str = ctx.stylize('[Circular]', 'special');
    }
  }
  if (isUndefined(name)) {
    if (array && key.match(/^\d+$/)) {
      return str;
    }
    name = JSON.stringify('' + key);
    if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
      name = name.substr(1, name.length - 2);
      name = ctx.stylize(name, 'name');
    } else {
      name = name.replace(/'/g, "\\'")
                 .replace(/\\"/g, '"')
                 .replace(/(^"|"$)/g, "'");
      name = ctx.stylize(name, 'string');
    }
  }

  return name + ': ' + str;
}


function reduceToSingleString(output, base, braces) {
  var numLinesEst = 0;
  var length = output.reduce(function(prev, cur) {
    numLinesEst++;
    if (cur.indexOf('\n') >= 0) numLinesEst++;
    return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
  }, 0);

  if (length > 60) {
    return braces[0] +
           (base === '' ? '' : base + '\n ') +
           ' ' +
           output.join(',\n  ') +
           ' ' +
           braces[1];
  }

  return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
}


// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
exports.types = require('./support/types');

function isArray(ar) {
  return Array.isArray(ar);
}
exports.isArray = isArray;

function isBoolean(arg) {
  return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;

function isNull(arg) {
  return arg === null;
}
exports.isNull = isNull;

function isNullOrUndefined(arg) {
  return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;

function isNumber(arg) {
  return typeof arg === 'number';
}
exports.isNumber = isNumber;

function isString(arg) {
  return typeof arg === 'string';
}
exports.isString = isString;

function isSymbol(arg) {
  return typeof arg === 'symbol';
}
exports.isSymbol = isSymbol;

function isUndefined(arg) {
  return arg === void 0;
}
exports.isUndefined = isUndefined;

function isRegExp(re) {
  return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;
exports.types.isRegExp = isRegExp;

function isObject(arg) {
  return typeof arg === 'object' && arg !== null;
}
exports.isObject = isObject;

function isDate(d) {
  return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;
exports.types.isDate = isDate;

function isError(e) {
  return isObject(e) &&
      (objectToString(e) === '[object Error]' || e instanceof Error);
}
exports.isError = isError;
exports.types.isNativeError = isError;

function isFunction(arg) {
  return typeof arg === 'function';
}
exports.isFunction = isFunction;

function isPrimitive(arg) {
  return arg === null ||
         typeof arg === 'boolean' ||
         typeof arg === 'number' ||
         typeof arg === 'string' ||
         typeof arg === 'symbol' ||  // ES6 symbol
         typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;

exports.isBuffer = require('./support/isBuffer');

function objectToString(o) {
  return Object.prototype.toString.call(o);
}


function pad(n) {
  return n < 10 ? '0' + n.toString(10) : n.toString(10);
}


var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
              'Oct', 'Nov', 'Dec'];

// 26 Feb 16:19:34
function timestamp() {
  var d = new Date();
  var time = [pad(d.getHours()),
              pad(d.getMinutes()),
              pad(d.getSeconds())].join(':');
  return [d.getDate(), months[d.getMonth()], time].join(' ');
}


// log is just a thin wrapper to console.log that prepends a timestamp
exports.log = function() {
  console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
};


/**
 * Inherit the prototype methods from one constructor into another.
 *
 * The Function.prototype.inherits from lang.js rewritten as a standalone
 * function (not on Function.prototype). NOTE: If this file is to be loaded
 * during bootstrapping this function needs to be rewritten using some native
 * functions as prototype setup using normal JavaScript does not work as
 * expected during bootstrapping (see mirror.js in r114903).
 *
 * @param {function} ctor Constructor function which needs to inherit the
 *     prototype.
 * @param {function} superCtor Constructor function to inherit prototype from.
 */
exports.inherits = require('inherits');

exports._extend = function(origin, add) {
  // Don't do anything if add isn't an object
  if (!add || !isObject(add)) return origin;

  var keys = Object.keys(add);
  var i = keys.length;
  while (i--) {
    origin[keys[i]] = add[keys[i]];
  }
  return origin;
};

function hasOwnProperty(obj, prop) {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}

var kCustomPromisifiedSymbol = typeof Symbol !== 'undefined' ? Symbol('util.promisify.custom') : undefined;

exports.promisify = function promisify(original) {
  if (typeof original !== 'function')
    throw new TypeError('The "original" argument must be of type Function');

  if (kCustomPromisifiedSymbol && original[kCustomPromisifiedSymbol]) {
    var fn = original[kCustomPromisifiedSymbol];
    if (typeof fn !== 'function') {
      throw new TypeError('The "util.promisify.custom" argument must be of type Function');
    }
    Object.defineProperty(fn, kCustomPromisifiedSymbol, {
      value: fn, enumerable: false, writable: false, configurable: true
    });
    return fn;
  }

  function fn() {
    var promiseResolve, promiseReject;
    var promise = new Promise(function (resolve, reject) {
      promiseResolve = resolve;
      promiseReject = reject;
    });

    var args = [];
    for (var i = 0; i < arguments.length; i++) {
      args.push(arguments[i]);
    }
    args.push(function (err, value) {
      if (err) {
        promiseReject(err);
      } else {
        promiseResolve(value);
      }
    });

    try {
      original.apply(this, args);
    } catch (err) {
      promiseReject(err);
    }

    return promise;
  }

  Object.setPrototypeOf(fn, Object.getPrototypeOf(original));

  if (kCustomPromisifiedSymbol) Object.defineProperty(fn, kCustomPromisifiedSymbol, {
    value: fn, enumerable: false, writable: false, configurable: true
  });
  return Object.defineProperties(
    fn,
    getOwnPropertyDescriptors(original)
  );
}

exports.promisify.custom = kCustomPromisifiedSymbol

function callbackifyOnRejected(reason, cb) {
  // `!reason` guard inspired by bluebird (Ref: https://goo.gl/t5IS6M).
  // Because `null` is a special error value in callbacks which means "no error
  // occurred", we error-wrap so the callback consumer can distinguish between
  // "the promise rejected with null" or "the promise fulfilled with undefined".
  if (!reason) {
    var newReason = new Error('Promise was rejected with a falsy value');
    newReason.reason = reason;
    reason = newReason;
  }
  return cb(reason);
}

function callbackify(original) {
  if (typeof original !== 'function') {
    throw new TypeError('The "original" argument must be of type Function');
  }

  // We DO NOT return the promise as it gives the user a false sense that
  // the promise is actually somehow related to the callback's execution
  // and that the callback throwing will reject the promise.
  function callbackified() {
    var args = [];
    for (var i = 0; i < arguments.length; i++) {
      args.push(arguments[i]);
    }

    var maybeCb = args.pop();
    if (typeof maybeCb !== 'function') {
      throw new TypeError('The last argument must be of type Function');
    }
    var self = this;
    var cb = function() {
      return maybeCb.apply(self, arguments);
    };
    // In true node style we process the callback on `nextTick` with all the
    // implications (stack, `uncaughtException`, `async_hooks`)
    original.apply(this, args)
      .then(function(ret) { process.nextTick(cb.bind(null, null, ret)) },
            function(rej) { process.nextTick(callbackifyOnRejected.bind(null, rej, cb)) });
  }

  Object.setPrototypeOf(callbackified, Object.getPrototypeOf(original));
  Object.defineProperties(callbackified,
                          getOwnPropertyDescriptors(original));
  return callbackified;
}
exports.callbackify = callbackify;

}).call(this)}).call(this,require('_process'))
},{"./support/isBuffer":212,"./support/types":213,"_process":191,"inherits":172}],215:[function(require,module,exports){
(function (global){(function (){
'use strict';

var forEach = require('foreach');
var availableTypedArrays = require('available-typed-arrays');
var callBound = require('call-bind/callBound');

var $toString = callBound('Object.prototype.toString');
var hasSymbols = require('has-symbols')();
var hasToStringTag = hasSymbols && typeof Symbol.toStringTag === 'symbol';

var typedArrays = availableTypedArrays();

var $slice = callBound('String.prototype.slice');
var toStrTags = {};
var gOPD = require('es-abstract/helpers/getOwnPropertyDescriptor');
var getPrototypeOf = Object.getPrototypeOf; // require('getprototypeof');
if (hasToStringTag && gOPD && getPrototypeOf) {
	forEach(typedArrays, function (typedArray) {
		if (typeof global[typedArray] === 'function') {
			var arr = new global[typedArray]();
			if (!(Symbol.toStringTag in arr)) {
				throw new EvalError('this engine has support for Symbol.toStringTag, but ' + typedArray + ' does not have the property! Please report this.');
			}
			var proto = getPrototypeOf(arr);
			var descriptor = gOPD(proto, Symbol.toStringTag);
			if (!descriptor) {
				var superProto = getPrototypeOf(proto);
				descriptor = gOPD(superProto, Symbol.toStringTag);
			}
			toStrTags[typedArray] = descriptor.get;
		}
	});
}

var tryTypedArrays = function tryAllTypedArrays(value) {
	var foundName = false;
	forEach(toStrTags, function (getter, typedArray) {
		if (!foundName) {
			try {
				var name = getter.call(value);
				if (name === typedArray) {
					foundName = name;
				}
			} catch (e) {}
		}
	});
	return foundName;
};

var isTypedArray = require('is-typed-array');

module.exports = function whichTypedArray(value) {
	if (!isTypedArray(value)) { return false; }
	if (!hasToStringTag) { return $slice($toString(value), 8, -1); }
	return tryTypedArrays(value);
};

}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"available-typed-arrays":154,"call-bind/callBound":160,"es-abstract/helpers/getOwnPropertyDescriptor":162,"foreach":164,"has-symbols":168,"is-typed-array":176}]},{},[9])(9)
});
