// Use ordinary array, since untyped makes no boost here


/* =========================================================================
 * 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()).
 */


/* =========================================================================
 * 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.
 */


/* ===========================================================================
 * 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()).
 */


/* ===========================================================================
 * 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.
 */


/* ===========================================================================
 * 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).
 */


/* ===========================================================================
 * 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.
 */


/* ===========================================================================
 * 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.
 */


/* ===========================================================================
 * 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.
 */


/* ===========================================================================
 * 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.)
 */


/* ===========================================================================
 * 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.)
 */


/* ===========================================================================
 * Initialize the "longest match" routines for a new zlib stream
 */


// We have no pointers in JS, so keep tables separate


/*
 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.
 */


/* ===========================================================================
 * Output a short LSB first on the stream.
 * IN assertion: there is enough room in pendingBuf.
 */


/* ===========================================================================
 * Send a value on a given number of bits.
 * IN assertion: length <= 16 and value fits in length bits.
 */


/* ===========================================================================
 * Reverse the first len bits of a code, using straightforward code (a faster
 * method would use a table)
 * IN assertion: 1 <= len <= 15
 */


/* ===========================================================================
 * Flush the bit buffer, keeping at most 7 bits in it.
 */


/* ===========================================================================
 * 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.
 */


/* ===========================================================================
 * 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.
 */


/* ===========================================================================
 * Initialize the various 'constant' tables.
 */


/* ===========================================================================
 * Initialize a new block.
 */


/* ===========================================================================
 * Flush the bit buffer and align the output on a byte boundary
 */


/* ===========================================================================
 * Copy a stored block, storing first the length and its
 * one's complement if requested.
 */


/* ===========================================================================
 * Compares to subtrees, using the tree depth as tie breaker when
 * the subtrees have equal frequency. This minimizes the worst case length.
 */


/* ===========================================================================
 * 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).
 */


/* ===========================================================================
 * Send the block data compressed using the given Huffman trees
 */


/* ===========================================================================
 * 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.
 */


/* ===========================================================================
 * Scan a literal or distance tree to determine the frequencies of the codes
 * in the bit length tree.
 */


/* ===========================================================================
 * Send a literal or distance tree in compressed form, using the codes in
 * bl_tree.
 */


/* ===========================================================================
 * Construct the Huffman tree for the bit lengths and return the index in
 * bl_order of the last bit length code to send.
 */


/* ===========================================================================
 * 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.
 */


/* ===========================================================================
 * 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.
 */


/* ===========================================================================
 * Initialize the tree data structures for a new zlib stream.
 */


/* ===========================================================================
 * Send a stored block
 */


/* ===========================================================================
 * Send one empty static block to give enough lookahead for inflate.
 * This takes 10 bits, of which 7 may remain in the bit buffer.
 */


/* ===========================================================================
 * Determine the best encoding for the current block: dynamic trees, static
 * trees or store, and output the encoded block to the zip file.
 */


/* ===========================================================================
 * Save the match info and tally the frequency counts. Return true if
 * the current block must be flushed.
 */


//named nextTick for less confusing stack traces


// classy, since V8 prefers predictable objects.


/**
 * Converts a set of Longitude and Latitude co-ordinates to UTM
 * using the WGS84 ellipsoid.
 *
 * @private
 * @param {object} ll Object literal with lat and lon properties
 *     representing the WGS84 coordinate to be converted.
 * @return {object} Object literal containing the UTM value with easting,
 *     northing, zoneNumber and zoneLetter properties, and an optional
 *     accuracy property in digits. Returns null if the conversion failed.
 */


/**
 * Converts UTM coords to lat/long, using the WGS84 ellipsoid. This is a convenience
 * class where the Zone can be specified as a single string eg."60N" which
 * is then broken down into the ZoneNumber and ZoneLetter.
 *
 * @private
 * @param {object} utm An object literal with northing, easting, zoneNumber
 *     and zoneLetter properties. If an optional accuracy property is
 *     provided (in meters), a bounding box will be returned instead of
 *     latitude and longitude.
 * @return {object} An object literal containing either lat and lon values
 *     (if no accuracy was provided), or top, right, bottom and left values
 *     for the bounding box calculated according to the provided accuracy.
 *     Returns null if the conversion failed.
 */


/**
 * Encodes a UTM location as MGRS string.
 *
 * @private
 * @param {object} utm An object literal with easting, northing,
 *     zoneLetter, zoneNumber
 * @param {number} accuracy Accuracy in digits (1-5).
 * @return {string} MGRS string for the given UTM location.
 */


/**
 * Decode the UTM parameters from a MGRS string.
 *
 * @private
 * @param {string} mgrsString an UPPERCASE coordinate string is expected.
 * @return {object} An object literal with easting, northing, zoneLetter,
 *     zoneNumber and accuracy (in meters) properties.
 */


// Function to round decimals


// Function to construct Icon Marker


// Customized icon function


// Customized icon function for FEWS


// Show data panel


// Sphinx theme nav state


/**
 * A helper function to create the webpack config object to be sent to webpack module bundler.
 * @param {Object}  config configuration parameters used to create the webpack config object.
 * @param {string}  config.src  an array of source directories
 * @param {string}  config.firefly_root  Firefly's build root
 * @param {string}  [config.firefly_dir]  Firefly's JS source directory
 * @param {Object}  [config.alias]  additional alias
 * @param {boolean=true} [config.use_loader]  generate a loader to load compiled JS script(s).  Defaults to true
 * @param {string}  [config.project]  project name
 * @param {string}  [config.filename]  name of the generated JS script.
 * @param {string}  [config.baseWarName]  name of the the war file base, defaults to config.name
 * @param {function}  [config.doFirst]  execute with the original config param if given.
 * @param {function}  [config.doLast]   execute with the created webpack_config param if given.
 * @returns {Object} a webpack config object.
 *
 *
 * note- removed unmaintained webpack-visualizer-plugin, maybe replace with webpack-bundle-analyzer in the future
 */


/**
 * check with github for latest commit datetime
 */


/**
 * add options to store and setup any options that need specific initialization
 * @param {Object} appSpecificOptions
 */


/*
 *
 * @param {string} divId
 * @param {AppProps} props
 * @return {Object} return object has two functions {unrender:Function, render:Function}
 */


/**
 * bootstrap Firefly api or application.
 * @param {AppProps} props - application properties
 * @param {FireflyOptions} clientAppSpecificOptions - firefly options specific to this client
 * @param {Array.<WebApiCommand>} webApiCommands
 * @returns {Promise.<boolean>}
 */


/**
 *
 * @param ops
 * @param overrideOps
 */


/**
 *
 * @param {Array.<WebApiCommand>} commandsAry
 * @param {String} [overrideUrl] - is passed use this instead of location
 * @return {UrlApiStatus}
 */


/**
 *
 * @param {WebApiCommand} urlApiCmd
 * @param {Object} params
 * @param {Object} originalParams
 * @param {Array.<string>} inBadParams
 * @return {{valid: boolean, invalidStatObj: {helpType: string, cmd: (string|string), params: *, status: string}}|*}
 */


/**
 *
 * @param {Object} passedParams - parameters from url
 * @param {Array} parameters - possible parameter list
 * @return {Array.<string>} return an array of missing parameters names
 */


/**
 *
 * @param {string} desc
 * @param {string} cmd
 * @param {Object.<String,String>} [params] - key is the param, value is the param value
 * @return {{url: string, desc: string}}
 */


/**
 * @param llApi the lowlevel api
 * @returns {Object}
 * @ignore
 *
 */


/**
 * Build the deprecated API
 * @param llApi
 * @returns {Object}
 * @ignore
 */


/*---------------------------- TABLE PART >----------------------------*/


/**
 * Build the deprecated API
 * @param llApi
 * @returns {Object}
 * @deprecated
 * @ignore
 */


/**
 * Start in api mode. Will create the api and call window.onFireflyLoaded(firefly)
 * @ignore
 */


/**
 * Return the api object.
 * @return {{action:{},ui:{},util:{}}}
 * @ignore
 */


/**
 * pull all the dispatch functions out of the object
 * @param {Object} obj
 * @return {*}
 * @ignore
 */


/**
 * pull all the action type constants out of the object
 * @param {Object} obj
 * @param {string} prefix
 * @return {*}
 * @ignore
 */


/**
 * @param {{chartId: string, data: array.object, layout: object}} params - chart parameters
 * @param {string} viewerId - ignored in triview mode
 * @param {Function} dispatch - dispatch function
 */


/**
 * @param {XYPlotOptions} params - XY plot parameters
 * @param {string} viewerId - ignored in triview mode
 * @param {Function} dispatch - dispatch function
 */


/**
 * @param {HistogramOptions} params - histogram parameters
 * @param {string} viewerId - ignored in triview mode
 * @param {Function} dispatch - dispatch function
 */


/**
 *
 * @param {string|Object} div a div element or a string id of the div element
 * @param {Object} Component a react component
 * @param {Object} [props] props for the React component
 * @param {boolean} wrapWithFireflyRoot wrap component in FireflyRoot
 * @public
 * @function renderDOM
 * @memberof firefly.util
 */


/**
 * @param {string|Object} div a div element or a string id of the div element
 * @public
 * @function unrenderDOM
 * @memberof firefly.util
 */


/**
 * @summary Add a listener to any action type
 * @param {string} actionType a string or an array of strings. Each string is an action constant from firefly.action.type
 * @param {function} callBack the call back will be called with three parameters: action object, state object, and extraData
 *                 If it returns true the listener will be removed.
 * @param {object} extraData an object with data to send to the callback, can be anything
 *
 * @return {function} a function that will remove the listener
 * @public
 * @func addActionListener
 * @memberof firefly.util
 */


/**
 * Define a rectangle object
 * @param x - the x coordinate
 * @param y - the y coordinate
 * @param width - the width of the rectangle
 * @param height - the height of the rectangle
 * @constructor
 */


/**
 * walk around four corners to find the proper ranges
 * @param intervals
 * @param x0 - the starting x value of the corner
 * @param y0 - the starting y value of the corner
 * @param dx - the increment
 * @param dy - the increment
 * @param range - the x and y rangs
 * @param csys - the coordinate system the grid is drawing with
 * @param wrap - boolean value, true or false
 * @param cc - the CoordinateSys object
 */


/**
 * get the ege value
 * @param intervals - the number of interval to look for the values
 * @param width - the width of the image
 * @param height - the height of the image
 * @param csys - the coordinate system the grid is drawing with
 * @param wrap - boolean value, true or false
 * @param cc - the CoordinateSys object
 * @returns the value at the edge of the image
 */


/**
 * Test to see if the edge is a real one
 * @param xrange
 * @param trange
 * @returns {boolean}
 */


/**
 * For image map, the interval is hard coded as 4 in the original version (java).  I think that
 * since each image only covers a small stripe of the sky, the range of longitude usually less than 1 degree.
 * Howver for HiPs map, the range is whole sky (0-360).  The hard coded interval 4 is not good enough to find the
 * good points.
 *
 * For HiPs map, I use the interval = longitude-range/0.5, and interval = latitude-range/0.5.  Thus, more points
 * are checked and found for each line.
 *
 * @param cc
 * @param csys
 * @param intervals
 * @param x0
 * @param y0
 * @param dx
 * @param dy
 * @param opoints
 * @returns {*}
 */


/*
 Use two workspace points to find the slop of the line, the angle = arctan(slope) are in the range [-90, 90].
 For all lon(or lat) lines, the slope may be positive or negative.  The text label is placed above the grid lines
 if the angle is positive and under the line if the angle is negative. In such case, if some lon lines have positive
 angle and some has negative angle, the negative angle is changed to 180+angle which is postive.  Thus, the label text
 are all placed in the same manner.

 check to see if all angles to make sure they are all the same positive or negative.
 If any of them are not the same sign as the other, some of the labels are above the line and some are under the line
 because the native angle.  Since  arctan(a) lies [-90, 90] and the tag(a)=tag(a+n*PI), if a <0, use 180+a
 */


/**
 * create region plot layer
 * @param initPayload in region plot layer, attribute regionAry: region description array
 *                                 dataFrom: regionAry is from 'json' object or 'ds9' string
 *                                 regions:   array of region object constructed by parsing regionAry
 *                                 regionObjAry: array of drawing object constructed from regions
 *                                 => regions and regionObjAry are updated as adding or removing regions occurs
 *                                 highlightedRegion: selected region
 * @return {DrawLayer}
 */


/**
 * find the drawObj which is selected for highlight
 * @param mouseStatePayload
 * @returns {Function}
 */


/**
 * @summary create DrawingObj for all regions
 * @param {Object} dl    drawing layer
 * @returns {Object[]}
 */


/**
 * @summary create DrawingObj for highlighted region
 * @param {Object} highlightedObj
 * @param {string} plotId
 * @param {Object} selectMode
 * @returns {Object[]}
 */


/**
 * @summary add new DrawingObj into originally displayed DrawingObj set
 * @param {Object} drawLayer
 * @param {string|string[]} addedRegions
 * @returns {Object[]}
 */


/**
 * remove DrawingObj from originally displayed DrawingObj set
 * @param {Object} drawLayer
 * @param {string|string[]} removedRegions
 * @returns {Object[]}
 */


/**
 * create region plot layer
 * @param initPayload moc_nuniq_nums, highlightedCell, selectMode
 * @return {DrawLayer}
 */


/**
 * create MocObj base on cell nuniq numbers and the coordinate systems
 * @param dl
 * @param moc_nuniq_nums
 * @param mocCsys
 * @returns {Object}
 */


/**
 * update MOC draw data at specific intervals
 * @param {DrawLayer} inDrawLayer
 * @param {String} plotId
 */


/**
 * start producing draw data at specific intervals
 * @param drawLayerId
 * @param plotId
 * @returns {Function}
 */


/**
 * dispatch update drawlayer until all draw data is produced
 * @param drawObjAry
 * @param drawLayer
 * @param plotId
 */


/**
 * complete the async drow object computation and update the draw layer
 * @param dl - the droaw layer, if not defined then just end the async task
 * @param updateStatusAry
 * @param pId
 * @param updateMethod
 */


/**
 * produce the draw data in async style
 * @param drawLayer
 * @param action
 */


/**
 *  @param  params
 * @return {Function}
 */


/**
 * action creator for MARKER_START, only when the cursor shows 'pointer"
 * @param rawAction
 * @returns {Function}
 */


/**
 * action creator for MARKER_END
 * @param rawAction
 * @returns {Function}
 */


/**
 * action create for MARKER_MOVE
 * @param rawAction
 * @returns {Function}
 */


/**
 * create drawing layer for a new marker
 * @param initPayload
 * @returns {Function}
 */


/**
 * update text in marker object
 * @param text
 * @param textLoc
 * @param markerDrawObj
 * @param plotIdAry
 * @returns {*}
 */


/**
 * make rectangle with the same width and height
 * @param size
 * @returns {Array}
 */


/**
 * dispatch action to locate marker with corners and no corner by timer interval on the drawing layer
 * @param dispatcher
 * @param actionType
 * @param plotId
 * @param doneStatus
 * @param timer  milliseconds
 * @param drawLayerId
 * @param isHandle
 * @param wpt    marker location world coordinatee
 * @param size   in screen coordinate
 * @param unitT  unit of size
 * @param refPt
 * @param move
 */


/**
 * calculate vertex distance for catching the marker
 * @param markObj
 * @param plotId
 * @param dlObj
 * @returns {{vertexDef: *, exclusiveDef: *}}
 */


/**
 * create drawlayer object containing only the properties which has updated value
 * @param action
 * @param dl
 * @param plotId
 * @param wpt
 * @param size
 * @param prevRet previous return object
 * @param unitT marker size unit
 * @returns {*}
 */


/**
 * update the location of marker/footprint in sync with the relocation of the marker/footprint in the active plot
 * when it is first formed (the stage that the status is changed from 'attached' to 'attached_relocate')
 * @param markObj marker/footprint object
 * @param move    object with translation info.
 * @param cc
 * @returns {*}
 */


/**
 * update the handle inclusion on the object
 * @param isHandle
 * @param markerObj
 */


/**
 * add the marker drawing objects into the new plot created after the drawing layer is created
 * @param drawLayer
 * @param newPlotId new plot to attach
 * @returns {*}
 */


/**
* action creator for FOOTPRINT_START, only when the cursor shows 'pointer"
* @param rawAction
* @returns {Function}
*/


/**
 * action creator for FOOTPRINT_END
 * @param rawAction
 * @returns {Function}
 */


/**
 * action create for FOOTPRINT_MOVE
 * @param rawAction
 * @returns {Function}
 */


/**
 * create drawing layer for a new marker
 * @param {object} initPayload
 * @return {Function}
 */


/**
 * set footprint rotate angle
 * @param angleDegStr
 * @param footprintDrawObj
 * @param plotIdAry
 * @returns {*}
 */


/**
 * dispatch action to locate marker with corners and no corner by timer interval on the drawing layer
 * @param dispatcher
 * @param actionType
 * @param regions regions contained in footprint drawObj
 * @param plotId
 * @param doneStatus
 * @param timer  milliseconds
 * @param drawLayerId
 * @param isHandle
 * @param fpInfo
 * @param wpt    marker location world coordinate
 * @param refPt
 * @param move
 */


/**
 * create drawlayer object containing only the properties which has updated value
 * @param action
 * @param dl
 * @param plotId
 * @param wpt
 * @param prevRet previous return object
 * @returns {*}
 */


/**
 * get rotation center
 * @param drawObj
 * @param wpt
 * @returns {*}
 */


/**
 * update the handle inclusion on the footprint object
 * @param isHandle
 * @param footprintObj
 */


/**
 * add the footprint drawing objects into the new plot after the drawing layer is created
 * @param drawLayer
 *  @param newPlotId new plot to attach
 * @returns {*}
 */


/**
 * This function is mapped to the mouse down key
 * @param mouseStatePayload
 */


/**
 * looks for a point to highlight. Use a interval to keep from locking up the event loop
 * Ends with a dispatch call
 * @param drawLayer
 * @param plotId
 * @param screenPt
 * @returns {function()}
 */


/**
 *
 * @param drawLayer
 * @param tableData
 * @param columns
 * @returns {Array} build and return an array of PointDataObj for drawing.
 */


/**
 *
 * @param drawLayer
 * @param columns
 * @returns {Object[]} return a array of PointDataObj that represents the highlighted object
 */


/**
 * @param {Point} firstPt
 * @param {Point} currPt
 * @param  drawAry
 * @return {object}
 */


/**
 * @param {Point} firstPt
 * @param {Point} currentPt
 * @param {Point} activePt
 * @param {String} selectionType
 * @param {boolean} offsetCal
 * @param {CysConverter} cc
 * @return {Array}
 */


/**
 * create drawing object and title object based on the statical or dynamical settings.
 * drawObj could be either statically set by dl.drawObj or dynamically set by plot attribute,
 * i.e. PlotAttribute.OUTLINEIMAGE_DRAWOBJ or PlotAttribute.OUTLINEIMAGE_BOUNDS
 * title could be either statically set by dl.text or dynamically set by attribute
 * PlotAttribute.OUTLINEIMAGE_TITLE
 * text location could be either statically set by dl.textLoc or dynamically set by attribute
 * PlotAttribute.OUTLINEIMAGE_TITLELOC
 * @param dl
 * @param plot
 * @returns {*}
 */


/**
 * @param initPayload containing the setting for handle color, selected shape like 'rectangle', 'circle'
 * @return {Function}
 */


/**
 * @param {object} originalCenterPt
 * @param {object} inFirstPt
 * @param {object} inCurrentPt
 * @param {WebPlot} plot
 * @param {boolean} rotation is plot rotated
 * @param {string} title
 * @param {object} dl
 * @return {Array}
 */


/**
 * check if the point is below the line by placing x & y with p.x & p.y in
 * ax+by+c formed by two ends of the line and check the value after that
 * @param line_pt1
 * @param line_pt2   point of greater  y
 * @param pt
 * @param cc
 * @returns {boolean}
 */


/**
 * @param {object} firstPt
 * @param {object} currentPt
 * @param {boolean} offsetCal
 * @param {CysConverter} cc
 * @return {Array}
 */


/**
 * @summary create DrawingObj for highlighted region
 * @param {object} drawLayer
 * @param {Object} highlightedFootprint object for ConnectedObj
 * @param {string} plotId
 * @param {Object} drawingDef
 * @returns {Object[]}
 */


// every draw layer must default export with factoryDef and TYPE_ID


/**
 *
 * @param {DrawLayer} dl
 * @param {WebPlot} p
 * @param {Object} sel
 * @param {String} selectedShape
 * @return {Promise<{foundPts: boolean, dispatchSelect: Function}>}
 */


/**
 *
 * @param {DrawLayer} dl
 * @param {WebPlot} p
 * @param {Object} sel
 * @param {String} selectedShape
 * @return {Promise<{foundPts: boolean, dispatchFilter: Function}>}
 */


/**
 *
 * @param groupKey
 * @param includeUnmounted
 * @return Promise with the valid state true/false --- todo: the return value does not work
 */


/**
 *
 * @param groupKeyAry
 * @param includeUnmounted
 */


/**
 *
 * @param {string} groupKey
 * @param {function} stateUpdaterFunc this is a functions takes a field object as a parameter and should update the
 *                   react components state
 * @return {function} a function that will unbind the store, should be called on componentWillUnmount
 */


/**
 * Revalidate fields. If no change happened in a field state, its reference does not change
 * @param fields
 */


/**
 *
 * @param {Action} rawAction
 * @return {Function}
 */


/**
 * main reducer
 * @param {Object} state
 * @param {Action} action
 * @return {Object} return state
 */


/**
 * @param {Object} state
 * @param {Action} action
 */


/**
 * @param {object} fg the field group
 * @param {Action} action - the action to fire
 * @return {Object} the fields
 * fire the reducer for field group if it has been defined
 */


/**
 * Returns old fields unless some attribute has changed.
 * @param oldFields
 * @param newFields
 * @returns {*}
 */


/**
 * @param {object} state
 * @param {string} groupKey
 * @param {object} newValues any value replacements
 */


/**
     * Parses the string argument into a ServerRequest object.
     * This method is reciprocal to toString().
     * @param str
     * @param req
     * @return {ServerRequest} the passed request
     */


/**
 *
 * @param {WorkerAction} action
 * @return {Promise<unknown>}
 */


/**
 * applies token substitution if any.  If the resulting value is nullish, return the def val.
 * @see {@link http://www.ivoa.net/documents/VOTable/20130920/REC-VOTable-1.3-20130920.html#ToC54}
 * A.1 link substitution
 * @param tableModel    table model with data and columns info
 * @param val           the value to resolve
 * @param rowIdx        row index to be resolved
 * @param def           return value if val is nullish
 * @param encodeValues  encodeURIComponent the values.  default to false.
 * @returns {string}    the resolved href after substitution
 */


/**
 * check if ucd value contains the searched ucd word at the right position
 * @param ucdValue
 * @param ucdWord
 * @param syntaxCode 'P': only first word, 'S': only secondary, 'Q' either first or secondary
 */


/**
 * @param {Object} p
 * @param p.semantics
 * @param p.localSemantics
 * @param p.contentQualifier
 * @param p.contentType
 * @param p.sourceObsCoreData
 * @return {DlAnalysisData}
 */


/**
 * Check to see if the file analysis report indicates the file is a service descriptor
 * @param {FileAnalysisReport} report
 * @returns {boolean} true if the file analysis report indicates a service descriptor
 */


/**
 * return true if all columns include the obs tap required columns
 * @param columnsModel
 * @return {boolean}
 */


/**
     * search center columns pair by guessing the column name
     * @param {boolean} acceptArrayCol - if true then allow of a single column with an array entry for RA and Dec
     * @returns {null|CoordColsDescription}
     */


/**
 * find the center column base on the table model of catalog or image metadata
 * Investigate table meta data a return a CoordColsDescription for two columns that represent and object in the row
 * @param {TableModel|String} tableOrId - a table model or a table id
 * @param {boolean} acceptArrayCol - if true then allow of a single column with an array entry for RA and Dec
 * @return {CoordColsDescription|null|undefined}
 */


/**
 * If there are center columns defined with this table then return a WorldPt
 * @param {TableModel|String} tableOrId - a table model or a table id
 * @param row
 * @return {WorldPt|undefined} a world point or undefined it no center columns exist
 */


/**
 * @summary check if there is center column or corner columns defined, if so this table has coverage information
 * @param {TableModel|String} tableOrId - a table model or a table id
 * @returns {boolean} True if there  is coverage data in this table
 */


/**
 * Test to see it this is a catalog. A catalog must have one of the following:
 *  - CatalogOverlayType meta data entry defined and not equal to 'FALSE' and we must be able to find the columns
 *                            either by meta data or by guessing
 *  - We find the columns by some vo standard
 *
 *  Note- if the CatalogOverlayType meta toUpperCase === 'FALSE' then we will treat it as not a catalog no matter how the
 *  vo columns might be defined.
 *
 * @param {TableModel|String} tableOrId - a table model or a table id
 * @return {boolean} True if the table is a catalog
 * @see MetaConst.CATALOG_OVERLAY_TYPE
 */


/**
 * Return true this table can be access as ObsCore data
 * @param {TableModel|String} tableOrId
 * @return {boolean}
 */


/**
 * Return true if this table has a datalink service descriptor (treat it like obscore-type table in this case)
 * @param {TableModel|String} tableOrId
 * @return {boolean}
 */


/**
 * Guess if this table contains image meta data. It contains image meta data if IMAGE_SOURCE_ID is defined
 * or a DATA_SOURCE column name is defined, or it is an obscore table, or it has service descriptors
 * @param {TableModel|String} tableOrId - a table model or a table id
 * @return {boolean} true if there is image meta data
 * @see MetaConst.DATA_SOURCE
 * @see MetaConst.IMAGE_SOURCE_ID
 */


/**
 * Guess if this table has enough ObsCore attributes to be considered an ObsCore table.
 * - any column contains utype with 'obscore:' prefix
 * - matches 3 or more of ObsCore column names
 * @param {TableModel} tableModel
 * @returns {boolean}
 */


/**
 * @summary Convert shallow object with XYPlot parameters to scatter plot parameters object.
 * @param {XYPlotOptions} params - shallow object with XYPlot parameters
 * @returns {Object} - object, used to create Plotly chart
 * @public
 * @function makeXYPlotParams
 * @deprecated
 * @memberof firefly.util.chart
 */


/**
 * @summary Convert shallow object with Histogram parameters to histogram plot parameters object.
 * @param {HistogramOptions} params - shallow object with Histogram parameters
 * @returns {HistogramParams} - object, used to create Histogram chart
 * @public
 * @function makeHistogramParams
 * @deprecated
 * @memberof firefly.util.chart
 */


// walk through object and replace values where there's an array with only the selected indexes.


/**
 * Get trace annotation as a one level deep array
 * @param {array} annotations - trace annotations (there could be none, a single, or an array of annotations per point
 */


/**
 *
 * @param {object} p
 * @param {string} p.chartId
 * @param {object[]} p.data
 * @param {object[]} p.fireflyData
 */


/**
 * Get feedback as boolean on whether chart is fully loaded or not
 * @param chartId - chartId of the current chart
 */


/**
 * set default value for layout and data
 * @param chartData
 * @param resetColor reset color generator for default color assignment of the chart
 */


/**
 * Convert column expression to internal (database) syntax
 * @param {object} p
 * @param {string} p.colOrExpr - column expression
 * @param {boolean} p.quoted - if true, quote variable names
 * @param {string[]} p.colNames - valid column names
 * @returns {*}
 */


/*
 * check if the trace is not 3d-like chart or pie and has x and y defined
*/


/**
 * This implementation prioritizes performance over robustness.
 * It only handles plain objects and arrays, skipping more complex cases such as Map, Set, Date, RegExp, and others.
 * Also, it does not include type checking or manage circular references.
 * @param {object} obj
 * @returns {object} a deep clone of the given object
 */


/**
 * Move firefly attributes from data and layout objects to fireflyData and fireflyLayout
 * @param payload - original action payload
 * @return updated action payload
 */


/**
 * Convert deprecated title attributes: string title and titlefont
 * into title: {text, font}
 * Updates payload in place, if an update is necessary
 * @param payload
 */


/*
 Possible structure of store:
  /ui
    expanded: Object - the information about expanded chart
    {
         chartId: string
    }
  /data - chart data, object with the keys that are chart id
    chart data
*/


/**
 * @param state - ui part of chart state
 * @param action - action
 * @returns {*} - updated ui part of the state
 */


/**
 * @callback actionWatcherCallback
 * @param action
 */


/*
 * Get the number of points, min and max values, units and description for each table column
 * @param {ServerRequest} searchRequest - table search request
 * @param {function} dispatcher only for special dispatching uses such as remote
 */


/**
 * fetches histogram data for a column or column expression of an active table
 * set isColStatsReady to true once done.
 * @param dispatch
 * @param activeTableServerRequest table search request to obtain source table
 */


/**
 * Firefly added scatter changes
 * @param {Object} p - parameters
 * @param p.changes existing changes
 * @param p.chartId chart id
 * @param p.traceNum
 * @param p.tablesource
 * @param p.tableModel - original table model
 */


/**
 * Return true if 'from' unit can be converted to 'to'.
 * @param p
 * @param p.from
 * @param p.to
 * @returns {boolean}
 */


/**
 * Maps any unit representation (value/label/alias) back to a key in UnitXref (and UnitMetadata). Also returns the
 * scalar factor if present.
 * @param u {string} - the unit to normalize
 * @return {{unit: UnitKey|null, factor: string}} - the key in UnitXref if found, otherwise null and the scalar factor.
 */


/**
 * Decomposes a unit string into its base units and their powers.
 * @param unit {string} - the unit string to decompose, e.g. 'erg/s/cm^2/A'
 * @returns {Map<any, any>} - a Map where keys are base units and values are their powers, e.g.
 * `Map{'erg'=> 1, 's'=> -1, 'cm'=> -2, 'A'=> -1}`
 */


/*
 * Expecting an 2 dimensional array of numbers
 * each row is an array of 3 values:
 * [0] number of points in a bin,
 * [1] minimum of a bin
 * [2] maximum of a bin
 */


/**
 * Returns object with changes
 * @param p
 * @param p.data
 * @param p.fireflyData
 * @param p.layout
 * @return changes
 */


// positive and bigger than 1 when yaxis is on the right (opposite)


/**
 * 2D transformation matrix object initialized with identity matrix.
 *
 * The matrix can synchronize a canvas 2D context by supplying the context
 * as an argument, or later apply current absolute transform to an
 * existing context.
 *
 * To synchronize a DOM element you can use [`toCSS()`]{@link Matrix#toCSS} or [`toCSS3D()`]{@link Matrix#toCSS3D}.
 * together with for example the `style.transform` property.
 *
 * @param {CanvasRenderingContext2D} [context] - Optional context to sync with Matrix
 * @param {HTMLElement} [element=null] - DOM Element to synchronize
 * @prop {number} a - scale x
 * @prop {number} b - shear y
 * @prop {number} c - shear x
 * @prop {number} d - scale y
 * @prop {number} e - translate x
 * @prop {number} f - translate y
 * @prop {CanvasRenderingContext2D} [context] - set or get current synchronized 2D context
 * @prop {HTMLElement} [element] - get current synchronized DOM element
 * @prop {boolean} [useCSS3D=false] - is a DOM element is defined for sync., choose whether to use 2D (false) or 3D (true) matrix to sync it.
 * @constructor
 * @license MIT license
 * @copyright Epistemex.com 2014-2018
 */


/**
	 * Computes the projection of 1 point : ra,dec => X,Y
	 * alpha, delta = longitude, latitude
	 */


/**
	 * Computes the coordinates from a projection point : X,Y => ra,dec
	 * return o = [ ra, dec ]
	 */


/**
	 * Compute projections from unit vector
	 * The center of the projection correspond to u = [1, 0, 0)
	 * proj = projection system (integer code like _PROJ_MERCATOR_
	 * u[3] = unit vector
	 * return: an array [x,y] or null
	 */


/**
	 * Computes Unit vector from a position in projection centered at position (0,0).
	 * proj = projection code
	 * X,Y : coordinates of the point in the projection
	 * returns : the unit vector u[3] or a face number for cube projection.
	 *           null if the point is outside the limits, or if the projection is unknown.
	 */


/**
	 * Creates the rotation matrix R[3][3] defined as
	 * R[0] (first row) = unit vector towards Zenith
	 * R[1] (second row) = unit vector towards East
	 * R[2] (third row) = unit vector towards North
	 * o[2] original angles
	 * @return rotation matrix
	 */


/**
	 * Transformation from polar coordinates to Unit vector
	 * @return U[3]
	 */


/**
	 * Rotates the unit vector u1 using the rotation matrix
	 * u1[3] unit vector
	 * R[3][3] rotation matrix
	 * return resulting unit vector u2[3]
	 */


/**
	 * reverse rotation the unit vector u1 using the rotation matrix
	 * u1[3] unit vector
	 * R[3][3] rotation matrix
	 * return resulting unit vector u2[3]
	 */


/**
	 * Computes angles from direction cosines
	 * u[3] = direction cosines vector
	 * return o = [ ra, dec ]
	 */


/**
     *
     * @param {number} pixsize
     * @return {number}
     */


/**
     * Returns a range set of pixels whose centers lie within a given disk. <p>
     *  This method is more efficient in the RING scheme.
     *  @param {SpatialVector} spatialVector the angular coordinates of the disk center
     *  @param {number} radius the radius (in radians) of the disk
     *  @param {boolean} nest true if nest, false if ring
     *  @param {boolean} inclusive If False, return the exact set of pixels whose pixel centers lie
     *       within the disk; if True, return all pixels that overlap with the disk,
     *  @return {Array.<number> }the requested set of pixel number ranges
     */


// Current language at caret position


// Replace currently selected text


/**
	 * Wrap text with strings
	 * @param before {String} The text to insert before
	 * @param after {String} The text to insert after
	 * @param start {Number} Character offset
	 * @param end {Number} Character offset
	 */


// Utility method to get regex matches


/**
 * Upload a URL, File, Blob, or WebPlotRequest to the server, using AnyFileUpload on the firefly server side.
 * Determine how to upload based on what is passed. It may be called with a URL, Blob, File, or WebPlotRequest
 * @param {WebPlotRequest|Blob|File|String} item - if string the interpret as a URL, if is an object and has WebPlotRequest function then
 * interpret as a WebPlotRequest, Otherwise interpret as a file or blob
 * @param {String|Boolean} fileAnalysis
 * @param {Object} params - any extra parameters to the upload
 * @return {Promise}
 */


/**
 * return the correct upload parameter based on the type that is passed
 * @param {WebPlotRequest|Blob|File|String} item - the parameter to evaluate
 * @return {Object|undefined} an object with the correct parameter to pass to the upload,
 * or undefined if it is not the correct parameter type
 */


/**
 * parse an upload result
 * @param {String} text - the result text from the upload call
 * @return {{cacheKey: String, analysisResult: String, message: String, status: String}}
 */


/**
 * tableRequest will be sent to the server as a json string.
 * @param {TableRequest} tableRequest is a table request params object
 * @param {number} [hlRowIdx] set the highlightedRow.  default to startIdx.
 * @returns {Promise.<TableModel>}
 * @public
 * @func doFetchTable
 * @memberof firefly.util.table
 */


/**
 * a utility function used to query data from the given tableRequest without altering the table.
 * @param {TableRequest} tableRequest is a table request params object
 * @param {TableRequest} queryRequest filters, sortInfo, and inclCols are used on the tableRequest to return the results
 * @returns {Promise.<number>}
 */


/**
 * add a column to the table backed by this tableRequest
 * @param {TableRequest} tableRequest is a table request params object
 * @param colAttribs      column attributes.  i.e. name, type, expression, etc
 * @returns {Promise.<number>}
 */


/**
 * delete a column from the table backed by this tableRequest
 * @param {TableRequest} tableRequest is a table request params object
 * @param cname      column name to delete
 * @returns {Promise.<number>}
 */


/**
 * returns the table data for the given parameters
 * @param {Object} p  parameters object
 * @param {string[]} p.columnNames an array of column names
 * @param {TableRequest} p.request   location of the file on the server
 * @param {string} p.selectedRows   a comma-separated string of indices of the rows to get the data from
 * @return {Promise<TableModel>}
 */


/**
 *
 * @param {string} channel
 * @param {string} desc
 * @param {string} data
 * @return {Promise}
 */


/**
 *
 * @param jobId
 * @param fname
 * @param dataSource
 * @param {array} attributes and array of ScriptAttributes
 * @return {Promise}
 */


/**
 * @param {WebPlotRequest} redRequest
 * @param {WebPlotRequest} greenRequest
 * @param {WebPlotRequest} blueRequest
 * @return {Promise}
 */


/**
 *
 * @param {Array.<string>|string} imageSources -
 * @param {Array.<string>|string} projectSortOrder -  a arrays of projects that you want on top, any project not in
 * list will be add at the bottom
 * @return {Promise} the json image data
 */


/**
 * @param {Array} startAry
 * @param {boolean} includeDirectAccessData
 * @return {Array}
 */


/**
 * @summary Add a table type watcher definition. Test every table (existing or new) and if the test is passed then create
 * a table type watcher for that table.
 *
 * @description A table type watcher, by using the payload definition will start a single watch for ever table that is newly loaded
 * or previously loaded that passes the 'testTable' function test.
 * These watchers will have the same life time of the table they are watching.
 * A  TableType watcher is like a normal watcher with following exceptions:
 * <ul>
 * <li> tbl_id is first parameter.
 * <li> The params object with always include a options and a sharedData property. They may be undefined.
 * <li> The options object should be thought of as read only. It should not be modified.
 * <li> It is call the first time with the action undefined.  This can be for initialization.  The table will beloaded
 * </ul>
 *
 * @param {TableWatcherDef} def
 *
 * @see TableWatchFunc
 * @see TestWatchFunc
 *
 * @public
 * @function dispatchAddTableTypeWatcherDef
 * @memberof firefly.action
 */


/**
 * This saga launches all the predefined Sagas then loops and waits for any ADD_SAGA actions and launches those Segas
 */


/**
 * Notify other apps this app is ready
 */


/**
 * execute this callback when app is ready.
 * @param {function} callback
 */


/**
 * Given a channel string return a version of the will be the channel for a viewer
 * @param {String} channel
 * @return {string}
 */


/**
 * Action watcher callback. Watches for app_data.isReady.
 * When app data are loaded, it will execute the callback passed with params with the current state.
 * @callback actionWatcherCallback
 * @param action
 * @param cancelSelf
 * @param params
 * @param {function} params.callback function to execute on completion
 * @param {function} dispatch
 * @param {function} getState
 */


/**
 * This handles the general use case of the drop-down panel.
 * It will collapse the drop-down panel when new tables or images are added.
 * It will expand the drop-down panel when there is no results to be shown.
 * @param {LayoutInfo} layoutInfo
 * @param {Action} action
 * @returns {LayoutInfo}  return new LayoutInfo if layout was affected.  Otherwise, return the given layoutInfo.
 */


/**
 * This handles the general use case of the drop-down panel.
 * It will collapse the drop-down panel when new tables or images are added.
 * It will expand the drop-down panel when there is no results to be shown.
 */


/**
 *
 * @param {string} url
 * @param params
 * @param {boolean} doPost
 * @param {boolean} useBigInt       // use BigInt supported json parser
 * @return {Promise} a promise with the results
 */


/**
 *
 * @param {string} cmd
 * @param [paramList]
 * @param {boolean} doPost
 * @param {boolean} useBigInt  // support BigInt in JSON.  default to false
 * @return {Promise} a promise with the results
 */


/**
 *
 * @param {String} channelId
 * @param {Action} action
 */


/**
 * add the given name-value param into a new paramList.  If a param by a given name exists, it will be replaced.
 * if name is not given, the original paramList is returned.
 * @param {Object | Object[]} paramList
 * @param {string} name
 * @param {string} value
 */


/**
 *
 * @param {Array.<WebPlotRequest>} inReqAry
 * @param {string} imageViewerId
 * @param {string} tbl_id
 * @param {Array.<Object>} plotRows
 * @param {String} [highlightPlotId]
 * @return {undefined|function(): void}
 */


/**
 *
 * @param {WebPlotRequest} request
 * @param {string} imageViewerId
 * @param {string} tbl_id
 * @param {string} highlightedRow
 * @return {undefined|function(): void}
 */


/**
 * pass a request or array of request and return an extraction function
 * @param {WebPlotRequest|Array.<WebPlotRequest>} request
 * @param {ObsCoreData} [sourceObsCoreData]
 * @param {DatalinkData} [dlData]
 * @return {Function}
 */


/**
 *
 * @param {string} activePlotId the new active plot id after the replot
 * @param {boolean} makeActive if true, make the plotId active
 * @param {string} imageViewerId the id of the viewer
 * @param {string} tbl_id table id of the table with the data products
 * @param {Array.<WebPlotRequest>} reqAry an array of request to execute
 * @param {Array.<WebPlotRequest>} [threeReqAry] an array of request for a three color plot, optional, max 3 entries, r,g,b
 * @param {function} [onActivePvChanged] called when the active pv changes
 */


/**
 *
 * @param table table data
 * @param {Array.<string>} colToUse columns from table
 * @param {Array.<string>} headerParams meta data parameters
 * @param {RangeValues} rv rangeValues
 * @param {number} colorTableId color table id
 * @return {function} see below, function takes plotId, reqKey,title, rowNum, extranParams and returns a WebPlotRequest
 *
 */


/**
 *
 * @param {Object} p
 * @param {FileAnalysisPart} p.part
 * @param {WebPlotRequest} p.request
 * @param {TableModel} p.table
 * @param {number} p.row
 * @param {String} p.fileFormat (see Format object)
 * @param {DatalinkData} p.dlData
 * @param {String} p.source
 * @param {String} p.originalTitle
 * @param {ActivateParams} p.activateParams
 * @param {DataProductsFactoryOptions} p.options
 * @param {String} p.title
 * @return {{tableResult: DataProductsDisplayType|Array.<DataProductsDisplayType>|undefined, imageResult: DataProductsDisplayType|undefined}}
 */


/**
 * Determine what type of displays are available for this part
 * @param part
 * @param fileFormat
 * @return {Array.<String>} return an array of DPTypes string const
 */


/**
 *
 * @param {TableModel} table
 * @param {Number} row
 * @param {FileAnalysisPart} part
 * @param {String} fileFormat
 * @param {String} source - server key to access the file
 * @param {String} title
 * @param {ActivateParams} activateParams
 * @param {DataProductsFactoryOptions} options
 * @param {String} originalTitle
 * @param {DatalinkData} dlData
 * @return {DataProductsDisplayType|undefined}
 */


/**
 * Activate a chart and table
 * @param {Object} p
 * @param {boolean} [p.chartAndTable] - true - both char and table, false - table only
 * @param {String} p.source
 * @param {{titleString:String,showChartTitle:boolean}|String} p.titleInfo an object that has a titleStr and showchartTile properties
 * @param {ActivateParams} p.activateParams
 * @param {ChartInfo} [p.chartInfo]
 * @param {Number} p.tbl_index
 * @param {ImageAsTableInfo} [p.imageAsTableInfo]
 * @param {String|undefined} [p.chartId]
 * @param {String} [p.tbl_id]
 * @param {String} [p.statefulTabComponentKey]
 * @return {function} the activate function
 */


/**
 * helper function to make a list of table row that should be used to plot in grid mode
 * @param table
 * @param maxRows
 * @param plotIdRoot
 * @return {Array<{plotId:String,row:number,highlight:boolean}>}
 */


/**
 * passing an object with tbl_id as key and activate function as value. Make a new activate function
 * @param {Object} activateObj
 * @param {ActivateParams} activateParams
 * @return {function(): function(): void}
 */


/**
 * passing an object with tbl_id as key and extraction function as value. Make a new extraction function
 * @param {Object} extractionObj
 * @param {ActivateParams} activateParams
 * @return {function(): function(): void}
 */


/**
 * Support data that we don't know about
 * @param table
 * @param row
 * @param includeSingle
 * @param includeStandard
 * @return {{}}
 */


/**
 * get a DataProductsConvertType for a table. The converter is determined by the type of table. Each converter has
 * a table matches funtion. If a match occurs then the create function is called with that table and the results
 * are returned.
 * @param {TableModel} table
 * @param {string} factoryKey - which factory to use
 * @return {DataProductsConvertType}
 */


/**
 * create a function to analyze and show a data product
 * @param makeReq
 * @return {function}
 */


/**
 *
 * Returns a function that returns a Promise<DataProductsDisplayType>.
 * callback: function(table:TableModel, plotRows:Array.<Object>,activeParams:ActivateParams)
 * @param makeReq
 * @return {function | promise}
 */


/**
 * return an activate function that will upload and analyze the file then dispatch the new DataProductsDisplayType
 * as the active data product. This will be picked up by the MultiProductViewer
 * @param {Object} obj
 * @param obj.table
 * @param obj.row
 * @param obj.request
 * @param obj.activateParams
 * @param obj.menuKey
 * @param {ServiceDescriptorDef} [obj.serDef]
 * @param {DatalinkData} [obj.dlData]
 * @param [obj.originalTitle]
 * @param {DataProductsFactoryOptions} [obj.options]
 * @return {function}
 */


/**
 * dispatch the new DataProductsDisplayType
 * @param {DataProductsDisplayType} dpType
 * @param {Array.<DataProductsDisplayType>} menu
 * @param {string} menuKey
 * @param {string} activeMenuLookupKey
 * @param {string} dpId
 * @param {ServiceDescriptorDef} serDef
 * @param {function} analysisActivateFunc
 */


/**
 * This is the core function to upload and analyze the data
 * @param obj
 * @param {TableModel} obj.table table of data products
 * @param {number} obj.row active row number
 * @param {WebPlotRequest} obj.request - used for image or just downloading files
 * @param {ActivateParams} obj.activateParams
 * @param {DataProductsFactoryOptions} obj.options
 * @param {Array.<Object>} [obj.menu]
 * @param {ServiceDescriptorDef} [obj.serDef]
 * @param {DatalinkData} [obj.dlData]
 * @param {Object} [obj.userInputParams]
 * @param {Function} [obj.analysisActivateFunc]
 * @param {string} [obj.originalTitle]
 * @param {string} [obj.menuKey]
 * @param obj.serviceDescMenuList
 * @return {Promise.<DataProductsDisplayType>}
 */


/**
 *
 * @param obj
 * @param {TableModel} obj.table table of data products
 * @param {number} obj.row active row number
 * @param {WebPlotRequest} obj.request - used for image or just downloading files
 * @param {ActivateParams} obj.activateParams
 * @param {String} obj.serverCacheFileKey - key to use for server calls to access the file
 * @param {FileAnalysisReport} obj.fileAnalysis results of the file analysis server call
 * @param {Function} obj.analysisActivateFunc
 * @param {ServiceDescriptorDef} obj.serDef
 * @param {DatalinkData} [obj.dlData]
 * @param {String} obj.originalTitle
 * @param {DataProductsFactoryOptions} obj.options
 * @param {String} obj.menuKey
 * @return {DataProductsDisplayType}
 */


/**
 *
 * @param {String} message
 * @param {Promise} promise
 * @param {WebPlotRequest} [request]
 * @return {DataProductsDisplayType}
 */


/**
 *
 * @param {object} p
 * @param {String} p.name
 * @param {String} p.dropDownText
 * @param {function} p.activate
 * @param {String} p.url
 * @param {ServiceDescriptorDef} p.serDef
 * @param {String} p.menuKey
 * @param {String} p.semantics
 * @param {number} [p.size]
 * @param {String} p.activeMenuLookupKey
 * @param {WebPlotRequest} p.request
 * @param {String} [p.sRegion]
 * @param {String} [p.serviceDefRef]
 * @param {boolean} [p.allowsInput]
 * @param {String} [p.standardID]
 * @param {String} [p.ID]
 * @param {DatalinkData} [p.dlData]
 * @param {DatalinkData} [p.cutoutToFullWarning]
 * @return {DataProductsDisplayType}
 */


/**
 * Action watcher callback:
 * loads images on table change (only acts on image metadata tables),
 * pauses when the viewer is unmounted, resumes when it is mounted again
 * @callback actionWatcherCallback
 * @param tbl_id
 * @param action
 * @param cancelSelf
 * @param params
 * @param params.options.imageViewerId
 * @param params.paused
 */


/**
 *
 * @param {String} factoryKey
 * @param {Action} action
 * @param {boolean} firstTime
 * @param {string} tbl_id
 * @param {ActivateParams} activateParams
 * @param {function} abortLastPromise
 * @return {function} a function to abort any unfinished promises
 */


/**
 *
 * @param {ActivateParams} activateParams
 * @param tbl_id
 */


/**
 * @param {Action} rawAction
 * @returns {Function}
 */


/**
 *
 * @typedef {Object} Viewer
 * @prop {string} dpId
 * @prop {DataProductsDisplayType} dataProducts
 * @prop {Object} serviceDescriptorsActivateStatus
 * @prop {Object} activeFileMenuKeys - key serialized request, value - activeFileMenuKey, only used with containerType:WRAPPER
 * @prop {Object} activeMenuKeys - key serialized request, value - activeFileMenuKey, last active menu key, only used with containerType:WRAPPER
 * @prop {Array.<{activeMenuLookupKey:string,menuKey:string,params:Object}>} serviceParamsAry
 *
 * @global
 * @public
 */


/**
 *
 * @param {Object} params
 * @param {TableModel} params.sourceTable
 * @param {number} params.row
 * @param {TableModel} params.datalinkTable
 * @param {ActivateParams} params.activateParams
 * @param {Array.<DataProductsDisplayType>} [params.additionalServiceDescMenuList]
 * @param {string} params.dlTableUrl datalink url - url of the datalink Table
 * @param {boolean} [params.doFileAnalysis]
 * @param {String} [params.parsingAlgorithm] - which type of DL data
 * @param {DataProductsFactoryOptions} [params.options] - which type of DL data
 * @param {string} [params.baseTitle]
 * @param {boolean} [params.useForTableGrid] - this result is part of a table grid Result
 * @return {DataProductsDisplayType}
 */


/**
 *
 * @param {Object} p
 * @param {String} p.dlTableUrl
 * @param {DatalinkData} p.dlData
 * @param {number} p.idx
 * @param {TableModel} p.sourceTable
 * @param p.sourceRow
 * @param {DataProductsFactoryOptions} p.options
 * @param p.doFileAnalysis
 * @param p.dropDownText
 * @param p.name
 * @param {ActivateParams} p.activateParams
 * @return {DataProductsDisplayType|{displayType: string, menuKey: string, name: *, url: *, fileType: *}}
 */


/**
 *
 * @param {Object} p
 * @param {String} p.dlTableUrl
 * @param {DatalinkData} p.dlData
 * @param {number} p.idx
 * @param {string} p.baseTitle
 * @param {TableModel} p.sourceTable
 * @param {number} p.sourceRow
 * @param {DataProductsFactoryOptions} p.options
 * @param {string} p.name
 * @param {string} [p.dropDownText]
 * @param {boolean} p.doFileAnalysis
 * @param p.activateParams
 * @return {Object}
 */


/**
 *
 * @param parsingAlgorithm
 * @param {Array.<DatalinkData>} dataLinkData
 * @return {Array.<DatalinkData>}
 */


/**
 *
 * @param obj
 * @param obj.dlTableUrl
 * @param {Array.<DatalinkData>} obj.dataLinkData
 * @param {TableModel} obj.sourceTable
 * @param {number} obj.sourceRow
 * @param {ActivateParams} obj.activateParams
 * @param {Array.<DataProductsDisplayType>} [obj.additionalServiceDescMenuList]
 * @param obj.doFileAnalysis
 * @param obj.preferCutout
 * @param {DataProductsFactoryOptions} obj.options
 * @param obj.parsingAlgorithm
 * @param obj.baseTitle
 * @return {Array.<DataProductsDisplayType>}
 */


/**
 *
 * @param {TableModel} table
 * @param {DataProductsConvertType} converterTemplate
 * @param {DataProductsFactoryOptions} options
 * @return {DataProductsConvertType}
 */


/**
 *
 * @param {Object} p
 * @param {Array.<ServiceDescriptorDef>} p.descriptors
 * @param {WorldPt|undefined} p.positionWP
 * @param {TableModel} p.table
 * @param {number} p.row
 * @param {ActivateParams} p.activateParams
 * @param {String} p.activeMenuLookupKey
 * @param {DataProductsFactoryOptions} p.options
 * @return {Array.<DataProductsDisplayType>}
 */


/**
 *
 * @param {TableModel} table
 * @param {DataProductsConvertType} converterTemplate
 * @return {DataProductsConvertType}
 */


/**
 * make a list of plot request for wise. This function works with ConverterFactory.
 * @param table
 * @param row
 * @param includeSingle
 * @param includeStandard
 * @param threeColorOps
 * @return {{}}
 */


/**
 * make a list of plot request for atlas. This function works with ConverterFactory.
 * @param table
 * @param row
 * @param includeSingle
 * @param includeStandard
 * @param threeColorOps
 * @return {{}}
 */


/**
 * make a list of plot request for 2mass. This function works with ConverterFactory.
 * @param table
 * @param row
 * @param includeSingle
 * @param includeStandard
 * @param threeColorOps
 * @return {{}}
 */


/**
 * make a list of plot request for wise. This function works with ConverterFactory.
 * @param table
 * @param row
 * @param includeSingle
 * @param includeStandard
 * @return {{}}
 */


/**
 * Call the function when drag is beginning.  It returns a function that is call on each drag event.
 * call the returned function with each next screen location it returns the new scroll point.
 * @param originalDeviceX
 * @param originalDeviceY
 * @param {ScreenPt} originalScrollPt
 * @param mouseDownScreenPt
 * @param {PlotView} startingPlotView
 * @return {function({number}, {number})}
 */


/**
 * should be call if the viewport changes, scroll changes, rotation changes, viewDim (offsetWidth, offsetHeight) changes
 * @param {PlotView} pv
 * @return {PlotView}
 */


/**
 *
 * @param {string} viewerId
 * @param {string} canReceiveNewPlots   a string representation of one of NewPlotMode.
 * @param {string} containerType a string with container type, IMAGE and PLOT2D are predefined
 * @param {boolean} mounted
 * @param {string} [renderTreeId] - used only with multiple rendered tree, like slate in jupyter lab
 * @param {string} [layout] - layout type - SINGLE or GRID, defaults to GRID
 * @param {boolean} [reservedContainer]
 * @param {boolean} [internallyManaged]
 */


/**
 *
 * @param {MultiViewerRoot} multiViewRoot
 * @param {VisRoot} visRoot
 * @param {string} plotId
 * @return {boolean}
 */


/**
 *
 * @param {MultiViewerRoot} state
 * @param {string} viewerId
 * @param {string[]} itemIdAry
 * @param {string} containerType
 * @param {string} [renderTreeId] - used only with multiple rendered tree, like slate in jupyter lab, only used here
 *                                 if the viewerId does not exist and it needs to make one.
 * @return {MultiViewerRoot}
 */


/**
 *
 * @param {MultiViewerRoot} state
 * @param {string} viewerId
 * @param {string[]} itemIdAry
 * @param {string} containerType
 * @return {MultiViewerRoot}
 */


/**
 * Delete an item with only knowing the itemId and containerType but not the viewerId
 * @param {MultiViewerRoot} state
 * @param {string} itemId
 * @param {string} containerType
 * @return {MultiViewerRoot}
 */


/**
 *
 * @param {string} plotId
 * @param {Enum} mouseState
 * @param {Object} screenPt
 * @param {number} screenX
 * @param {number} screenY
 * @param {Object} kState
 * @param kState.shiftDown
 * @param kState.controlDown
 * @param kState.metaDown
 * @return {{plotId: string, mouseState: Enum, screenPt: object, imagePt: object, worldPt: object, screenX: number, screenY: number}}
 */


/**
     * @param wp
     * @param {string} survey  must be one of 'asky', 'askyw', 'sx', 'sxw', 'cal'
     * @param {string} band  must be one of 'j','h','k'
     * @param sizeInDeg less then .138 degrees (500 arcsec)
     * @return {WebPlotRequest}
     */


/**
     *
     * @param wp
     * @param survey can be Atlas, 3a, 1b
     * @param band 1,2,3,4
     * @param sizeInDeg  .01 to 3
     * @return {WebPlotRequest}
     */


/**
     *
     * @param wp
     * @param {string} survey  'ref'
     * @param {string} band  must be one of 'zg', 'zr'
     * @param sizeInDeg
     * @return {WebPlotRequest}
     */


/**
     *
     * @param wp
     * @param {string} survey 'level2'
     * @param {string} band  must be one of '1' '2'
     * @param sizeInDeg
     * @return {WebPlotRequest}
     */


/**
     *
     * @param wp
     * @param survey any atlas combination tables: schema.table, i.e. 'spitzer.seip_science'
     * @param band any atlas 'band_name' column, i.e 'IRAC2' (which is 2.4 microns channel from IRAC instrument)
     * @param filter extra filter for particular table, such as 'file_type = 'science' and fname like '%.mosaic.fits' or 'file_type = 'science' and principal=1'
     * @param sizeInDeg
     * @return {WebPlotRequest}
     */


/**
    * This method can only be used for those services that take the standard
    * ra, dec, radius approach (so far, iras, issa, 2mass, dss)
    *
    * @param {ServiceType} serviceType the network service type
    * @param {WorldPt} wp the center point WorldPt
    * @param {string} survey
    * @param {number} sizeInDeg size in degrees
    * @param {string} [title] the title of the plot
    * @param {string} [subGroup] - the subgroup Id
    *
    * @return {WebPlotRequest} the PlotRequest object that was constructed
    */


/**
     * @param {Object} [overrideParams] params to replace in the copy
     * @return {WebPlotRequest}
     */


/**
 * Create a new object with the keys more consistent with the keys defined in WebPlotRequest.
 * if a case insensitive version of the key exist then replace it with the proper one, otherwise
 * use the key. This will also remove the attributes object.
 * The loop looks up the key with case insensitive matching, if it does not exist it uses the original key
 * @param {Object} r - plain object
 * @return {Object}
 */


/**
 * take a plain object plot request and clean it up has a minimum required
 * to be valid
 * @param {Object|Array} request
 * @param global
 * @param fallbackGroupId
 * @param {Function} makePlotId make a plot id
 * @return {*}
 */


/**
 * convert the tables types to 'boolean', 'int', 'long', 'float', 'double', 'string'
 * @param fdt
 * @return {string}
 */


/**
 * return true if the two plot HDU has the same number and matching dimensions
 * @param refPlot
 * @param plot
 * @return {boolean}
 */


/**
 * @param {WebPlotRequest|String} plotRequest - the plot request or serialized version
 * @param {RangeValues|String} rangeValues - the range values or serialized version
 * @return {BandState}
 */


/**
 * make a BandState from a pure json representation
 * @param {object} bsJson parsed json object
 * @param {WebPlotRequest|String} overridePlotRequest -  a request or the serialized string version
 * @param {RangeValues|String} overrideRV -  a RangeValues or the serialized string version
 * @return {BandState}
 */


/**
 * @param {BandState|undefined|null} bs
 * @param {boolean} includeDirectAccessData include the directFileAccessData object
 */


/**
 * check if the table from upload analysis is valid with fits header
 * @param report
 * @returns {Object} valid and mocInfo including nipxColName and MOCOrder
 */


/**
 * check to see if the loaded table is a MOC
 * @param {TableModel} table
 * @returns {boolean} true if table is a MOC
 */


/**
 * check if the moc fits valid with fits header
 * @param report
 * @returns {Object} valid and mocInfo including nipxColName and MOCOrder
 */


/**
 * add new layer on MOC table
 * @param {Object} params moc table id
 * @param {string} params.tbl_id moc table id
 * @param {string} [params.title] optional title
 * @param {string} params.fitsPath moc fits path at the server after upload
 * @param {string} params.mocUrl  moc fits url
 * @param {string} params.uniqColName column name for uniq number
 * @param {boolean} params.tablePreloaded - true if the MOC table has already been loaded
 * @param {string} [params.color] - color string
 * @param {string} [params.maxFetchDepth] - maximum healpix order to fetch
 * @param {string} [params.mocGroupDefColorId ] - group color id
 * @returns {T|SelectInfo|*|{}}
 */


/**
 * tile at each side as the tile of order 0 is sub-divided into 4 tiles recursively up to order 'maxOrder'
 * @param {number} maxOrder  the max order to generate the side cells
 * @return {array} side cells from left bottom corner of order 0 to maxOrder, the return is like
 *                  [ [0]   // cell 0 at order 0
 *                    [0, 1, 3, 2]   // side cells at order 1, from the side cells of order 0
 *                    [0, .........] // side cells at order 2, from the side cells of order 1
 *                   ...]
 */


/**
 * get corners from healpix, some fixing on order 0, need more investigation later.
 * @param wpCorners  in original order
 * @param npix
 * @param healpixCache
 * @param coordsys
 * @returns {*}      in original order
 */


/**
 * get default sources with source and label.  Put in an array if more than one.
 * i.e. {source: 'abc', label: 'ABC featured'}
 * @returns {Array}
 */


/**
 * resolve a ivo hips id to a URL if a url is passed just return it.
 * @param {string} ivoOrUrl - a url or a IVO id
 * @return {Promise} a promise the resolves to a url
 */


/**
 * Build the HiPS url
 * @param {String} url
 * @param {boolean} proxy - make a URL the uses proxying
 * @param {boolean} alwaysUseCached - true if this url references a hips tile
 * @return {String} the modified url
 */


/**
 * This function make an object (with functions) to cache allsky type computations.
 * @return {*}
 */


/**
 * Get the x,y of the tile for the healpix pixel. This assumes 512x512 tiles.
 * @param pixel
 * @return {{x: number, y: number}}
 */


/**
 *
 * @param {WorldPt} wp
 * @return {SpatialVector}
 */


/**
 * check if tile1 (norder1 & npix1) is within tile2 (norder2 & npix2)
 * @param norder1
 * @param npix1
 * @param norder2
 * @param npix2
 * @returns {boolean}
 */


/**
 * replace the hips projection if the coordinate system changes
 * @param {WebPlot|undefined} plot
 * @param coordinateSys
 * @param {WorldPt} wp
 * @return {WebPlot}
 */


/**
 * @summary create drawing layer
 * @param {string} drawLayerTypeId - id of drawing layer
 * @param {Object} params
 * @public
 * @memberof firefly.action
 * @function  dispatchCreateDrawLayer
 */


/**
 * @summary change the visibility of one more drawing layers on a set of WebPlots.
 * This function can match the drawing layers using drawLayerId, drawLayerTypeId and further
 * match using the title of the draw layer. it can match hhe plots by plotId, then all the plots in the group, and then
 * can limit the plots in the group using subgroupId
 * @param {Object} p
 * @param {string|string[]} p.id - make the drawLayerId or drawLayerTypeId, this may be an array
 * @param {boolean} p.visible
 * @param {string} p.plotId - the plotId to change the visibility on, if used group is defined then visibility will be
 * change for all the plotIds in the PlotGroup
 * @param {boolean} [p.useGroup] - If true, get all the plotViews in the group of the plotId, if false use only the one
 * @param {string} [p.subGroupId] - if defined the list of PlotViews affected will be filtered by the subGroupId. In other words
 * it will only change the visibility on PlotView that have a matching subGroupId.
 * @param {boolean} [p.matchTitle] -  matches any draw layers that have the same title as the one specified by the id
 *  @public
 *  @memberof firefly.action
 *  @function dispatchChangeVisibility
 */


/**
 * @summary change the drawing definition of the drawing layer
 * @param {string|string[]} id make the drawLayerId or drawLayerTypeId, this may be an array
 * @param drawingDef
 * @param plotId
 * @param {boolean} [matchTitle] -  matches any draw layers that have the same title as the one specified by the id
 *  @public
 *  @memberof firefly.action
 *  @function dispatchChangeDrawingDef
 */


/**
 * @summary create custom changes to the drawing layer
 * @param {string|string[]} id make the drawLayerId or drawLayerTypeId, this may be an array
 * @param {Object} changes any object of changes
 * @param {string} [plotId] a plotId
 * @public
 * @memberof firefly.action
 * @function dispatchModifyCustomField
 */


/**
 * @summary force to update the drawing layer
 * @param id
 * @param plotId
 * @public
 * @memberof firefly.action
 * @function dispatchForceDrawLayerUpdate
 */


/**
 * @summary destroy the drawing layer
 * @param {string} id make the drawLayerId or drawLayerTypeId
 * @public
 * @memberof firefly.action
 * @function dispatchDestroyDrawLayer
 */


/**
 * @summary attach drawing layer to plot
 * @param {string|string[]} id make the drawLayerId or drawLayerTypeId, this may be an array
 * @param {string|string[]} plotId to attach this may by a string or an array of strings
 * @param {boolean} attachAllPlot
 * @param {boolean|string} visible - Can have three values: true: layer is attach visible, false: attach not-visible,
 * value (string) 'inherit' layer is visible
 * @param {boolean} plotTypeMustMatch
 * @memberof firefly.action
 * @public
 * @function  dispatchAttachLayerToPlot
 */


/**
 * @summary Detach drawing layer from the plot
 * @param {string|string[]} id make the drawLayerId or drawLayerTypeId, this may be an array
 * @param {string|string[]} plotId to attach this may by a string or an array of string
 * @param detachAllPlot
 * @param destroyWhenAllDetached if all plots are detached then destroy this plot
 * @public
 * @memberof firefly.action
 * @function dispatchDetachLayerFromPlot
 */


/**
 * check and create selectMode with valid property and value.
 * @param selectMode
 * @returns {{selectStyle, selectColor, lineWidth}}
 */


/**
 * @summary Create drawing layer based on region file or region description
 * @param {string} drawLayerId - id of the drawing layer to be created, required
 * @param {string} layerTitle - if it is empty, it will be created internally
 * @param {string} fileOnServer - region file name on server
 * @param {string[]|string} regionAry - array or string of region description
 * @param {string[]|string} plotId - array or string of plot id. If plotId is empty, all plots of the active group are applied
 * @param {RegionSelectMode} selectMode - rendering features for the selected region
 * @param {Function} dispatcher
 * @public
 * @function dispatchCreateRegionLayer
 * @memberof firefly.action
 */


/**
 * @summary Add regions to drawing layer
 * @param {string} drawLayerId - id of the drawing layer where the region(s) are added to
 * if the layer doesn't exist, a new drawing layer is created by either using the specified drawLayerId or
 * creating a new id based on the setting of 'layerTitle' in case drawLayerId is undefined
 * @param {string[]|string} regionChanges - array or string of region description
 * @param {string[]|string} plotId - array or string of plot id. If plotId is empty, all plots of the active group are applied
 * @param {string} layerTitle - will replace the original title if the drawing layer exists and layerTitle is non-empty
 * @param {RegionSelectMode} selectMode - rendering features for the selected region
 * @param {Function} dispatcher
 * @public
 * @function dispatchAddRegionEntry
 * @memberof firefly.action
 */


/**
 * @summary create drawing layer with footprint
 * @param {string} footprintId - id of the drawing layer
 * @param {string} layerTitle - title of the drawing layer
 * @param {footprintInfo} footprintData footprint information for footprint layer,
 *                        relocateBy: 'origin' means relocating footprint origin to the target location
 *                                    'center' means relocating footprint center to the target location
 * @param {string[]|string} plotId - array or string of plot id. If plotId is empty, all plots of the active group are applied
 * @param {bool} attachPlotGroup - attach all plots of the same plot group
 * @param dispatcher
 * @public
 * @function dispatchCreateFootprintLayer
 * @memberof firefly.action
 */


/**
 *
 * @param factory
 * @ignore
 */


/**
 * Create a drawing layer
 * @param state
 * @param {{type:string,payload:object}} action
 * @returns {Object} the new state;
 * @ignore
 */


/**
 * Destroy the drawing layer
 * @param state
 * @param {{type:string,payload:object}} action
 * @returns {Object} the new state;
 * @ignore
 */


/**
 * Call the reducer for the drawing layer defined by the action
 * @param state
 * @param {{type:string,payload:object}} action
 * @param dlReducer drawinglayer subreducer{string|string[]}
 * @returns {Object} the new state;
 * @ignore
 */


/**
 * Call all the drawing layers that are interested in the action.  Since this function will be called often it does
 *  a lot of checking for change.
 *  If nothing has changed it returns the original state.
 * @param state
 * @param {{type:string,payload:object}} action
 * @param dlReducer drawinglayer subreducer
 * @param force
 * @returns {Object} the new state;
 * @ignore
 */


/**
 * return promise to a loaded PlotView
 * @param plotId
 * @param failureAsReject - if true the call reject otherwise just resolve with an undefined
 * @return {Promise<PlotView>}
 */


/**
 * Find any related data that is associated with the PlotView that is supported has not be activated
 * (activated means turned into an overlay).
 * @param {PlotView} pv
 * @return {RelatedData[]} all the unactivated related data the the UI supports
 */


/**
 * For the related data that is passed check to see if is supported and has not been completely activated.
 * Mask type related data might be partially activated.
 * @param {RelatedData} r
 * @param idx
 * @param {PlotView} pv
 * @return {boolean}
 */


/**
 * apply a function to all OverlayPlotView that match the passed, match is determined by same group and title
 * @param {VisRoot}  vr
 * @param {OverlayPlotView} opv
 * @param {Function} func  an OverlayPlotView is passed as parameter
 */


/**
 * Do processing to turn this related data into a drawing layer
 * @param {VisRoot}  vr
 * @param {PlotView|undefined} pv
 * @param {RelatedData} relatedData
 */


/**
 *
 * search matchOverlayPlotViews array and find any related data in the passed PlotView. Enable the layers
 * that match.
 * This function has side effect of dispatching actions
 * @param {PlotView|undefined} pv the plot view the contains the related data
 * @param {OverlayPlotView[]} matchOverlayPlotViews array of OverlayPlotView that must be matched
 */


/**
 * check if the full path (server path format) is one of the workspace files
 * @param fName
 * @returns {*}
 */


/**
 * convert files from the server to a list of file/folder for FilePicker
 * currently React-Keyed-file-browser is used and the key (file path) starts without '/'
 * @param wFiles
 * @returns {*}
 */


/**
 * If infinite depth is not supported by the server,
 * folders are populated as they are clicked to be open.
 * This function sets childrenRetrieved attribute of the object
 * representing folder node in the state.
 * @param folderRelPath
 * @param state
 * @returns {*}
 */


/**
     *
     * @param {Operation} op
     */


/**
     * Create a plot state object from the json version of the object.
     * @param psJson
     * @param [overridePlotRequest] - if defined and not three color use this one instead of the on the the json object
     * @param [overrideRV] - if defined and not three color use this one instead of the on the the json object
     * @return {PlotState}
     */


/**
     * @summary convert his PlotState to something can be used with JSON.stringify
     * @param {PlotState} s
     * @param {boolean} includeDirectAccessData include the includeDirectAccessData object
     */


/**
     *
     * @param {WorldPt} wp world point
     * @param {ImagePt} imp Image Point
     */


/**
     * Determine if a world point is in data Area of the plot and is not null
     * @param {WorldPt} iwPt the point to test.
     * @returns {boolean} true if it is in the data boundaries, false if not.
     */


/**
     * This method returns false if the point is definitely not in plot.  It returns true if the point might be in the plot.
     * Used for tossing out points that we know that are not in plot without having to do all the math.  It is much faster.
     * @param {WorldPt} wp
     * @return {boolean} true in we guess it might be in the bounds, false if we know that it is not in the bounds
     */


/**
     * Determine if a image point is in the plot boundaries and is not null.
     * @param {ImagePt} ipt the point to test.
     * @returns {boolean} true if it is in the boundaries, false if not.
     */


/**
     * Determine if a image point is in the plot boundaries and is not null
     * @param {Point} pt the point to test.
     * @return {boolean} true if it is in the boundaries, false if not.
     */


/* check if the point within display range for either regular image or hips */


/**
     * @description  Return a ImageWorkspacePt from the screen point.
     * @param {ScreenPt} screenPt
     * @param {number} [altZoomLevel]
     */


/**
     *
     * @param {Object} sp ScreenPt
     * @param {Object} [altTransform]
     * @return {DevicePt}
     */


/**
     *
     * @param {ImageWpt} iwpt ImageWorkspacePt
     * @param {number} [altZoomLevel]
     */


/**
     * @param wp1
     * @param wp2
     * @param [lenientFactor] how aggressive to check
     * @return {boolean}
     */


/**
 * Return true if east if left of north.  If east is right of north return false. This works regardless of the rotation
 * of the image.
 * @param {WebPlot|undefined} plot
 * @return {boolean} true if is is left of north.
 */


/**
 * @param {WebPlot|undefined} plot
 * @return {{x: number, y: number, w: number, h: number}|undefined}
 */


/**
 * return true if the image is completely covering the area passed. The width and height are in Device coordinate
 * system.
 * @param {WebPlot} plot
 * @param {Point} pt
 * @param {number} width in device coordinates
 * @param {number} height in device coordinates
 * @return {boolean} true if covering
 */


/**
 * @global
 * @public
 * @typedef {Object} ClientTile
 *
 * @prop {Object} tileAttributes
 * @prop {String} dataUrl
 * @prop {number} width - width of this tile
 * @prop {number} height - height of this tile
 * @prop {number} index - index of this tile
 * @prop {string} url - original file key to use in the service to retrieve this tile
 * @prop {number} x - pixel offset of this tile
 * @prop {number} y - pixel offset of this tile
 */


/**
 * Show image with new color table loaded
 * Note - function parameter is a single object
 *
 *
 * @param {Object}  obj
 * @param {string} obj.plotId
 * @param {number} obj.cbarId must be in the range, 0 - 21, each number represents different color bar
 * @param {number} obj.bias bias between 0 - 1, .5 is no bias
 * @param {number} obj.contrast bias between 0 - 2, .1 is no contrast
 * @param {boolean} obj.useRed use this band, only use with 3 color
 * @param {boolean} obj.useGreen use this band, only use with 3 color
 * @param {boolean} obj.useBlue use this band, only use with 3 color
 *
 * @param {string|ActionScope} [obj.actionScope] default to group
 * @param {Function} [obj.dispatcher] only for special dispatching uses such as remote
 *
 *
 * @public
 * @function dispatchColorChange
 * @memberof firefly.action
 */


/**
 * @summary recenter the images on the plot center or the ACTIVE_TARGET
 *
 * Note - function parameter is a single object
 * @param {Object}  p
 * @param {string} p.plotId
 * @param {Point} [p.centerPt] Point to center on
 * @param {boolean} [p.centerOnImage] only used if centerPt is not defined.  If true then the centering will be
 *                                  the center of the image.  If false, then the center point will be the
 *                                  FIXED_TARGET attribute, if defined. Otherwise it will be the center of the image.
 * @param {boolean} [p.updateFixedTarget] if true and centerPt is a worldPt, it will update the
 *                                  FIXED_TARGET attribute on the plot
 * @param {boolean} [p.updateWcsPrimId] if true update the make plotId the prime for wcs matching, defaults to true
 * @param {Function} [p.dispatcher] only for special dispatching uses such as remote
 *
 * @public
 * @function dispatchRecenter
 * @memberof firefly.action
 */


/**
 * @summary Plot an image.
 * @description Note - function parameter is a single object
 * @param {Object}  p
 * @param {string} [p.plotId] is required unless defined in the WebPlotRequest
 * @param {WebPlotParams|WebPlotRequest|Array} p.wpRequest -  plotting parameters, required or for 3 color pass an array of WebPlotParams or WebPlotRequest
 * @param {boolean} [p.threeColor] is a three color request, if true the wpRequest should be an array
 * @param {boolean} [p.useContextModifications=true] it true the request will be modified to use preferences, rotation, etc
 *                                 should only be false when it is doing a 'restore to defaults' type plot
 * @param {Object} [p.attributes] meta data that is added the plot
 * @param {HipsImageConversionSettings} [p.hipsImageConversion= undefined] if defined, use these parameter to
 *                                                convert between image and HiPS
 * @param {Object} [p.pvOptions] parameter specific to the  plotView, only read the first time per plot id
 * @param {boolean} [p.setNewPlotAsActive= true] the new plot will be active
 * @param {boolean} [p.holdWcsMatch= false] if wcs match is on, then modify the request to hold the wcs match
 * @param {boolean} [p.enableRestore= true] if true the original request is saved for restore
 * @param {string} [p.viewerId] - viewer that this plot should be put into, only optional if
 *                                you have added the plot id manually to a viewer.
 *                                otherwise, you need to specify the viewer.
 * @param {string} [p.renderTreeId] - used only with multiple rendered tree, like slate in jupyter lab
 * @param {Function} [p.dispatcher] only for special dispatching uses such as remote
 * @public
 * @function dispatchPlotImage
 * @memberof firefly.action
 */


/**
 * @summary Plot a group of images.
 * Note - function parameter is a single object
 * @param {Object}  p this function takes a single parameter
 * @param {WebPlotRequest[]} p.wpRequestAry
 * @param {string} p.viewerId
 * @param {PVCreateOptions} p.pvOptions PlotView init Options
 * @param {Object} [p.attributes] meta data that is added the plot
 * @param {boolean} [p.setNewPlotAsActive] the last completed plot will be active
 * @param {boolean} [p.holdWcsMatch= true] if wcs match is on, then modify the request to hold the wcs match
 * @param {boolean} [p.enableRestore= true] if true the original request is saved for restore
 * @param {string} [p.renderTreeId] - used only with multiple rendered tree, like slate in jupyter lab
 * @param {Function} [p.dispatcher] only for special dispatching uses such as remote
 */


/**
 * @summary Plot a HiPS display
 * @description Note - function parameter is a single object
 * @param {Object}  p this function takes a single parameter
 * @param {string} p.plotId
 * @param {WebPlotParams|WebPlotRequest} p.wpRequest
 * @param {HipsImageConversionSettings} [p.hipsImageConversion= undefined] if defined, use these parameter to
 * @param {string} [p.viewerId]
 * @param {PVCreateOptions} [p.pvOptions] PlotView init Options
 * @param {Object} [p.attributes] meta data that is added the plot
 * @param {boolean} [p.setNewPlotAsActive] the last completed plot will be active
 * @param {boolean} [p.enableRestore= true] if true the original request is saved for restore
 * @param {string} [p.renderTreeId] - used only with multiple rendered tree, like slate in jupyter lab
 * @param {Function} [p.dispatcher] only for special dispatching uses such as remote
 *
 * @public
 * @function dispatchPlotHiPS
 * @memberof firefly.action
 */


/**
 * @summary Plot a HiPS or a image depending on the FOV size
 * @description Note - function parameter is a single object
 * @param {Object}  p this function takes a single parameter
 * @param {string} p.plotId
 * @param {WebPlotParams|WebPlotRequest} p.hipsRequest
 * @param {WebPlotParams|WebPlotRequest} p.imageRequest - must be a ServiceType request.
 * @param {WebPlotParams|WebPlotRequest} p.allSkyRequest - must be an allsky type request,  this is deprecated, parameter ignored
 * @param {boolean} [p.plotAllSkyFirst= false] - if there is an all sky set up then plot that first
 * @param {number} [p.fovDegFallOver] - the size in degrees that the image will switch between hips and a image cutout
 * @param {number} [p.fovMaxFitsSize] - the max size the fits image service can support
 * @param {boolean} [p.autoConvertOnZoom] - convert between images and FITS on zoom
 * @param {string} [p.viewerId]
 * @param {string} [p.renderTreeId] - used only with multiple rendered tree, like slate in jupyter lab
 * @param {PVCreateOptions} [p.pvOptions] PlotView init Options
 * @param {Object} [p.attributes] meta data that is added the plot
 * @param {boolean} [p.setNewPlotAsActive] the last completed plot will be active
 * @param {Function} [p.dispatcher] only for special dispatching uses such as remote
 *
 * @public
 * @function dispatchPlotImageOrHiPS
 * @memberof firefly.action
 */


/**
 * @summary Add a mask
 * @param {Object}  p this function takes a single parameter
 * @param {string} p.plotId
 * @param {number} p.maskValue power of 2, e.g 4, 8, 32, 128, etc
 * @param {number} p.maskNumber 2, e.g 4, 8, 32, 128, etc
 * @param {string} p.imageOverlayId
 * @param {number} p.imageNumber hdu number of fits
 * @param {string} p.fileKey file on the server
 * @param {string} p.color - color is optional, if not specified, one is chosen
 * @param {string} p.title
 * @param {string} p.lazyLoad
 * @param {string} p.uiCanAugmentTitle
 * @param {string} [p.relatedDataId] pass a related data id if one exist
 * @param {Function} [p.dispatcher] only for special dispatching uses such as remote
 *
 * @public
 * @function dispatchPlotMask
 * @memberof firefly.action
 */


/**
 * Set the plotId of the active plot view
 * @param {string} plotId
 * @param {string} reason
 */


/**
 *
 * @param {Object}  p this function takes a single parameter
 * @param p.plotId
 * @param p.overlayColorScope
 * @param p.positionScope
 * @param p.attKey
 * @param p.attValue
 * @param {Object} p.changes attribute changes, use if more than one
 * @param p.toAllPlotsInPlotView if a multiImageFits apply to all the images
 */


/**
 *
 * @param {ExpandType|boolean} expandedMode the mode to change to, it true the expand and match the last one,
 *          if false colapse
 *
 */


/**
 *
 * @param {VisRoot} state
 * @param {Action} action
 * @returns {VisRoot}
 */


/**
 * Convert from one coordinate system to another. No action, if the world point coordinate system is non-celestial.
 *
 * @param {WorldPt|undefined} wpt the world point to convert
 * @param {CoordinateSys} to CoordSys, the coordinate system to convert to; defaults to J2000, ignored for non-celestial world points
 * @return {WorldPt|undefined} the world point in the new coordinate system
 */


/**
 * Find an approximate central point and search radius for a group of positions
 *
 * @param inPoints array of points for which the central point is desired
 * @return {{centralPoint:WorldPt, maxRadius: Number}}
 */


/**
 * call computeCentralPointAndRadius in 2 ways first with a flatten version of the 2d array
 * then again with group of points.  This allows us not to overweight a larger group when computing the center.
 *
 * @param {Array.<Array.<WorldPt>>} inPoints2dAry  a 2d array of world points. Each array represents a group of points
 * @param {number} minSizeDeg
 * @return {{centralPoint:WorldPt, avgOfCenters:WorldPt, fovSize:number}}
 */


/**
 * Return a WorldPt that is offset by the relative ra and dec from the passed in position
 * @param {WorldPt} pos1
 * @param {number} offsetRa in arcseconds
 * @param {number} offsetDec in arcseconds
 * @return {WorldPt}
 */


/**
 * convert angle value of one unit to that of another unit
 * @param {string} from 'degree' or 'deg', 'arcmin', 'arcsec', 'radian' case insensitive
 * @param {string} to 'degree' or 'deg', 'arcmin', 'arcsec', 'radian' case insensitive
 * @param {*} angle  number or string
 * @returns {number}
 */


/**
 * Find the point at intersection of two line segments.
 * If the lines do intersect then return an object with the intersection point x,y and
 * two booleans to represent it the intersection point is on each line segment.
 * Return false if the lines do not intersect.
 * @param {number} seg1x1 - line segment 1 first point x
 * @param {number} seg1y1 - line segment 1 first point y
 * @param {number} seg1x2 - line segment 1 second point x
 * @param {number} seg1y2 - line segment 1 second point y
 * @param {number} seg2x1 - line segment 2 first point x
 * @param {number} sec2y2 - line segment 2 first point y
 * @param {number} seg2x2 - line segment 2 second point x
 * @param {number} seg2y2 - line segment 2 second point y
 * @return {{x: number, y:number, onSeg1:boolean, onSeg2:boolean} | boolean}
 */


/*
 * direction from a to b to c, pixels on device coordinate domain
 * 0: collinear
 * 1: clockwise
 * 2: counterclockwise
 */


// check if there is intersection between two segments


// check if a segment intersects a view area (assume a non-slanted rectangular area)


/**
 * Determine if a point is in a rectangle
 * assume a non-slanted rectangular area
 * @param point1
 * @param view_corners
 * @return {boolean}
 */


/**
 * distance between point and line defined by two end points
 * @param pts
 * @param cc
 * @param pt
 * @returns {number}
 */


/**
 * distance between point to polygon boundary
 * @param pts
 * @param cc
 * @param pt
 * @returns {*}
 */


/**
 * distance between point to circle boundary
 * @param radius  in screen pixel
 * @param pts  center in any domain
 * @param cc
 * @param pt
 * @returns {Number}
 */


/**
 * change pref
 * @param {string} cacheKey
 * @param {PlotState} state
 * @param {Number} colorTableId
 */


/**
     *
     * @param {Object} obj
     * @param {string} obj.plotId
     * @param {Dimension} [obj.viewDim]
     * @param {WebPlotInitializer} obj.wpInit init data returned from server
     * @param {object} [obj.attributes] any attributes to initialize
     * @param {boolean} [obj.asOverlay]
     * @param {CubeCtx} [obj.cubeCtx]
     * @param {WebPlotRequest} [obj.request0] - only used when this is part of a cube
     * @param {RangeValues} [obj.rv0] - only used when this is part of a cube
     * @return {WebPlot} the plot
     */


/**
     *
     * @param plotId
     * @param {String} hipsUrlRoot - the url of the hips repository
     * @param wpRequest
     * @param {HipsProperties} hipsProperties
     * @param {PlotAttribute|object} attributes
     * @param {boolean} proxyHips - if true use the proxy (firefly server) to get the hips tailes
     * @return {WebPlot} the new WebPlot object for HiPS
     */


/**
     *
     * @param {WebPlot} plot
     * @param {object} stateJson
     * @param {number} zoomFactor
     * @param {ImageTileData} [rawData]
     * @param {Number} [colorTableId]
     * @param {Number} [bias]
     * @param {Number} [contrast]
     * @param {boolean|undefined} useRed
     * @param {boolean|undefined} useGreen
     * @param {boolean|undefined} useBlue
     * @return {WebPlot}
     */


/**
 * @param {HipsProperties} hipsProperties
 * @return {CoordinateSys}
 */


/**
 * replace the hips projection if the coordinate system changes
 * @param {WebPlot} plot
 * @param {HipsProperties} hipsProperties
 * @param {WorldPt} wp
 */


/**
 * @param {WebPlot|CysConverter} plot
 * @param {WorldPt} [wp] new center of projection, if undefined then it will keep the same center point
 * @param {boolean} [fullSky] true if this is a fullSky projection (AITOFF), if undefined then it will stay the same
 * @return {WebPlot}
 */


/**
 * @param {WebPlotInitializer} wpInit
 * @param {PlotState} [plotState] - only necessary for three color
 * @return {{processHeader:Object, wlData: Object, wlDataAry: Array.<Object>, wlTableRelatedAry:Array}}
 */


/**
 *
 * @param {Array.<String>} wpParts
 * @return {*}
 */


/**
 * return a number give a number or a string
 * @param {number|string|undefined|null} v
 * @return {number} converted to a number
 */


/**
 * given an x,y, and a CoordinateSys object or string, make the correct type of point.
 * @param {number} x
 * @param {number} y
 * @param {CoordinateSys|String} coordSys
 * @return {Point}
 */


/**
 * given serialized point object return the appropriate point.
 * @param inStr
 * @return {Point|WorldPt|ScreenPt|ImagePt|ZeroBasedImagePt|undefined}
 */


/**
 * @summary Test if two points are equals.  They must be the same coordinate system and have the same values to be
 * equal. Two points that are null or undefined are also considered equal.
 * If both points are WorldPt and are equal in values and coordinate system but have a
 * different resolver and object names, * they are still considered equal.
 *
 * @param {Point} p1 - the first point
 * @param {Point} p2 - the second point
 *
 * @return  true if equals
 *
 * @function pointEquals
 * @memberof firefly.util.image
 * @public
 * @global
 */


/**
     *
     * @param p the params objects
     * @param p.which
     * @param p.lowerWhich
     * @param p.lowerValue
     * @param p.upperWhich
     * @param p.upperValue
     * @param p.asinhQValue
     * @param p.gammaValue
     * @param p.algorithm
     * @param p.zscaleContrast
     * @param p.zscaleSamples
     * @param p.zscaleSamplesPerLine
     * @param p.rgbPreserveHue
     * @param p.asinhStretch - stretch parameter for hue-preserving rgb
     * @param p.scalingK - flux scaling coefficient for hue-preserving rgb
     * @param p.bias
     * @param p.contrast
     * @return {RangeValues}
     */