{"version":3,"sources":["jsdelivr-header.js","/npm/qz-tray@2.2.4/qz-tray.js"],"names":[],"mappings":"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","file":"/npm/qz-tray@2.2.4/qz-tray.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/qz-tray@2.2.4/qz-tray.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","'use strict';\n\n/**\n * @version 2.2.4\n * @overview QZ Tray Connector\n * @license LGPL-2.1-only\n * <p/>\n * Connects a web client to the QZ Tray software.\n * Enables printing and device communication from javascript.\n */\nvar qz = (function() {\n\n///// POLYFILLS /////\n\n    if (!Array.isArray) {\n        Array.isArray = function(arg) {\n            return Object.prototype.toString.call(arg) === '[object Array]';\n        };\n    }\n\n    if (!Number.isInteger) {\n        Number.isInteger = function(value) {\n            return typeof value === 'number' && isFinite(value) && Math.floor(value) === value;\n        };\n    }\n\n///// PRIVATE METHODS /////\n\n    var _qz = {\n        VERSION: \"2.2.4\",                              //must match @version above\n        DEBUG: false,\n\n        log: {\n            /** Debugging messages */\n            trace: function() { if (_qz.DEBUG) { console.log.apply(console, arguments); } },\n            /** General messages */\n            info: function() { console.info.apply(console, arguments); },\n            /** General warnings */\n            warn: function() { console.warn.apply(console, arguments); },\n            /** Debugging errors */\n            allay: function() { if (_qz.DEBUG) { console.warn.apply(console, arguments); } },\n            /** General errors */\n            error: function() { console.error.apply(console, arguments); }\n        },\n\n\n        //stream types\n        streams: {\n            serial: 'SERIAL', usb: 'USB', hid: 'HID', printer: 'PRINTER', file: 'FILE', socket: 'SOCKET'\n        },\n\n\n        websocket: {\n            /** The actual websocket object managing the connection. */\n            connection: null,\n            /** Track if a connection attempt is being cancelled. */\n            shutdown: false,\n\n            /** Default parameters used on new connections. Override values using options parameter on {@link qz.websocket.connect}. */\n            connectConfig: {\n                host: [\"localhost\", \"localhost.qz.io\"], //hosts QZ Tray can be running on\n                hostIndex: 0,                           //internal var - index on host array\n                usingSecure: true,                      //boolean use of secure protocol\n                protocol: {\n                    secure: \"wss://\",                   //secure websocket\n                    insecure: \"ws://\"                   //insecure websocket\n                },\n                port: {\n                    secure: [8181, 8282, 8383, 8484],   //list of secure ports QZ Tray could be listening on\n                    insecure: [8182, 8283, 8384, 8485], //list of insecure ports QZ Tray could be listening on\n                    portIndex: 0                        //internal var - index on active port array\n                },\n                keepAlive: 60,                          //time between pings to keep connection alive, in seconds\n                retries: 0,                             //number of times to reconnect before failing\n                delay: 0                                //seconds before firing a connection\n            },\n\n            setup: {\n                /** Loop through possible ports to open connection, sets web socket calls that will settle the promise. */\n                findConnection: function(config, resolve, reject) {\n                    if (_qz.websocket.shutdown) {\n                        reject(new Error(\"Connection attempt cancelled by user\"));\n                        return;\n                    }\n\n                    //force flag if missing ports\n                    if (!config.port.secure.length) {\n                        if (!config.port.insecure.length) {\n                            reject(new Error(\"No ports have been specified to connect over\"));\n                            return;\n                        } else if (config.usingSecure) {\n                            _qz.log.error(\"No secure ports specified - forcing insecure connection\");\n                            config.usingSecure = false;\n                        }\n                    } else if (!config.port.insecure.length && !config.usingSecure) {\n                        _qz.log.trace(\"No insecure ports specified - forcing secure connection\");\n                        config.usingSecure = true;\n                    }\n\n                    var deeper = function() {\n                        if (_qz.websocket.shutdown) {\n                            //connection attempt was cancelled, bail out\n                            reject(new Error(\"Connection attempt cancelled by user\"));\n                            return;\n                        }\n\n                        config.port.portIndex++;\n\n                        if ((config.usingSecure && config.port.portIndex >= config.port.secure.length)\n                            || (!config.usingSecure && config.port.portIndex >= config.port.insecure.length)) {\n                            if (config.hostIndex >= config.host.length - 1) {\n                                //give up, all hope is lost\n                                reject(new Error(\"Unable to establish connection with QZ\"));\n                                return;\n                            } else {\n                                config.hostIndex++;\n                                config.port.portIndex = 0;\n                            }\n                        }\n\n                        // recursive call until connection established or all ports are exhausted\n                        _qz.websocket.setup.findConnection(config, resolve, reject);\n                    };\n\n                    var address;\n                    if (config.usingSecure) {\n                        address = config.protocol.secure + config.host[config.hostIndex] + \":\" + config.port.secure[config.port.portIndex];\n                    } else {\n                        address = config.protocol.insecure + config.host[config.hostIndex] + \":\" + config.port.insecure[config.port.portIndex];\n                    }\n\n                    try {\n                        _qz.log.trace(\"Attempting connection\", address);\n                        _qz.websocket.connection = new _qz.tools.ws(address);\n                    }\n                    catch(err) {\n                        _qz.log.error(err);\n                        deeper();\n                        return;\n                    }\n\n                    if (_qz.websocket.connection != null) {\n                        _qz.websocket.connection.established = false;\n\n                        //called on successful connection to qz, begins setup of websocket calls and resolves connect promise after certificate is sent\n                        _qz.websocket.connection.onopen = function(evt) {\n                            if (!_qz.websocket.connection.established) {\n                                _qz.log.trace(evt);\n                                _qz.log.info(\"Established connection with QZ Tray on \" + address);\n\n                                _qz.websocket.setup.openConnection({ resolve: resolve, reject: reject });\n\n                                if (config.keepAlive > 0) {\n                                    var interval = setInterval(function() {\n                                        if (!_qz.tools.isActive() || _qz.websocket.connection.interval !== interval) {\n                                            clearInterval(interval);\n                                            return;\n                                        }\n\n                                        _qz.websocket.connection.send(\"ping\");\n                                    }, config.keepAlive * 1000);\n\n                                    _qz.websocket.connection.interval = interval;\n                                }\n                            }\n                        };\n\n                        //called during websocket close during setup\n                        _qz.websocket.connection.onclose = function() {\n                            // Safari compatibility fix to raise error event\n                            if (_qz.websocket.connection && typeof navigator !== 'undefined' && navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {\n                                _qz.websocket.connection.onerror();\n                            }\n                        };\n\n                        //called for errors during setup (such as invalid ports), reject connect promise only if all ports have been tried\n                        _qz.websocket.connection.onerror = function(evt) {\n                            _qz.log.trace(evt);\n\n                            _qz.websocket.connection = null;\n\n                            deeper();\n                        };\n                    } else {\n                        reject(new Error(\"Unable to create a websocket connection\"));\n                    }\n                },\n\n                /** Finish setting calls on successful connection, sets web socket calls that won't settle the promise. */\n                openConnection: function(openPromise) {\n                    _qz.websocket.connection.established = true;\n\n                    //called when an open connection is closed\n                    _qz.websocket.connection.onclose = function(evt) {\n                        _qz.log.trace(evt);\n\n                        _qz.websocket.connection = null;\n                        _qz.websocket.callClose(evt);\n                        _qz.log.info(\"Closed connection with QZ Tray\");\n\n                        for(var uid in _qz.websocket.pendingCalls) {\n                            if (_qz.websocket.pendingCalls.hasOwnProperty(uid)) {\n                                _qz.websocket.pendingCalls[uid].reject(new Error(\"Connection closed before response received\"));\n                            }\n                        }\n\n                        //if this is set, then an explicit close call was made\n                        if (this.promise != undefined) {\n                            this.promise.resolve();\n                        }\n                    };\n\n                    //called for any errors with an open connection\n                    _qz.websocket.connection.onerror = function(evt) {\n                        _qz.websocket.callError(evt);\n                    };\n\n                    //send JSON objects to qz\n                    _qz.websocket.connection.sendData = function(obj) {\n                        _qz.log.trace(\"Preparing object for websocket\", obj);\n\n                        if (obj.timestamp == undefined) {\n                            obj.timestamp = Date.now();\n                            if (typeof obj.timestamp !== 'number') {\n                                obj.timestamp = new Date().getTime();\n                            }\n                        }\n                        if (obj.promise != undefined) {\n                            obj.uid = _qz.websocket.setup.newUID();\n                            _qz.websocket.pendingCalls[obj.uid] = obj.promise;\n                        }\n\n                        // track requesting monitor\n                        obj.position = {\n                            x: typeof screen !== 'undefined' ? ((screen.availWidth || screen.width) / 2) + (screen.left || screen.availLeft || 0) : 0,\n                            y: typeof screen !== 'undefined' ? ((screen.availHeight || screen.height) / 2) + (screen.top || screen.availTop || 0) : 0\n                        };\n\n                        try {\n                            if (obj.call != undefined && obj.signature == undefined && _qz.security.needsSigned(obj.call)) {\n                                var signObj = {\n                                    call: obj.call,\n                                    params: obj.params,\n                                    timestamp: obj.timestamp\n                                };\n\n                                //make a hashing promise if not already one\n                                var hashing = _qz.tools.hash(_qz.tools.stringify(signObj));\n                                if (!hashing.then) {\n                                    hashing = _qz.tools.promise(function(resolve) {\n                                        resolve(hashing);\n                                    });\n                                }\n\n                                hashing.then(function(hashed) {\n                                    return _qz.security.callSign(hashed);\n                                }).then(function(signature) {\n                                    _qz.log.trace(\"Signature for call\", signature);\n                                    obj.signature = signature || \"\";\n                                    obj.signAlgorithm = _qz.security.signAlgorithm;\n\n                                    _qz.signContent = undefined;\n                                    _qz.websocket.connection.send(_qz.tools.stringify(obj));\n                                }).catch(function(err) {\n                                    _qz.log.error(\"Signing failed\", err);\n\n                                    if (obj.promise != undefined) {\n                                        obj.promise.reject(new Error(\"Failed to sign request\"));\n                                        delete _qz.websocket.pendingCalls[obj.uid];\n                                    }\n                                });\n                            } else {\n                                _qz.log.trace(\"Signature for call\", obj.signature);\n\n                                //called for pre-signed content and (unsigned) setup calls\n                                _qz.websocket.connection.send(_qz.tools.stringify(obj));\n                            }\n                        }\n                        catch(err) {\n                            _qz.log.error(err);\n\n                            if (obj.promise != undefined) {\n                                obj.promise.reject(err);\n                                delete _qz.websocket.pendingCalls[obj.uid];\n                            }\n                        }\n                    };\n\n                    //receive message from qz\n                    _qz.websocket.connection.onmessage = function(evt) {\n                        var returned = JSON.parse(evt.data);\n\n                        if (returned.uid == null) {\n                            if (returned.type == null) {\n                                //incorrect response format, likely connected to incompatible qz version\n                                _qz.websocket.connection.close(4003, \"Connected to incompatible QZ Tray version\");\n\n                            } else {\n                                //streams (callbacks only, no promises)\n                                switch(returned.type) {\n                                    case _qz.streams.serial:\n                                        if (!returned.event) {\n                                            returned.event = JSON.stringify({ portName: returned.key, output: returned.data });\n                                        }\n\n                                        _qz.serial.callSerial(JSON.parse(returned.event));\n                                        break;\n                                    case _qz.streams.socket:\n                                        _qz.socket.callSocket(JSON.parse(returned.event));\n                                        break;\n                                    case _qz.streams.usb:\n                                        if (!returned.event) {\n                                            returned.event = JSON.stringify({ vendorId: returned.key[0], productId: returned.key[1], output: returned.data });\n                                        }\n\n                                        _qz.usb.callUsb(JSON.parse(returned.event));\n                                        break;\n                                    case _qz.streams.hid:\n                                        _qz.hid.callHid(JSON.parse(returned.event));\n                                        break;\n                                    case _qz.streams.printer:\n                                        _qz.printers.callPrinter(JSON.parse(returned.event));\n                                        break;\n                                    case _qz.streams.file:\n                                        _qz.file.callFile(JSON.parse(returned.event));\n                                        break;\n                                    default:\n                                        _qz.log.allay(\"Cannot determine stream type for callback\", returned);\n                                        break;\n                                }\n                            }\n\n                            return;\n                        }\n\n                        _qz.log.trace(\"Received response from websocket\", returned);\n\n                        var promise = _qz.websocket.pendingCalls[returned.uid];\n                        if (promise == undefined) {\n                            _qz.log.allay('No promise found for returned response');\n                        } else {\n                            if (returned.error != undefined) {\n                                promise.reject(new Error(returned.error));\n                            } else {\n                                promise.resolve(returned.result);\n                            }\n                        }\n\n                        delete _qz.websocket.pendingCalls[returned.uid];\n                    };\n\n\n                    //send up the certificate before making any calls\n                    //also gives the user a chance to deny the connection\n                    function sendCert(cert) {\n                        if (cert === undefined) { cert = null; }\n\n                        //websocket setup, query what version is connected\n                        qz.api.getVersion().then(function(version) {\n                            _qz.websocket.connection.version = version;\n                            _qz.websocket.connection.semver = version.toLowerCase().replace(/-rc\\./g, \"-rc\").split(/[\\\\+\\\\.-]/g);\n                            for(var i = 0; i < _qz.websocket.connection.semver.length; i++) {\n                                try {\n                                    if (i == 3 && _qz.websocket.connection.semver[i].toLowerCase().indexOf(\"rc\") == 0) {\n                                        // Handle \"rc1\" pre-release by negating build info\n                                        _qz.websocket.connection.semver[i] = -(_qz.websocket.connection.semver[i].replace(/\\D/g, \"\"));\n                                        continue;\n                                    }\n                                    _qz.websocket.connection.semver[i] = parseInt(_qz.websocket.connection.semver[i]);\n                                }\n                                catch(ignore) {}\n\n                                if (_qz.websocket.connection.semver.length < 4) {\n                                    _qz.websocket.connection.semver[3] = 0;\n                                }\n                            }\n\n                            //algorithm can be declared before a connection, check for incompatibilities now that we have one\n                            _qz.compatible.algorithm(true);\n                        }).then(function() {\n                            _qz.websocket.connection.sendData({ certificate: cert, promise: openPromise });\n                        });\n                    }\n\n                    _qz.security.callCert().then(sendCert).catch(function(error) {\n                        _qz.log.warn(\"Failed to get certificate:\", error);\n\n                        if (_qz.security.rejectOnCertFailure) {\n                            openPromise.reject(error);\n                        } else {\n                            sendCert(null);\n                        }\n                    });\n                },\n\n                /** Generate unique ID used to map a response to a call. */\n                newUID: function() {\n                    var len = 6;\n                    return (new Array(len + 1).join(\"0\") + (Math.random() * Math.pow(36, len) << 0).toString(36)).slice(-len)\n                }\n            },\n\n            dataPromise: function(callName, params, signature, signingTimestamp) {\n                return _qz.tools.promise(function(resolve, reject) {\n                    var msg = {\n                        call: callName,\n                        promise: { resolve: resolve, reject: reject },\n                        params: params,\n                        signature: signature,\n                        timestamp: signingTimestamp\n                    };\n\n                    _qz.websocket.connection.sendData(msg);\n                });\n            },\n\n            /** Library of promises awaiting a response, uid -> promise */\n            pendingCalls: {},\n\n            /** List of functions to call on error from the websocket. */\n            errorCallbacks: [],\n            /** Calls all functions registered to listen for errors. */\n            callError: function(evt) {\n                if (Array.isArray(_qz.websocket.errorCallbacks)) {\n                    for(var i = 0; i < _qz.websocket.errorCallbacks.length; i++) {\n                        _qz.websocket.errorCallbacks[i](evt);\n                    }\n                } else {\n                    _qz.websocket.errorCallbacks(evt);\n                }\n            },\n\n            /** List of function to call on closing from the websocket. */\n            closedCallbacks: [],\n            /** Calls all functions registered to listen for closing. */\n            callClose: function(evt) {\n                if (Array.isArray(_qz.websocket.closedCallbacks)) {\n                    for(var i = 0; i < _qz.websocket.closedCallbacks.length; i++) {\n                        _qz.websocket.closedCallbacks[i](evt);\n                    }\n                } else {\n                    _qz.websocket.closedCallbacks(evt);\n                }\n            }\n        },\n\n\n        printing: {\n            /** Default options used for new printer configs. Can be overridden using {@link qz.configs.setDefaults}. */\n            defaultConfig: {\n                //value purposes are explained in the qz.configs.setDefaults docs\n\n                bounds: null,\n                colorType: 'color',\n                copies: 1,\n                density: 0,\n                duplex: false,\n                fallbackDensity: null,\n                interpolation: 'bicubic',\n                jobName: null,\n                legacy: false,\n                margins: 0,\n                orientation: null,\n                paperThickness: null,\n                printerTray: null,\n                rasterize: false,\n                rotation: 0,\n                scaleContent: true,\n                size: null,\n                units: 'in',\n\n                forceRaw: false,\n                encoding: null,\n                spool: null\n            }\n        },\n\n\n        serial: {\n            /** List of functions called when receiving data from serial connection. */\n            serialCallbacks: [],\n            /** Calls all functions registered to listen for serial events. */\n            callSerial: function(streamEvent) {\n                if (Array.isArray(_qz.serial.serialCallbacks)) {\n                    for(var i = 0; i < _qz.serial.serialCallbacks.length; i++) {\n                        _qz.serial.serialCallbacks[i](streamEvent);\n                    }\n                } else {\n                    _qz.serial.serialCallbacks(streamEvent);\n                }\n            }\n        },\n\n\n        socket: {\n            /** List of functions called when receiving data from network socket connection. */\n            socketCallbacks: [],\n            /** Calls all functions registered to listen for network socket events. */\n            callSocket: function(socketEvent) {\n                if (Array.isArray(_qz.socket.socketCallbacks)) {\n                    for(var i = 0; i < _qz.socket.socketCallbacks.length; i++) {\n                        _qz.socket.socketCallbacks[i](socketEvent);\n                    }\n                } else {\n                    _qz.socket.socketCallbacks(socketEvent);\n                }\n            }\n        },\n\n\n        usb: {\n            /** List of functions called when receiving data from usb connection. */\n            usbCallbacks: [],\n            /** Calls all functions registered to listen for usb events. */\n            callUsb: function(streamEvent) {\n                if (Array.isArray(_qz.usb.usbCallbacks)) {\n                    for(var i = 0; i < _qz.usb.usbCallbacks.length; i++) {\n                        _qz.usb.usbCallbacks[i](streamEvent);\n                    }\n                } else {\n                    _qz.usb.usbCallbacks(streamEvent);\n                }\n            }\n        },\n\n\n        hid: {\n            /** List of functions called when receiving data from hid connection. */\n            hidCallbacks: [],\n            /** Calls all functions registered to listen for hid events. */\n            callHid: function(streamEvent) {\n                if (Array.isArray(_qz.hid.hidCallbacks)) {\n                    for(var i = 0; i < _qz.hid.hidCallbacks.length; i++) {\n                        _qz.hid.hidCallbacks[i](streamEvent);\n                    }\n                } else {\n                    _qz.hid.hidCallbacks(streamEvent);\n                }\n            }\n        },\n\n\n        printers: {\n            /** List of functions called when receiving data from printer connection. */\n            printerCallbacks: [],\n            /** Calls all functions registered to listen for printer events. */\n            callPrinter: function(streamEvent) {\n                if (Array.isArray(_qz.printers.printerCallbacks)) {\n                    for(var i = 0; i < _qz.printers.printerCallbacks.length; i++) {\n                        _qz.printers.printerCallbacks[i](streamEvent);\n                    }\n                } else {\n                    _qz.printers.printerCallbacks(streamEvent);\n                }\n            }\n        },\n\n\n        file: {\n            /** List of functions called when receiving info regarding file changes. */\n            fileCallbacks: [],\n            /** Calls all functions registered to listen for file events. */\n            callFile: function(streamEvent) {\n                if (Array.isArray(_qz.file.fileCallbacks)) {\n                    for(var i = 0; i < _qz.file.fileCallbacks.length; i++) {\n                        _qz.file.fileCallbacks[i](streamEvent);\n                    }\n                } else {\n                    _qz.file.fileCallbacks(streamEvent);\n                }\n            }\n        },\n\n\n        security: {\n            /** Function used to resolve promise when acquiring site's public certificate. */\n            certHandler: function(resolve, reject) { reject(); },\n            /** Called to create new promise (using {@link _qz.security.certHandler}) for certificate retrieval. */\n            callCert: function() {\n                if (typeof _qz.security.certHandler.then === 'function') {\n                    //already a promise\n                    return _qz.security.certHandler;\n                } else if (_qz.security.certHandler.constructor.name === \"AsyncFunction\") {\n                    //already callable as a promise\n                    return _qz.security.certHandler();\n                } else {\n                    //turn into a promise\n                    return _qz.tools.promise(_qz.security.certHandler);\n                }\n            },\n\n            /** Function used to create promise resolver when requiring signed calls. */\n            signatureFactory: function() { return function(resolve) { resolve(); } },\n            /** Called to create new promise (using {@link _qz.security.signatureFactory}) for signed calls. */\n            callSign: function(toSign) {\n                if (_qz.security.signatureFactory.constructor.name === \"AsyncFunction\") {\n                    //use directly\n                    return _qz.security.signatureFactory(toSign);\n                } else {\n                    //use in a promise\n                    return _qz.tools.promise(_qz.security.signatureFactory(toSign));\n                }\n            },\n\n            /** Signing algorithm used on signatures */\n            signAlgorithm: \"SHA1\",\n\n            rejectOnCertFailure: false,\n\n            needsSigned: function(callName) {\n                const undialoged = [\n                    \"printers.getStatus\",\n                    \"printers.stopListening\",\n                    \"usb.isClaimed\",\n                    \"usb.closeStream\",\n                    \"usb.releaseDevice\",\n                    \"hid.stopListening\",\n                    \"hid.isClaimed\",\n                    \"hid.closeStream\",\n                    \"hid.releaseDevice\",\n                    \"file.stopListening\",\n                    \"getVersion\"\n                ];\n\n                return callName != null && undialoged.indexOf(callName) === -1;\n            }\n        },\n\n\n        tools: {\n            /** Create a new promise */\n            promise: function(resolver) {\n                //prefer global object for historical purposes\n                if (typeof RSVP !== 'undefined') {\n                    return new RSVP.Promise(resolver);\n                } else if (typeof Promise !== 'undefined') {\n                    return new Promise(resolver);\n                } else {\n                    _qz.log.error(\"Promise/A+ support is required.  See qz.api.setPromiseType(...)\");\n                }\n            },\n\n            /** Stub for rejecting with an Error from withing a Promise */\n            reject: function(error) {\n                return _qz.tools.promise(function(resolve, reject) {\n                    reject(error);\n                });\n            },\n\n            stringify: function(object) {\n                //old versions of prototype affect stringify\n                var pjson = Array.prototype.toJSON;\n                delete Array.prototype.toJSON;\n\n                function skipKeys(key, value) {\n                    if (key === \"promise\") {\n                        return undefined;\n                    }\n\n                    return value;\n                }\n\n                var result = JSON.stringify(object, skipKeys);\n\n                if (pjson) {\n                    Array.prototype.toJSON = pjson;\n                }\n\n                return result;\n            },\n\n            hash: function(data) {\n                //prefer global object for historical purposes\n                if (typeof Sha256 !== 'undefined') {\n                    return Sha256.hash(data);\n                } else {\n                    return _qz.SHA.hash(data);\n                }\n            },\n\n            ws: typeof WebSocket !== 'undefined' ? WebSocket : null,\n\n            absolute: function(loc) {\n                if (typeof window !== 'undefined' && typeof document.createElement === 'function') {\n                    var a = document.createElement(\"a\");\n                    a.href = loc;\n                    return a.href;\n                } else if (typeof exports === 'object') {\n                    //node.js\n                    require('path').resolve(loc);\n                }\n                return loc;\n            },\n\n            relative: function(data) {\n                for(var i = 0; i < data.length; i++) {\n                    if (data[i].constructor === Object) {\n                        var absolute = false;\n\n                        if (data[i].data && data[i].data.search && data[i].data.search(/data:image\\/\\w+;base64,/) === 0) {\n                            //upgrade from old base64 behavior\n                            data[i].flavor = \"base64\";\n                            data[i].data = data[i].data.replace(/^data:image\\/\\w+;base64,/, \"\");\n                        } else if (data[i].flavor) {\n                            //if flavor is known, we can directly check for absolute flavor types\n                            if ([\"FILE\", \"XML\"].indexOf(data[i].flavor.toUpperCase()) > -1) {\n                                absolute = true;\n                            }\n                        } else if (data[i].format && [\"HTML\", \"IMAGE\", \"PDF\", \"FILE\", \"XML\"].indexOf(data[i].format.toUpperCase()) > -1) {\n                            //if flavor is not known, all valid pixel formats default to file flavor\n                            //previous v2.0 data also used format as what is now flavor, so we check for those values here too\n                            absolute = true;\n                        } else if (data[i].type && (([\"PIXEL\", \"IMAGE\", \"PDF\"].indexOf(data[i].type.toUpperCase()) > -1 && !data[i].format)\n                            || ([\"HTML\", \"PDF\"].indexOf(data[i].type.toUpperCase()) > -1 && (!data[i].format || data[i].format.toUpperCase() === \"FILE\")))) {\n                            //if all we know is pixel type, then it is image's file flavor\n                            //previous v2.0 data also used type as what is now format, so we check for those value here too\n                            absolute = true;\n                        }\n\n                        if (absolute) {\n                            //change relative links to absolute\n                            data[i].data = _qz.tools.absolute(data[i].data);\n                        }\n                        if (data[i].options && typeof data[i].options.overlay === 'string') {\n                            data[i].options.overlay = _qz.tools.absolute(data[i].options.overlay);\n                        }\n                    }\n                }\n            },\n\n            /** Performs deep copy to target from remaining params */\n            extend: function(target) {\n                //special case when reassigning properties as objects in a deep copy\n                if (typeof target !== 'object') {\n                    target = {};\n                }\n\n                for(var i = 1; i < arguments.length; i++) {\n                    var source = arguments[i];\n                    if (!source) { continue; }\n\n                    for(var key in source) {\n                        if (source.hasOwnProperty(key)) {\n                            if (target === source[key]) { continue; }\n\n                            if (source[key] && source[key].constructor && source[key].constructor === Object) {\n                                var clone;\n                                if (Array.isArray(source[key])) {\n                                    clone = target[key] || [];\n                                } else {\n                                    clone = target[key] || {};\n                                }\n\n                                target[key] = _qz.tools.extend(clone, source[key]);\n                            } else if (source[key] !== undefined) {\n                                target[key] = source[key];\n                            }\n                        }\n                    }\n                }\n\n                return target;\n            },\n\n            versionCompare: function(major, minor, patch, build) {\n                if (_qz.tools.assertActive()) {\n                    var semver = _qz.websocket.connection.semver;\n                    if (semver[0] != major) {\n                        return semver[0] - major;\n                    }\n                    if (minor != undefined && semver[1] != minor) {\n                        return semver[1] - minor;\n                    }\n                    if (patch != undefined && semver[2] != patch) {\n                        return semver[2] - patch;\n                    }\n                    if (build != undefined && semver.length > 3 && semver[3] != build) {\n                        return Number.isInteger(semver[3]) && Number.isInteger(build) ? semver[3] - build : semver[3].toString().localeCompare(build.toString());\n                    }\n                    return 0;\n                }\n            },\n\n            isVersion: function(major, minor, patch, build) {\n                return _qz.tools.versionCompare(major, minor, patch, build) == 0;\n            },\n\n            isActive: function() {\n                return !_qz.websocket.shutdown && _qz.websocket.connection != null\n                    && (_qz.websocket.connection.readyState === _qz.tools.ws.OPEN\n                        || _qz.websocket.connection.readyState === _qz.tools.ws.CONNECTING);\n            },\n\n            assertActive: function() {\n                if (_qz.tools.isActive()) {\n                    return true;\n                }\n                // Promise won't reject on throw; yet better than 'undefined'\n                throw new Error(\"A connection to QZ has not been established yet\");\n            },\n\n            uint8ArrayToHex: function(uint8) {\n                return Array.from(uint8)\n                    .map(function(i) { return i.toString(16).padStart(2, '0'); })\n                    .join('');\n            },\n\n            uint8ArrayToBase64: function(uint8) {\n                /**\n                 * Adapted from Egor Nepomnyaschih's code under MIT Licence (C) 2020\n                 * see https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727\n                 */\n                var map = [\n                    \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\", \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\",\n                    \"V\", \"W\", \"X\", \"Y\", \"Z\", \"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\", \"i\", \"j\", \"k\", \"l\", \"m\", \"n\", \"o\", \"p\",\n                    \"q\", \"r\", \"s\", \"t\", \"u\", \"v\", \"w\", \"x\", \"y\", \"z\", \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"+\", \"/\"\n                ];\n\n                var result = '', i, l = uint8.length;\n                for (i = 2; i < l; i += 3) {\n                    result += map[uint8[i - 2] >> 2];\n                    result += map[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)];\n                    result += map[((uint8[i - 1] & 0x0F) << 2) | (uint8[i] >> 6)];\n                    result += map[uint8[i] & 0x3F];\n                }\n                if (i === l + 1) { // 1 octet yet to write\n                    result += map[uint8[i - 2] >> 2];\n                    result += map[(uint8[i - 2] & 0x03) << 4];\n                    result += \"==\";\n                }\n                if (i === l) { // 2 octets yet to write\n                    result += map[uint8[i - 2] >> 2];\n                    result += map[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)];\n                    result += map[(uint8[i - 1] & 0x0F) << 2];\n                    result += \"=\";\n                }\n                return result;\n            },\n        },\n\n        compatible: {\n            /** Converts message format to a previous version's */\n            data: function(printData) {\n                // special handling for Uint8Array\n                for(var i = 0; i < printData.length; i++) {\n                    if (printData[i].constructor === Object && printData[i].data instanceof Uint8Array) {\n                        if (printData[i].flavor) {\n                            var flavor = printData[i].flavor.toString().toUpperCase();\n                            switch(flavor) {\n                                case 'BASE64':\n                                    printData[i].data = _qz.tools.uint8ArrayToBase64(printData[i].data);\n                                    break;\n                                case 'HEX':\n                                    printData[i].data = _qz.tools.uint8ArrayToHex(printData[i].data);\n                                    break;\n                                default:\n                                    throw new Error(\"Uint8Array conversion to '\" + flavor + \"' is not supported.\");\n                            }\n                        }\n                    }\n                }\n\n                if(_qz.tools.versionCompare(2, 2, 4) < 0) {\n                    for(var i = 0; i < printData.length; i++) {\n                        if (printData[i].constructor === Object) {\n                            // dotDensity: \"double-legacy|single-legacy\" since 2.2.4.  Fallback to \"double|single\"\n                            if (printData[i].options && typeof printData[i].options.dotDensity === 'string') {\n                                printData[i].options.dotDensity = printData[i].options.dotDensity.toLowerCase().replace(\"-legacy\", \"\");\n                            }\n                        }\n                    }\n                }\n\n                if (_qz.tools.isVersion(2, 0)) {\n                    /*\n                    2.0.x conversion\n                    -----\n                    type=pixel -> use format as 2.0 type (unless 'command' format, which forces 2.0 'raw' type)\n                    type=raw -> 2.0 type has to be 'raw'\n                                if format is 'image' -> force 2.0 'image' format, ignore everything else (unsupported in 2.0)\n\n                     flavor translates straight to 2.0 format (unless forced to 'raw'/'image')\n                     */\n                    _qz.log.trace(\"Converting print data to v2.0 for \" + _qz.websocket.connection.version);\n                    for(var i = 0; i < printData.length; i++) {\n                        if (printData[i].constructor === Object) {\n                            if (printData[i].type && printData[i].type.toUpperCase() === \"RAW\" && printData[i].format && printData[i].format.toUpperCase() === \"IMAGE\") {\n                                if (printData[i].flavor && printData[i].flavor.toUpperCase() === \"BASE64\") {\n                                    //special case for raw base64 images\n                                    printData[i].data = \"data:image/compat;base64,\" + printData[i].data;\n                                }\n                                printData[i].flavor = \"IMAGE\"; //forces 'image' format when shifting for conversion\n                            }\n                            if ((printData[i].type && printData[i].type.toUpperCase() === \"RAW\") || (printData[i].format && printData[i].format.toUpperCase() === \"COMMAND\")) {\n                                printData[i].format = \"RAW\"; //forces 'raw' type when shifting for conversion\n                            }\n\n                            printData[i].type = printData[i].format;\n                            printData[i].format = printData[i].flavor;\n                            delete printData[i].flavor;\n                        }\n                    }\n                }\n            },\n\n            /* Converts config defaults to match previous version */\n            config: function(config, dirty) {\n                if (_qz.tools.isVersion(2, 0)) {\n                    if (!dirty.rasterize) {\n                        config.rasterize = true;\n                    }\n                }\n                if(_qz.tools.versionCompare(2, 2) < 0) {\n                    if(config.forceRaw !== 'undefined') {\n                        config.altPrinting = config.forceRaw;\n                        delete config.forceRaw;\n                    }\n                }\n                if(_qz.tools.versionCompare(2, 1, 2, 11) < 0) {\n                    if(config.spool) {\n                        if(config.spool.size) {\n                            config.perSpool = config.spool.size;\n                            delete config.spool.size;\n                        }\n                        if(config.spool.end) {\n                            config.endOfDoc = config.spool.end;\n                            delete config.spool.end;\n                        }\n                        delete config.spool;\n                    }\n                }\n                return config;\n            },\n\n            /** Compat wrapper with previous version **/\n            networking: function(hostname, port, signature, signingTimestamp, mappingCallback) {\n                // Use 2.0\n                if (_qz.tools.isVersion(2, 0)) {\n                    return _qz.tools.promise(function(resolve, reject) {\n                        _qz.websocket.dataPromise('websocket.getNetworkInfo', {\n                            hostname: hostname,\n                            port: port\n                        }, signature, signingTimestamp).then(function(data) {\n                            if (typeof mappingCallback !== 'undefined') {\n                                resolve(mappingCallback(data));\n                            } else {\n                                resolve(data);\n                            }\n                        }, reject);\n                    });\n                }\n                // Wrap 2.1\n                return _qz.tools.promise(function(resolve, reject) {\n                    _qz.websocket.dataPromise('networking.device', {\n                        hostname: hostname,\n                        port: port\n                    }, signature, signingTimestamp).then(function(data) {\n                        resolve({ ipAddress: data.ip, macAddress: data.mac });\n                    }, reject);\n                });\n            },\n\n            /** Check if QZ version supports chosen algorithm */\n            algorithm: function(quiet) {\n                //if not connected yet we will assume compatibility exists for the time being\n                if (_qz.tools.isActive()) {\n                    if (_qz.tools.isVersion(2, 0)) {\n                        if (!quiet) {\n                            _qz.log.warn(\"Connected to an older version of QZ, alternate signature algorithms are not supported\");\n                        }\n                        return false;\n                    }\n                }\n\n                return true;\n            }\n        },\n\n        /**\n         * Adapted from Chris Veness's code under MIT Licence (C) 2002\n         * see http://www.movable-type.co.uk/scripts/sha256.html\n         */\n        SHA: {\n            //@formatter:off - keep this block compact\n            hash: function(msg) {\n                // add trailing '1' bit (+ 0's padding) to string [§5.1.1]\n                msg = _qz.SHA._utf8Encode(msg) + String.fromCharCode(0x80);\n\n                // constants [§4.2.2]\n                var K = [\n                    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n                    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n                    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n                    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n                    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n                    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n                    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n                    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n                ];\n                // initial hash value [§5.3.1]\n                var H = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ];\n\n                // convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1]\n                var l = msg.length / 4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length\n                var N = Math.ceil(l / 16);  // number of 16-integer-blocks required to hold 'l' ints\n                var M = new Array(N);\n\n                for(var i = 0; i < N; i++) {\n                    M[i] = new Array(16);\n                    for(var j = 0; j < 16; j++) {  // encode 4 chars per integer, big-endian encoding\n                        M[i][j] = (msg.charCodeAt(i * 64 + j * 4) << 24) | (msg.charCodeAt(i * 64 + j * 4 + 1) << 16) |\n                            (msg.charCodeAt(i * 64 + j * 4 + 2) << 8) | (msg.charCodeAt(i * 64 + j * 4 + 3));\n                    } // note running off the end of msg is ok 'cos bitwise ops on NaN return 0\n                }\n                // add length (in bits) into final pair of 32-bit integers (big-endian) [§5.1.1]\n                // note: most significant word would be (len-1)*8 >>> 32, but since JS converts\n                // bitwise-op args to 32 bits, we need to simulate this by arithmetic operators\n                M[N-1][14] = ((msg.length - 1) * 8) / Math.pow(2, 32);\n                M[N-1][14] = Math.floor(M[N-1][14]);\n                M[N-1][15] = ((msg.length - 1) * 8) & 0xffffffff;\n\n                // HASH COMPUTATION [§6.1.2]\n                var W = new Array(64); var a, b, c, d, e, f, g, h;\n                for(var i = 0; i < N; i++) {\n                    // 1 - prepare message schedule 'W'\n                    for(var t = 0; t < 16; t++) { W[t] = M[i][t]; }\n                    for(var t = 16; t < 64; t++) { W[t] = (_qz.SHA._dev1(W[t-2]) + W[t-7] + _qz.SHA._dev0(W[t-15]) + W[t-16]) & 0xffffffff; }\n                    // 2 - initialise working variables a, b, c, d, e, f, g, h with previous hash value\n                    a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7];\n                    // 3 - main loop (note 'addition modulo 2^32')\n                    for(var t = 0; t < 64; t++) {\n                        var T1 = h + _qz.SHA._sig1(e) + _qz.SHA._ch(e, f, g) + K[t] + W[t];\n                        var T2 = _qz.SHA._sig0(a) + _qz.SHA._maj(a, b, c);\n                        h = g; g = f; f = e; e = (d + T1) & 0xffffffff;\n                        d = c; c = b; b = a; a = (T1 + T2) & 0xffffffff;\n                    }\n                    // 4 - compute the new intermediate hash value (note 'addition modulo 2^32')\n                    H[0] = (H[0]+a) & 0xffffffff; H[1] = (H[1]+b) & 0xffffffff; H[2] = (H[2]+c) & 0xffffffff; H[3] = (H[3]+d) & 0xffffffff;\n                    H[4] = (H[4]+e) & 0xffffffff; H[5] = (H[5]+f) & 0xffffffff; H[6] = (H[6]+g) & 0xffffffff; H[7] = (H[7]+h) & 0xffffffff;\n                }\n\n                return _qz.SHA._hexStr(H[0]) + _qz.SHA._hexStr(H[1]) + _qz.SHA._hexStr(H[2]) + _qz.SHA._hexStr(H[3]) +\n                    _qz.SHA._hexStr(H[4]) + _qz.SHA._hexStr(H[5]) + _qz.SHA._hexStr(H[6]) + _qz.SHA._hexStr(H[7]);\n            },\n\n            // Rotates right (circular right shift) value x by n positions\n            _rotr: function(n, x) { return (x >>> n) | (x << (32 - n)); },\n            // logical functions\n            _sig0: function(x) { return _qz.SHA._rotr(2, x) ^ _qz.SHA._rotr(13, x) ^ _qz.SHA._rotr(22, x); },\n            _sig1: function(x) { return _qz.SHA._rotr(6, x) ^ _qz.SHA._rotr(11, x) ^ _qz.SHA._rotr(25, x); },\n            _dev0: function(x) { return _qz.SHA._rotr(7, x) ^ _qz.SHA._rotr(18, x) ^ (x >>> 3); },\n            _dev1: function(x) { return _qz.SHA._rotr(17, x) ^ _qz.SHA._rotr(19, x) ^ (x >>> 10); },\n            _ch: function(x, y, z) { return (x & y) ^ (~x & z); },\n            _maj: function(x, y, z) { return (x & y) ^ (x & z) ^ (y & z); },\n            // note can't use toString(16) as it is implementation-dependant, and in IE returns signed numbers when used on full words\n            _hexStr: function(n) { var s = \"\", v; for(var i = 7; i >= 0; i--) { v = (n >>> (i * 4)) & 0xf; s += v.toString(16); } return s; },\n            // implementation of deprecated unescape() based on https://cwestblog.com/2011/05/23/escape-unescape-deprecated/ (and comments)\n            _unescape: function(str) {\n                return str.replace(/%(u[\\da-f]{4}|[\\da-f]{2})/gi, function(seq) {\n                    if (seq.length - 1) {\n                        return String.fromCharCode(parseInt(seq.substring(seq.length - 3 ? 2 : 1), 16))\n                    } else {\n                        var code = seq.charCodeAt(0);\n                        return code < 256 ? \"%\" + (0 + code.toString(16)).slice(-2).toUpperCase() : \"%u\" + (\"000\" + code.toString(16)).slice(-4).toUpperCase()\n                    }\n                });\n            },\n            _utf8Encode: function(str) {\n                return _qz.SHA._unescape(encodeURIComponent(str));\n            }\n            //@formatter:on\n        },\n    };\n\n\n///// CONFIG CLASS ////\n\n    /** Object to handle configured printer options. */\n    function Config(printer, opts) {\n\n        this.config = _qz.tools.extend({}, _qz.printing.defaultConfig); //create a copy of the default options\n        this._dirtyOpts = {}; //track which config options have changed from the defaults\n\n        /**\n         * Set the printer assigned to this config.\n         * @param {string|Object} newPrinter Name of printer. Use object type to specify printing to file or host.\n         *  @param {string} [newPrinter.name] Name of printer to send printing.\n         *  @param {string} [newPrinter.file] DEPRECATED: Name of file to send printing.\n         *  @param {string} [newPrinter.host] IP address or host name to send printing.\n         *  @param {string} [newPrinter.port] Port used by &lt;printer.host>.\n         */\n        this.setPrinter = function(newPrinter) {\n            if (typeof newPrinter === 'string') {\n                newPrinter = { name: newPrinter };\n            }\n            this.printer = newPrinter;\n        };\n\n        /**\n         *  @returns {Object} The printer currently assigned to this config.\n         */\n        this.getPrinter = function() {\n            return this.printer;\n        };\n\n        /**\n         * Alter any of the printer options currently applied to this config.\n         * @param newOpts {Object} The options to change. See <code>qz.configs.setDefaults</code> docs for available values.\n         *\n         * @see qz.configs.setDefaults\n         */\n        this.reconfigure = function(newOpts) {\n            for(var key in newOpts) {\n                if (newOpts[key] !== undefined) {\n                    this._dirtyOpts[key] = true;\n                }\n            }\n\n            _qz.tools.extend(this.config, newOpts);\n        };\n\n        /**\n         * @returns {Object} The currently applied options on this config.\n         */\n        this.getOptions = function() {\n            return _qz.compatible.config(this.config, this._dirtyOpts);\n        };\n\n        // init calls for new config object\n        this.setPrinter(printer);\n        this.reconfigure(opts);\n    }\n\n    /**\n     * Shortcut method for calling <code>qz.print</code> with a particular config.\n     * @param {Array<Object|string>} data Array of data being sent to the printer. See <code>qz.print</code> docs for available values.\n     * @param {boolean} [signature] Pre-signed signature of JSON string containing <code>call</code>, <code>params</code>, and <code>timestamp</code>.\n     * @param {number} [signingTimestamp] Required with <code>signature</code>. Timestamp used with pre-signed content.\n     *\n     * @example\n     * qz.print(myConfig, ...); // OR\n     * myConfig.print(...);\n     *\n     * @see qz.print\n     */\n    Config.prototype.print = function(data, signature, signingTimestamp) {\n        qz.print(this, data, signature, signingTimestamp);\n    };\n\n\n///// PUBLIC METHODS /////\n\n    /** @namespace qz */\n    var qz = {\n\n        /**\n         * Calls related specifically to the web socket connection.\n         * @namespace qz.websocket\n         */\n        websocket: {\n            /**\n             * Check connection status. Active connection is necessary for other calls to run.\n             *\n             * @returns {boolean} If there is an active connection with QZ Tray.\n             *\n             * @see connect\n             *\n             * @memberof  qz.websocket\n             */\n            isActive: function() {\n                return _qz.tools.isActive();\n            },\n\n            /**\n             * Call to setup connection with QZ Tray on user's system.\n             *\n             * @param {Object} [options] Configuration options for the web socket connection.\n             *  @param {string|Array<string>} [options.host=['localhost', 'localhost.qz.io']] Host running the QZ Tray software.\n             *  @param {Object} [options.port] Config options for ports to cycle.\n             *   @param {Array<number>} [options.port.secure=[8181, 8282, 8383, 8484]] Array of secure (WSS) ports to try\n             *   @param {Array<number>} [options.port.insecure=[8182, 8283, 8384, 8485]] Array of insecure (WS) ports to try\n             *  @param {boolean} [options.usingSecure=true] If the web socket should try to use secure ports for connecting.\n             *  @param {number} [options.keepAlive=60] Seconds between keep-alive pings to keep connection open. Set to 0 to disable.\n             *  @param {number} [options.retries=0] Number of times to reconnect before failing.\n             *  @param {number} [options.delay=0] Seconds before firing a connection.  Ignored if <code>options.retries</code> is 0.\n             *\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.websocket\n             */\n            connect: function(options) {\n                return _qz.tools.promise(function(resolve, reject) {\n                    if (_qz.websocket.connection) {\n                        const state = _qz.websocket.connection.readyState;\n\n                        if (state === _qz.tools.ws.OPEN) {\n                            reject(new Error(\"An open connection with QZ Tray already exists\"));\n                            return;\n                        } else if (state === _qz.tools.ws.CONNECTING) {\n                            reject(new Error(\"The current connection attempt has not returned yet\"));\n                            return;\n                        } else if (state === _qz.tools.ws.CLOSING) {\n                            reject(new Error(\"Waiting for previous disconnect request to complete\"));\n                            return;\n                        }\n                    }\n\n                    if (!_qz.tools.ws) {\n                        reject(new Error(\"WebSocket not supported by this browser\"));\n                        return;\n                    } else if (!_qz.tools.ws.CLOSED || _qz.tools.ws.CLOSED == 2) {\n                        reject(new Error(\"Unsupported WebSocket version detected: HyBi-00/Hixie-76\"));\n                        return;\n                    }\n\n                    //ensure some form of options exists for value checks\n                    if (options == undefined) { options = {}; }\n\n                    //disable secure ports if page is not secure\n                    if (typeof location === 'undefined' || location.protocol !== 'https:') {\n                        //respect forcing secure ports if it is defined, otherwise disable\n                        if (typeof options.usingSecure === 'undefined') {\n                            _qz.log.trace(\"Disabling secure ports due to insecure page\");\n                            options.usingSecure = false;\n                        }\n                    }\n\n                    //ensure any hosts are passed to internals as an array\n                    if (typeof options.host !== 'undefined' && !Array.isArray(options.host)) {\n                        options.host = [options.host];\n                    }\n\n                    _qz.websocket.shutdown = false; //reset state for new connection attempt\n                    var attempt = function(count) {\n                        var tried = false;\n                        var nextAttempt = function() {\n                            if (!tried) {\n                                tried = true;\n\n                                if (options && count < options.retries) {\n                                    attempt(count + 1);\n                                } else {\n                                    _qz.websocket.connection = null;\n                                    reject.apply(null, arguments);\n                                }\n                            }\n                        };\n\n                        var delayed = function() {\n                            var config = _qz.tools.extend({}, _qz.websocket.connectConfig, options);\n                            _qz.websocket.setup.findConnection(config, resolve, nextAttempt)\n                        };\n                        if (count == 0) {\n                            delayed(); // only retries will be called with a delay\n                        } else {\n                            setTimeout(delayed, options.delay * 1000);\n                        }\n                    };\n\n                    attempt(0);\n                });\n            },\n\n            /**\n             * Stop any active connection with QZ Tray.\n             *\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.websocket\n             */\n            disconnect: function() {\n                return _qz.tools.promise(function(resolve, reject) {\n                    if (_qz.websocket.connection != null) {\n                        if (_qz.tools.isActive()) {\n                            // handles closing both 'connecting' and 'connected' states\n                            _qz.websocket.shutdown = true;\n                            _qz.websocket.connection.promise = { resolve: resolve, reject: reject };\n                            _qz.websocket.connection.close();\n                        } else {\n                            reject(new Error(\"Current connection is still closing\"));\n                        }\n                    } else {\n                        reject(new Error(\"No open connection with QZ Tray\"));\n                    }\n                });\n            },\n\n            /**\n             * List of functions called for any connections errors outside of an API call.<p/>\n             * Also called if {@link websocket#connect} fails to connect.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Event} event)</code> calls.\n             *\n             * @memberof qz.websocket\n             */\n            setErrorCallbacks: function(calls) {\n                _qz.websocket.errorCallbacks = calls;\n            },\n\n            /**\n             * List of functions called for any connection closing event outside of an API call.<p/>\n             * Also called when {@link websocket#disconnect} is called.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Event} event)</code> calls.\n             *\n             * @memberof qz.websocket\n             */\n            setClosedCallbacks: function(calls) {\n                _qz.websocket.closedCallbacks = calls;\n            },\n\n            /**\n             * @deprecated Since 2.1.0.  Please use qz.networking.device() instead\n             *\n             * @param {string} [hostname] Hostname to try to connect to when determining network interfaces, defaults to \"google.com\"\n             * @param {number} [port] Port to use with custom hostname, defaults to 443\n             * @param {string} [signature] Pre-signed signature of hashed JSON string containing <code>call='websocket.getNetworkInfo'</code>, <code>params</code> object, and <code>timestamp</code>.\n             * @param {number} [signingTimestamp] Required with <code>signature</code>. Timestamp used with pre-signed content.\n             *\n             * @returns {Promise<Object<{ipAddress: string, macAddress: string}>|Error>} Connected system's network information.\n             *\n             * @memberof qz.websocket\n             */\n            getNetworkInfo: _qz.compatible.networking,\n\n            /**\n             * @returns {Object<{socket: String, host: String, port: Number}>} Details of active websocket connection\n             *\n             * @memberof qz.websocket\n             */\n            getConnectionInfo: function() {\n                if (_qz.tools.assertActive()) {\n                    var url = _qz.websocket.connection.url.split(/[:\\/]+/g);\n                    return { socket: url[0], host: url[1], port: +url[2] };\n                }\n            }\n        },\n\n\n        /**\n         * Calls related to getting printer information from the connection.\n         * @namespace qz.printers\n         */\n        printers: {\n            /**\n             * @param {string} [signature] Pre-signed signature of hashed JSON string containing <code>call='printers.getDefault</code>, <code>params</code>, and <code>timestamp</code>.\n             * @param {number} [signingTimestamp] Required with <code>signature</code>. Timestamp used with pre-signed content.\n             *\n             * @returns {Promise<string|Error>} Name of the connected system's default printer.\n             *\n             * @memberof qz.printers\n             */\n            getDefault: function(signature, signingTimestamp) {\n                return _qz.websocket.dataPromise('printers.getDefault', null, signature, signingTimestamp);\n            },\n\n            /**\n             * @param {string} [query] Search for a specific printer. All printers are returned if not provided.\n             * @param {string} [signature] Pre-signed signature of hashed JSON string containing <code>call='printers.find'</code>, <code>params</code>, and <code>timestamp</code>.\n             * @param {number} [signingTimestamp] Required with <code>signature</code>. Timestamp used with pre-signed content.\n             *\n             * @returns {Promise<Array<string>|string|Error>} The matched printer name if <code>query</code> is provided.\n             *                                                Otherwise an array of printer names found on the connected system.\n             *\n             * @memberof qz.printers\n             */\n            find: function(query, signature, signingTimestamp) {\n                return _qz.websocket.dataPromise('printers.find', { query: query }, signature, signingTimestamp);\n            },\n\n            /**\n             * Provides a list, with additional information, for each printer available to QZ.\n             *\n             * @returns {Promise<Array<Object>|Object|Error>}\n             *\n             * @memberof qz.printers\n             */\n            details: function() {\n                return _qz.websocket.dataPromise('printers.detail');\n            },\n\n            /**\n             * Start listening for printer status events, such as paper_jam events.\n             * Reported under the ACTION type in the streamEvent on callbacks.\n             *\n             * @returns {Promise<null|Error>}\n             * @since 2.1.0\n             *\n             * @see qz.printers.setPrinterCallbacks\n             *\n             * @param {null|string|Array<string>} printers Printer or list of printers to listen to, null listens to all.\n             * @param {Object|null} [options] Printer listener options\n             *  @param {null|boolean} [options.jobData=false] Flag indicating if raw spool file content should be return as well as status information (Windows only)\n             *  @param {null|number} [options.maxJobData=-1] Maximum number of bytes to returns for raw spooled file content (Windows only)\n             *  @param {null|string} [options.flavor=\"plain\"] Flavor of data format returned. Valid flavors are <code>[base64 | hex | plain*]</code> (Windows only)\n             *\n             * @memberof qz.printers\n             */\n            startListening: function(printers, options) {\n                if (!Array.isArray(printers)) {\n                    printers = [printers];\n                }\n                var params = {\n                    printerNames: printers\n                };\n                if (options && options.jobData == true) params.jobData = true;\n                if (options && options.maxJobData) params.maxJobData = options.maxJobData;\n                if (options && options.flavor) params.flavor = options.flavor;\n                return _qz.websocket.dataPromise('printers.startListening', params);\n            },\n\n            /**\n             * Clear the queue of a specified printer or printers. Does not delete retained jobs.\n             *\n             * @param {string|Object} [options] Name of printer to clear\n             *  @param {string} [options.printerName] Name of printer to clear\n             *  @param {number} [options.jobId] Cancel a job of a specific JobId instead of canceling all. Must include a printerName.\n             *\n             * @returns {Promise<null|Error>}\n             * @since 2.2.4\n             *\n             * @memberof qz.printers\n             */\n            clearQueue: function(options) {\n                if (typeof options !== 'object') {\n                    options = {\n                        printerName: options\n                    };\n                }\n                return _qz.websocket.dataPromise('printers.clearQueue', options);\n            },\n\n            /**\n             * Stop listening for printer status actions.\n             *\n             * @returns {Promise<null|Error>}\n             * @since 2.1.0\n             *\n             * @see qz.printers.setPrinterCallbacks\n             *\n             * @memberof qz.printers\n             */\n            stopListening: function() {\n                return _qz.websocket.dataPromise('printers.stopListening');\n            },\n\n            /**\n             * Retrieve current printer status from any active listeners.\n             *\n             * @returns {Promise<null|Error>}\n             * @since 2.1.0\n             *\n             * @see qz.printers.startListening\n             *\n             * @memberof qz.printers\n             */\n            getStatus: function() {\n                return _qz.websocket.dataPromise('printers.getStatus');\n            },\n\n            /**\n             * List of functions called for any printer status change.\n             * Event data will contain <code>{string} printerName</code> and <code>{string} status</code> for all types.\n             *  For RECEIVE types, <code>{Array} output</code> (in hexadecimal format).\n             *  For ERROR types, <code>{string} exception</code>.\n             *  For ACTION types, <code>{string} actionType</code>.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Object} eventData)</code> calls.\n             * @since 2.1.0\n             *\n             * @memberof qz.printers\n             */\n            setPrinterCallbacks: function(calls) {\n                _qz.printers.printerCallbacks = calls;\n            }\n        },\n\n        /**\n         * Calls related to setting up new printer configurations.\n         * @namespace qz.configs\n         */\n        configs: {\n            /**\n             * Default options used by new configs if not overridden.\n             * Setting a value to NULL will use the printer's default options.\n             * Updating these will not update the options on any created config.\n             *\n             * @param {Object} options Default options used by printer configs if not overridden.\n             *\n             *  @param {Object} [options.bounds=null] Bounding box rectangle.\n             *   @param {number} [options.bounds.x=0] Distance from left for bounding box starting corner\n             *   @param {number} [options.bounds.y=0] Distance from top for bounding box starting corner\n             *   @param {number} [options.bounds.width=0] Width of bounding box\n             *   @param {number} [options.bounds.height=0] Height of bounding box\n             *  @param {string} [options.colorType='color'] Valid values <code>[color | grayscale | blackwhite | default]</code>\n             *  @param {number} [options.copies=1] Number of copies to be printed.\n             *  @param {number|Array<number>|Object|Array<Object>|string} [options.density=0] Pixel density (DPI, DPMM, or DPCM depending on <code>[options.units]</code>).\n             *      If provided as an array, uses the first supported density found (or the first entry if none found).\n             *      If provided as a string, valid values are <code>[best | draft]</code>, corresponding to highest or lowest reported density respectively.\n             *  @param {number} [options.density.cross=0] Asymmetric pixel density for the cross feed direction.\n             *  @param {number} [options.density.feed=0] Asymmetric pixel density for the feed direction.\n             *  @param {boolean|string} [options.duplex=false] Double sided printing, Can specify duplex style by passing a string value: <code>[one-sided | duplex | long-edge | tumble | short-edge]</code>\n             *  @param {number} [options.fallbackDensity=null] Value used when default density value cannot be read, or in cases where reported as \"Normal\" by the driver, (in DPI, DPMM, or DPCM depending on <code>[options.units]</code>).\n             *  @param {string} [options.interpolation='bicubic'] Valid values <code>[bicubic | bilinear | nearest-neighbor]</code>. Controls how images are handled when resized.\n             *  @param {string} [options.jobName=null] Name to display in print queue.\n             *  @param {boolean} [options.legacy=false] If legacy style printing should be used.\n             *  @param {Object|number} [options.margins=0] If just a number is provided, it is used as the margin for all sides.\n             *   @param {number} [options.margins.top=0]\n             *   @param {number} [options.margins.right=0]\n             *   @param {number} [options.margins.bottom=0]\n             *   @param {number} [options.margins.left=0]\n             *  @param {string} [options.orientation=null] Valid values <code>[portrait | landscape | reverse-landscape | null]</code>.\n             *                                             If set to <code>null</code>, orientation will be determined automatically.\n             *  @param {number} [options.paperThickness=null]\n             *  @param {string|number} [options.printerTray=null] Printer tray to pull from. The number N assumes string equivalent of 'Tray N'. Uses printer default if NULL.\n             *  @param {boolean} [options.rasterize=false] Whether documents should be rasterized before printing.\n             *                                             Specifying <code>[options.density]</code> for PDF print formats will set this to <code>true</code>.\n             *  @param {number} [options.rotation=0] Image rotation in degrees.\n             *  @param {boolean} [options.scaleContent=true] Scales print content to page size, keeping ratio.\n             *  @param {Object} [options.size=null] Paper size.\n             *   @param {number} [options.size.width=null] Page width.\n             *   @param {number} [options.size.height=null] Page height.\n             *  @param {string} [options.units='in'] Page units, applies to paper size, margins, and density. Valid value <code>[in | cm | mm]</code>\n             *\n             *  @param {boolean} [options.forceRaw=false] Print the specified raw data using direct method, skipping the driver.  Not yet supported on Windows.\n             *  @param {string|Object} [options.encoding=null] Character set for commands. Can be provided as an object for converting encoding types for RAW types.\n             *   @param {string} [options.encoding.from] If this encoding type is provided, RAW type commands will be parsed from this for the purpose of being converted to the <code>encoding.to</code> value.\n             *   @param {string} [options.encoding.to] Encoding RAW type commands will be converted into. If <Code>encoding.from</code> is not provided, this will be treated as if a string was passed for encoding.\n             *  @param {string} [options.endOfDoc=null] DEPRECATED Raw only: Character(s) denoting end of a page to control spooling.\n             *  @param {number} [options.perSpool=1] DEPRECATED: Raw only: Number of pages per spool.\n             *  @param {boolean} [options.retainTemp=false] Retain any temporary files used.  Ignored unless <code>forceRaw</code> <code>true</code>.\n             *  @param {Object} [options.spool=null] Advanced spooling options.\n             *   @param {number} [options.spool.size=null] Number of pages per spool.  Default is no limit.  If <code>spool.end</code> is provided, defaults to <code>1</code>\n             *   @param {string} [options.spool.end=null] Raw only: Character(s) denoting end of a page to control spooling.\n             *\n             * @memberof qz.configs\n             */\n            setDefaults: function(options) {\n                _qz.tools.extend(_qz.printing.defaultConfig, options);\n            },\n\n            /**\n             * Creates new printer config to be used in printing.\n             *\n             * @param {string|object} printer Name of printer. Use object type to specify printing to file or host.\n             *  @param {string} [printer.name] Name of printer to send printing.\n             *  @param {string} [printer.file] Name of file to send printing.\n             *  @param {string} [printer.host] IP address or host name to send printing.\n             *  @param {string} [printer.port] Port used by &lt;printer.host>.\n             * @param {Object} [options] Override any of the default options for this config only.\n             *\n             * @returns {Config} The new config.\n             *\n             * @see configs.setDefaults\n             *\n             * @memberof qz.configs\n             */\n            create: function(printer, options) {\n                return new Config(printer, options);\n            }\n        },\n\n\n        /**\n         * Send data to selected config for printing.\n         * The promise for this method will resolve when the document has been sent to the printer. Actual printing may not be complete.\n         * <p/>\n         * Optionally, print requests can be pre-signed:\n         * Signed content consists of a JSON object string containing no spacing,\n         * following the format of the \"call\" and \"params\" keys in the API call, with the addition of a \"timestamp\" key in milliseconds\n         * ex. <code>'{\"call\":\"<callName>\",\"params\":{...},\"timestamp\":1450000000}'</code>\n         *\n         * @param {Object<Config>|Array<Object<Config>>} configs Previously created config object or objects.\n         * @param {Array<Object|string>|Array<Array<Object|string>>} data Array of data being sent to the printer.<br/>\n         *      String values are interpreted as <code>{type: 'raw', format: 'command', flavor: 'plain', data: &lt;string>}</code>.\n         *  @param {string} data.data\n         *  @param {string} data.type Printing type. Valid types are <code>[pixel | raw*]</code>. *Default\n         *  @param {string} data.format Format of data type used. *Default per type<p/>\n         *      For <code>[pixel]</code> types, valid formats are <code>[html | image* | pdf]</code>.<p/>\n         *      For <code>[raw]</code> types, valid formats are <code>[command* | html | image | pdf]</code>.\n         *  @param {string} data.flavor Flavor of data format used. *Default per format<p/>\n         *      For <code>[command]</code> formats, valid flavors are <code>[base64 | file | hex | plain* | xml]</code>.<p/>\n         *      For <code>[html]</code> formats, valid flavors are <code>[file* | plain]</code>.<p/>\n         *      For <code>[image]</code> formats, valid flavors are <code>[base64 | file*]</code>.<p/>\n         *      For <code>[pdf]</code> formats, valid flavors are <code>[base64 | file*]</code>.\n         *  @param {Object} [data.options]\n         *   @param {string} [data.options.language] Required with <code>[raw]</code> type + <code>[image]</code> format. Printer language.\n         *   @param {number} [data.options.x] Optional with <code>[raw]</code> type + <code>[image]</code> format. The X position of the image.\n         *   @param {number} [data.options.y] Optional with <code>[raw]</code> type + <code>[image]</code> format. The Y position of the image.\n         *   @param {string|number} [data.options.dotDensity] Optional with <code>[raw]</code> type + <code>[image]</code> format.\n         *   @param {number} [data.precision=128] Optional with <code>[raw]</code> type <code>[image]</code> format. Bit precision of the ribbons.\n         *   @param {boolean|string|Array<Array<number>>} [data.options.overlay=false] Optional with <code>[raw]</code> type <code>[image]</code> format.\n         *       Boolean sets entire layer, string sets mask image, Array sets array of rectangles in format <code>[x1,y1,x2,y2]</code>.\n         *   @param {string} [data.options.xmlTag] Required with <code>[xml]</code> flavor. Tag name containing base64 formatted data.\n         *   @param {number} [data.options.pageWidth] Optional with <code>[html | pdf]</code> formats. Width of the rendering.\n         *       Defaults to paper width.\n         *   @param {number} [data.options.pageHeight] Optional with <code>[html | pdf]</code> formats. Height of the rendering.\n         *       Defaults to paper height for <code>[pdf]</code>, or auto sized for <code>[html]</code>.\n         *   @param {string} [data.options.pageRanges] Optional with <code>[pdf]</code> formats. Comma-separated list of page ranges to include.\n         *   @param {boolean} [data.options.ignoreTransparency=false] Optional with <code>[pdf]</code> formats. Instructs transparent PDF elements to be ignored.\n         *       Transparent PDF elements are known to degrade performance and quality when printing.\n         *   @param {boolean} [data.options.altFontRendering=false] Optional with <code>[pdf]</code> formats. Instructs PDF to be rendered using PDFBOX 1.8 techniques.\n         *       Drastically improves low-DPI PDF print quality on Windows.\n         * @param {...*} [arguments] Additionally three more parameters can be specified:<p/>\n         *     <code>{boolean} [resumeOnError=false]</code> Whether the chain should continue printing if it hits an error on one the the prints.<p/>\n         *     <code>{string|Array<string>} [signature]</code> Pre-signed signature(s) of the JSON string for containing <code>call</code>, <code>params</code>, and <code>timestamp</code>.<p/>\n         *     <code>{number|Array<number>} [signingTimestamps]</code> Required to match with <code>signature</code>. Timestamps for each of the passed pre-signed content.\n         *\n         * @returns {Promise<null|Error>}\n         *\n         * @see qz.configs.create\n         *\n         * @memberof qz\n         */\n        print: function(configs, data) {\n            var resumeOnError = false,\n                signatures = [],\n                signaturesTimestamps = [];\n\n            //find optional parameters\n            if (arguments.length >= 3) {\n                if (typeof arguments[2] === 'boolean') {\n                    resumeOnError = arguments[2];\n\n                    if (arguments.length >= 5) {\n                        signatures = arguments[3];\n                        signaturesTimestamps = arguments[4];\n                    }\n                } else if (arguments.length >= 4) {\n                    signatures = arguments[2];\n                    signaturesTimestamps = arguments[3];\n                }\n\n                //ensure values are arrays for consistency\n                if (signatures && !Array.isArray(signatures)) { signatures = [signatures]; }\n                if (signaturesTimestamps && !Array.isArray(signaturesTimestamps)) { signaturesTimestamps = [signaturesTimestamps]; }\n            }\n\n            if (!Array.isArray(configs)) { configs = [configs]; } //single config -> array of configs\n            if (!Array.isArray(data[0])) { data = [data]; } //single data array -> array of data arrays\n\n            //clean up data formatting\n            for(var d = 0; d < data.length; d++) {\n                _qz.tools.relative(data[d]);\n                _qz.compatible.data(data[d]);\n            }\n\n            var sendToPrint = function(mapping) {\n                var params = {\n                    printer: mapping.config.getPrinter(),\n                    options: mapping.config.getOptions(),\n                    data: mapping.data\n                };\n\n                return _qz.websocket.dataPromise('print', params, mapping.signature, mapping.timestamp);\n            };\n\n            //chain instead of Promise.all, so resumeOnError can collect each error\n            var chain = [];\n            for(var i = 0; i < configs.length || i < data.length; i++) {\n                (function(i_) {\n                    var map = {\n                        config: configs[Math.min(i_, configs.length - 1)],\n                        data: data[Math.min(i_, data.length - 1)],\n                        signature: signatures[i_],\n                        timestamp: signaturesTimestamps[i_]\n                    };\n\n                    chain.push(function() { return sendToPrint(map) });\n                })(i);\n            }\n\n            //setup to catch errors if needed\n            var fallThrough = null;\n            if (resumeOnError) {\n                var fallen = [];\n                fallThrough = function(err) { fallen.push(err); };\n\n                //final promise to reject any errors as a group\n                chain.push(function() {\n                    return _qz.tools.promise(function(resolve, reject) {\n                        fallen.length ? reject(fallen) : resolve();\n                    });\n                });\n            }\n\n            var last = null;\n            chain.reduce(function(sequence, link) {\n                last = sequence.catch(fallThrough).then(link); //catch is ignored if fallThrough is null\n                return last;\n            }, _qz.tools.promise(function(r) { r(); })); //an immediately resolved promise to start off the chain\n\n            //return last promise so users can chain off final action or catch when stopping on error\n            return last;\n        },\n\n\n        /**\n         * Calls related to interaction with serial ports.\n         * @namespace qz.serial\n         */\n        serial: {\n            /**\n             * @returns {Promise<Array<string>|Error>} Communication (RS232, COM, TTY) ports available on connected system.\n             *\n             * @memberof qz.serial\n             */\n            findPorts: function() {\n                return _qz.websocket.dataPromise('serial.findPorts');\n            },\n\n            /**\n             * List of functions called for any response from open serial ports.\n             * Event data will contain <code>{string} portName</code> for all types.\n             *  For RECEIVE types, <code>{string} output</code>.\n             *  For ERROR types, <code>{string} exception</code>.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({object} streamEvent)</code> calls.\n             *\n             * @memberof qz.serial\n             */\n            setSerialCallbacks: function(calls) {\n                _qz.serial.serialCallbacks = calls;\n            },\n\n            /**\n             * Opens a serial port for sending and receiving data\n             *\n             * @param {string} port Name of serial port to open.\n             * @param {Object} [options] Serial port configurations.\n             *  @param {number} [options.baudRate=9600] Serial port speed. Set to 0 for auto negotiation.\n             *  @param {number} [options.dataBits=8] Serial port data bits. Set to 0 for auto negotiation.\n             *  @param {number} [options.stopBits=1] Serial port stop bits. Set to 0 for auto negotiation.\n             *  @param {string} [options.parity='NONE'] Serial port parity. Set to AUTO for auto negotiation. Valid values <code>[NONE | EVEN | ODD | MARK | SPACE | AUTO]</code>\n             *  @param {string} [options.flowControl='NONE'] Serial port flow control. Set to AUTO for auto negotiation. Valid values <code>[NONE | XONXOFF | XONXOFF_OUT | XONXOFF_IN | RTSCTS | RTSCTS_OUT | RTSCTS_IN | AUTO]</code>\n             *  @param {string} [options.encoding='UTF-8'] Character set for communications.\n             *  @param {string} [options.start=0x0002] DEPRECATED: Legacy character denoting start of serial response. Use <code>options.rx.start</code> instead.\n             *  @param {string} [options.end=0x000D] DEPRECATED: Legacy character denoting end of serial response. Use <code>options.rx.end</code> instead.\n             *  @param {number} [options.width] DEPRECATED: Legacy use for fixed-width response serial communication. Use <code>options.rx.width</code> instead.\n             *  @param {Object} [options.rx] Serial communications response definitions. If an object is passed but no options are defined, all response data will be sent back as it is received unprocessed.\n             *   @param {string|Array<string>} [options.rx.start] Character(s) denoting start of response bytes. Used in conjunction with `end`, `width`, or `lengthbit` property.\n             *   @param {string} [options.rx.end] Character denoting end of response bytes. Used in conjunction with `start` property.\n             *   @param {number} [options.rx.width] Fixed width size of response bytes (not including header if `start` is set). Used alone or in conjunction with `start` property.\n             *   @param {boolean} [options.rx.untilNewline] Returns data between newline characters (`\\n` or `\\r`) Truncates empty responses.  Overrides `start`, `end`, `width`.\n             *   @param {number|Object} [options.rx.lengthBytes] If a number is passed it is treated as the length index. Other values are left as their defaults.\n             *    @param {number} [options.rx.lengthBytes.index=0] Position of the response byte (not including response `start` bytes) used to denote the length of the remaining response data.\n             *    @param {number} [options.rx.lengthBytes.length=1] Length of response length bytes after response header.\n             *    @param {string} [options.rx.lengthBytes.endian='BIG'] Byte endian for multi-byte length values. Valid values <code>[BIG | LITTLE]</code>\n             *   @param {number|Object} [options.rx.crcBytes] If a number is passed it is treated as the crc length. Other values are left as their defaults.\n             *    @param {number} [options.rx.crcBytes.index=0] Position after the response data (not including length or data bytes) used to denote the crc.\n             *    @param {number} [options.rx.crcBytes.length=1] Length of response crc bytes after the response data length.\n             *   @param {boolean} [options.rx.includeHeader=false] Whether any of the header bytes (`start` bytes and any length bytes) should be included in the processed response.\n             *   @param {string} [options.rx.encoding] Override the encoding used for response data. Uses the same value as <code>options.encoding</code> otherwise.\n             *\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.serial\n             */\n            openPort: function(port, options) {\n                var params = {\n                    port: port,\n                    options: options\n                };\n                return _qz.websocket.dataPromise('serial.openPort', params);\n            },\n\n            /**\n             * Send commands over a serial port.\n             * Any responses from the device will be sent to serial callback functions.\n             *\n             * @param {string} port An open serial port to send data.\n             * @param {string|Array<string>|Object} data Data to be sent to the serial device.\n             *  @param {string} [data.type='PLAIN'] Valid values <code>[FILE | PLAIN | HEX | BASE64]</code>\n             *  @param {string|Array<string>} data.data Data to be sent to the serial device.\n             * @param {Object} options Serial port configuration updates. See <code>qz.serial.openPort</code> `options` docs for available values.\n             *     For best performance, it is recommended to only set these values on the port open call.\n             *\n             * @returns {Promise<null|Error>}\n             *\n             * @see qz.serial.setSerialCallbacks\n             *\n             * @memberof qz.serial\n             */\n            sendData: function(port, data, options) {\n                if (_qz.tools.versionCompare(2, 1, 0, 12) >= 0) {\n                    if (typeof data !== 'object') {\n                        data = {\n                            data: data,\n                            type: \"PLAIN\"\n                        }\n                    }\n\n                    if (data.type && data.type.toUpperCase() == \"FILE\") {\n                        data.data = _qz.tools.absolute(data.data);\n                    }\n                }\n\n                var params = {\n                    port: port,\n                    data: data,\n                    options: options\n                };\n                return _qz.websocket.dataPromise('serial.sendData', params);\n            },\n\n            /**\n             * @param {string} port Name of port to close.\n             *\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.serial\n             */\n            closePort: function(port) {\n                return _qz.websocket.dataPromise('serial.closePort', { port: port });\n            }\n        },\n\n        /**\n         * Calls related to interaction with communication sockets.\n         * @namespace qz.socket\n         */\n        socket: {\n            /**\n             * Opens a network port for sending and receiving data.\n             *\n             * @param {string} host The connection hostname.\n             * @param {number} port The connection port number.\n             * @param {Object} [options] Network socket configuration.\n             *  @param {string} [options.encoding='UTF-8'] Character set for communications.\n             *\n             * @memberof qz.socket\n             */\n            open: function(host, port, options) {\n                var params = {\n                    host: host,\n                    port: port,\n                    options: options\n                };\n                return _qz.websocket.dataPromise(\"socket.open\", params);\n            },\n\n            /**\n             * @param {string} host The connection hostname.\n             * @param {number} port The connection port number.\n             *\n             * @memberof qz.socket\n             */\n            close: function(host, port) {\n                var params = {\n                    host: host,\n                    port: port\n                };\n                return _qz.websocket.dataPromise(\"socket.close\", params);\n            },\n\n            /**\n             * Send data over an open socket.\n             *\n             * @param {string} host The connection hostname.\n             * @param {number} port The connection port number.\n             * @param {string|Object} data Data to be sent over the port.\n             *  @param {string} [data.type='PLAIN'] Valid values <code>[PLAIN]</code>\n             *  @param {string} data.data Data to be sent over the port.\n             *\n             * @memberof qz.socket\n             */\n            sendData: function(host, port, data) {\n                if (typeof data !== 'object') {\n                    data = {\n                        data: data,\n                        type: \"PLAIN\"\n                    };\n                }\n\n                var params = {\n                    host: host,\n                    port: port,\n                    data: data\n                };\n                return _qz.websocket.dataPromise(\"socket.sendData\", params);\n            },\n\n            /**\n             * List of functions called for any response from open network sockets.\n             * Event data will contain <code>{string} host</code> and <code>{number} port</code> for all types.\n             *  For RECEIVE types, <code>{string} response</code>.\n             *  For ERROR types, <code>{string} exception</code>.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Object} eventData)</code> calls.\n             *\n             * @memberof qz.socket\n             */\n            setSocketCallbacks: function(calls) {\n                _qz.socket.socketCallbacks = calls;\n            }\n        },\n\n        /**\n         * Calls related to interaction with USB devices.\n         * @namespace qz.usb\n         */\n        usb: {\n            /**\n             * List of available USB devices. Includes (hexadecimal) vendor ID, (hexadecimal) product ID, and hub status.\n             * If supported, also returns manufacturer and product descriptions.\n             *\n             * @param includeHubs Whether to include USB hubs.\n             * @returns {Promise<Array<Object>|Error>} Array of JSON objects containing information on connected USB devices.\n             *\n             * @memberof qz.usb\n             */\n            listDevices: function(includeHubs) {\n                return _qz.websocket.dataPromise('usb.listDevices', { includeHubs: includeHubs });\n            },\n\n            /**\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             * @returns {Promise<Array<string>|Error>} List of available (hexadecimal) interfaces on a USB device.\n             *\n             * @memberof qz.usb\n             */\n            listInterfaces: function(deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('usb.listInterfaces', deviceInfo);\n            },\n\n            /**\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             *  @param deviceInfo.iface Hex string of interface on the USB device to search.\n             * @returns {Promise<Array<string>|Error>} List of available (hexadecimal) endpoints on a USB device's interface.\n             *\n             * @memberof qz.usb\n             */\n            listEndpoints: function(deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        interface: arguments[2]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('usb.listEndpoints', deviceInfo);\n            },\n\n            /**\n             * List of functions called for any response from open usb devices.\n             * Event data will contain <code>{string} vendorId</code> and <code>{string} productId</code> for all types.\n             *  For RECEIVE types, <code>{Array} output</code> (in hexadecimal format).\n             *  For ERROR types, <code>{string} exception</code>.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Object} eventData)</code> calls.\n             *\n             * @memberof qz.usb\n             */\n            setUsbCallbacks: function(calls) {\n                _qz.usb.usbCallbacks = calls;\n            },\n\n            /**\n             * Claim a USB device's interface to enable sending/reading data across an endpoint.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             *  @param deviceInfo.interface Hex string of interface on the USB device to claim.\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.usb\n             */\n            claimDevice: function(deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        interface: arguments[2]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('usb.claimDevice', deviceInfo);\n            },\n\n            /**\n             * Check the current claim state of a USB device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             * @returns {Promise<boolean|Error>}\n             *\n             * @since 2.0.2\n             * @memberOf qz.usb\n             */\n            isClaimed: function(deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('usb.isClaimed', deviceInfo);\n            },\n\n            /**\n             * Send data to a claimed USB device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             *  @param deviceInfo.endpoint Hex string of endpoint on the claimed interface for the USB device.\n             *  @param deviceInfo.data Bytes to send over specified endpoint.\n             *  @param {string} [deviceInfo.type='PLAIN'] Valid values <code>[FILE | PLAIN | HEX | BASE64]</code>\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.usb\n             */\n            sendData: function(deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        endpoint: arguments[2],\n                        data: arguments[3]\n                    };\n                }\n\n                if (_qz.tools.versionCompare(2, 1, 0, 12) >= 0) {\n                    if (typeof deviceInfo.data !== 'object') {\n                        deviceInfo.data = {\n                            data: deviceInfo.data,\n                            type: \"PLAIN\"\n                        }\n                    }\n\n                    if (deviceInfo.data.type && deviceInfo.data.type.toUpperCase() == \"FILE\") {\n                        deviceInfo.data.data = _qz.tools.absolute(deviceInfo.data.data);\n                    }\n                }\n\n                return _qz.websocket.dataPromise('usb.sendData', deviceInfo);\n            },\n\n            /**\n             * Read data from a claimed USB device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             *  @param deviceInfo.endpoint Hex string of endpoint on the claimed interface for the USB device.\n             *  @param deviceInfo.responseSize Size of the byte array to receive a response in.\n             * @returns {Promise<Array<string>|Error>} List of (hexadecimal) bytes received from the USB device.\n             *\n             * @memberof qz.usb\n             */\n            readData: function(deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        endpoint: arguments[2],\n                        responseSize: arguments[3]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('usb.readData', deviceInfo);\n            },\n\n            /**\n             * Provides a continuous stream of read data from a claimed USB device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             *  @param deviceInfo.endpoint Hex string of endpoint on the claimed interface for the USB device.\n             *  @param deviceInfo.responseSize Size of the byte array to receive a response in.\n             *  @param deviceInfo.interval=100 Frequency to send read data back, in milliseconds.\n             * @returns {Promise<null|Error>}\n             *\n             * @see qz.usb.setUsbCallbacks\n             *\n             * @memberof qz.usb\n             */\n            openStream: function(deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        endpoint: arguments[2],\n                        responseSize: arguments[3],\n                        interval: arguments[4]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('usb.openStream', deviceInfo);\n            },\n\n            /**\n             * Stops the stream of read data from a claimed USB device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             *  @param deviceInfo.endpoint Hex string of endpoint on the claimed interface for the USB device.\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.usb\n             */\n            closeStream: function(deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        endpoint: arguments[2]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('usb.closeStream', deviceInfo);\n            },\n\n            /**\n             * Release a claimed USB device to free resources after sending/reading data.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of USB device's vendor ID.\n             *  @param deviceInfo.productId Hex string of USB device's product ID.\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.usb\n             */\n            releaseDevice: function(deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('usb.releaseDevice', deviceInfo);\n            }\n        },\n\n\n        /**\n         * Calls related to interaction with HID USB devices<br/>\n         * Many of these calls can be accomplished from the <code>qz.usb</code> namespace,\n         * but HID allows for simpler interaction\n         * @namespace qz.hid\n         * @since 2.0.1\n         */\n        hid: {\n            /**\n             * List of available HID devices. Includes (hexadecimal) vendor ID and (hexadecimal) product ID.\n             * If available, also returns manufacturer and product descriptions.\n             *\n             * @returns {Promise<Array<Object>|Error>} Array of JSON objects containing information on connected HID devices.\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            listDevices: function() {\n                return _qz.websocket.dataPromise('hid.listDevices');\n            },\n\n            /**\n             * Start listening for HID device actions, such as attach / detach events.\n             * Reported under the ACTION type in the streamEvent on callbacks.\n             *\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @see qz.hid.setHidCallbacks\n             *\n             * @memberof qz.hid\n             */\n            startListening: function() {\n                return _qz.websocket.dataPromise('hid.startListening');\n            },\n\n            /**\n             * Stop listening for HID device actions.\n             *\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @see qz.hid.setHidCallbacks\n             *\n             * @memberof qz.hid\n             */\n            stopListening: function() {\n                return _qz.websocket.dataPromise('hid.stopListening');\n            },\n\n            /**\n             * List of functions called for any response from open usb devices.\n             * Event data will contain <code>{string} vendorId</code> and <code>{string} productId</code> for all types.\n             *  For RECEIVE types, <code>{Array} output</code> (in hexadecimal format).\n             *  For ERROR types, <code>{string} exception</code>.\n             *  For ACTION types, <code>{string} actionType</code>.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Object} eventData)</code> calls.\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            setHidCallbacks: function(calls) {\n                _qz.hid.hidCallbacks = calls;\n            },\n\n            /**\n             * Claim a HID device to enable sending/reading data across.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            claimDevice: function(deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('hid.claimDevice', deviceInfo);\n            },\n\n            /**\n             * Check the current claim state of a HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             * @returns {Promise<boolean|Error>}\n             *\n             * @since 2.0.2\n             * @memberOf qz.hid\n             */\n            isClaimed: function(deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('hid.isClaimed', deviceInfo);\n            },\n\n            /**\n             * Send data to a claimed HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             *  @param deviceInfo.data Bytes to send over specified endpoint.\n             *  @param deviceInfo.endpoint=0x00 First byte of the data packet signifying the HID report ID.\n             *                             Must be 0x00 for devices only supporting a single report.\n             *  @param deviceInfo.reportId=0x00 Alias for <code>deviceInfo.endpoint</code>. Not used if endpoint is provided.\n             *  @param {string} [deviceInfo.type='PLAIN'] Valid values <code>[FILE | PLAIN | HEX | BASE64]</code>\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            sendData: function(deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        data: arguments[2],\n                        endpoint: arguments[3]\n                    };\n                }\n\n                if (_qz.tools.versionCompare(2, 1, 0, 12) >= 0) {\n                    if (typeof deviceInfo.data !== 'object') {\n                        deviceInfo.data = {\n                            data: deviceInfo.data,\n                            type: \"PLAIN\"\n                        }\n                    }\n\n                    if (deviceInfo.data.type && deviceInfo.data.type.toUpperCase() == \"FILE\") {\n                        deviceInfo.data.data = _qz.tools.absolute(deviceInfo.data.data);\n                    }\n                } else {\n                    if (typeof deviceInfo.data === 'object') {\n                        if (deviceInfo.data.type.toUpperCase() !== \"PLAIN\"\n                            || typeof deviceInfo.data.data !== \"string\") {\n                            return _qz.tools.reject(new Error(\"Data format is not supported with connected QZ Tray version \" + _qz.websocket.connection.version));\n                        }\n\n                        deviceInfo.data = deviceInfo.data.data;\n                    }\n                }\n\n                return _qz.websocket.dataPromise('hid.sendData', deviceInfo);\n            },\n\n            /**\n             * Read data from a claimed HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             *  @param deviceInfo.responseSize Size of the byte array to receive a response in.\n             * @returns {Promise<Array<string>|Error>} List of (hexadecimal) bytes received from the HID device.\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            readData: function(deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        responseSize: arguments[2]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('hid.readData', deviceInfo);\n            },\n\n            /**\n             * Send a feature report to a claimed HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             *  @param deviceInfo.data Bytes to send over specified endpoint.\n             *  @param deviceInfo.endpoint=0x00 First byte of the data packet signifying the HID report ID.\n             *                             Must be 0x00 for devices only supporting a single report.\n             *  @param deviceInfo.reportId=0x00 Alias for <code>deviceInfo.endpoint</code>. Not used if endpoint is provided.\n             *  @param {string} [deviceInfo.type='PLAIN'] Valid values <code>[FILE | PLAIN | HEX | BASE64]</code>\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.hid\n             */\n            sendFeatureReport: function(deviceInfo) {\n                return _qz.websocket.dataPromise('hid.sendFeatureReport', deviceInfo);\n            },\n\n            /**\n             * Get a feature report from a claimed HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             *  @param deviceInfo.responseSize Size of the byte array to receive a response in.\n             * @returns {Promise<Array<string>|Error>} List of (hexadecimal) bytes received from the HID device.\n             *\n             * @memberof qz.hid\n             */\n            getFeatureReport: function(deviceInfo) {\n                return _qz.websocket.dataPromise('hid.getFeatureReport', deviceInfo);\n            },\n\n            /**\n             * Provides a continuous stream of read data from a claimed HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             *  @param deviceInfo.responseSize Size of the byte array to receive a response in.\n             *  @param deviceInfo.interval=100 Frequency to send read data back, in milliseconds.\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @see qz.hid.setHidCallbacks\n             *\n             * @memberof qz.hid\n             */\n            openStream: function(deviceInfo) {\n                //backwards compatibility\n                if (typeof deviceInfo !== 'object') {\n                    deviceInfo = {\n                        vendorId: arguments[0],\n                        productId: arguments[1],\n                        responseSize: arguments[2],\n                        interval: arguments[3]\n                    };\n                }\n\n                return _qz.websocket.dataPromise('hid.openStream', deviceInfo);\n            },\n\n            /**\n             * Stops the stream of read data from a claimed HID device.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            closeStream: function(deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('hid.closeStream', deviceInfo);\n            },\n\n            /**\n             * Release a claimed HID device to free resources after sending/reading data.\n             *\n             * @param {object} deviceInfo Config details of the HID device.\n             *  @param deviceInfo.vendorId Hex string of HID device's vendor ID.\n             *  @param deviceInfo.productId Hex string of HID device's product ID.\n             *  @param deviceInfo.usagePage Hex string of HID device's usage page when multiple are present.\n             *  @param deviceInfo.serial Serial ID of HID device.\n             * @returns {Promise<null|Error>}\n             * @since 2.0.1\n             *\n             * @memberof qz.hid\n             */\n            releaseDevice: function(deviceInfo) {\n                if (typeof deviceInfo !== 'object') { deviceInfo = { vendorId: arguments[0], productId: arguments[1] }; } //backwards compatibility\n\n                return _qz.websocket.dataPromise('hid.releaseDevice', deviceInfo);\n            }\n        },\n\n\n        /**\n         * Calls related to interactions with the filesystem\n         * @namespace qz.file\n         * @since 2.1\n         */\n        file: {\n            /**\n             * List of files available at the given directory.<br/>\n             * Due to security reasons, paths are limited to the qz data directory unless overridden via properties file.\n             *\n             * @param {string} path Relative or absolute directory path. Must reside in qz data directory or a white-listed location.\n             * @param {Object} [params] Object containing file access parameters\n             *  @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections\n             *  @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user\n             * @returns {Promise<Array<String>|Error>} Array of files at the given path\n             *\n             * @memberof qz.file\n             */\n            list: function(path, params) {\n                var param = _qz.tools.extend({ path: path }, params);\n                return _qz.websocket.dataPromise('file.list', param);\n            },\n\n            /**\n             * Reads contents of file at the given path.<br/>\n             * Due to security reasons, paths are limited to the qz data directory unless overridden via properties file.\n             *\n             * @param {string} path Relative or absolute file path. Must reside in qz data directory or a white-listed location.\n             * @param {Object} [params] Object containing file access parameters\n             *  @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections\n             *  @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user\n             *  @param {string} [params.flavor='plain'] Flavor of data format used, valid flavors are <code>[base64 | hex | plain]</code>.\n             * @returns {Promise<String|Error>} String containing the file contents\n             *\n             * @memberof qz.file\n             */\n            read: function(path, params) {\n                var param = _qz.tools.extend({ path: path }, params);\n                return _qz.websocket.dataPromise('file.read', param);\n            },\n\n            /**\n             * Writes data to the file at the given path.<br/>\n             * Due to security reasons, paths are limited to the qz data directory unless overridden via properties file.\n             *\n             * @param {string} path Relative or absolute file path. Must reside in qz data directory or a white-listed location.\n             * @param {Object} params Object containing file access parameters\n             *  @param {string} params.data File data to be written\n             *  @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections\n             *  @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user\n             *  @param {boolean} [params.append=false] Appends to the end of the file if set, otherwise overwrites existing contents\n             *  @param {string} [params.flavor='plain'] Flavor of data format used, valid flavors are <code>[base64 | file | hex | plain]</code>.\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.file\n             */\n            write: function(path, params) {\n                var param = _qz.tools.extend({ path: path }, params);\n                return _qz.websocket.dataPromise('file.write', param);\n            },\n\n            /**\n             * Deletes a file at given path.<br/>\n             * Due to security reasons, paths are limited to the qz data directory unless overridden via properties file.\n             *\n             * @param {string} path Relative or absolute file path. Must reside in qz data directory or a white-listed location.\n             * @param {Object} [params] Object containing file access parameters\n             *  @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections\n             *  @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.file\n             */\n            remove: function(path, params) {\n                var param = _qz.tools.extend({ path: path }, params);\n                return _qz.websocket.dataPromise('file.remove', param);\n            },\n\n            /**\n             * Provides a continuous stream of events (and optionally data) from a local file.\n             *\n             * @param {string} path Relative or absolute directory path. Must reside in qz data directory or a white-listed location.\n             * @param {Object} [params] Object containing file access parameters\n             *  @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections\n             *  @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user\n             *  @param {Object} [params.listener] If defined, file data will be returned on events\n             *   @param {number} [params.listener.bytes=-1] Number of bytes to return or -1 for all\n             *   @param {number} [params.listener.lines=-1] Number of lines to return or -1 for all\n             *   @param {boolean} [params.listener.reverse] Controls whether data should be returned from the bottom of the file.  Default value is true for line mode and false for byte mode.\n             *   @param {string|Array<string>} [params.include] File patterns to match.  Blank values will be ignored.\n             *   @param {string|Array<string>} [params.exclude] File patterns to exclude.  Blank values will be ignored.  Takes priority over <code>params.include</code>.\n             *   @param {boolean} [params.ignoreCase=true] Whether <code>params.include</code> or <code>params.exclude</code> are case-sensitive.\n             * @returns {Promise<null|Error>}\n             * @since 2.1.0\n             *\n             * @see qz.file.setFileCallbacks\n             *\n             * @memberof qz.file\n             */\n            startListening: function(path, params) {\n                if (params && typeof params.include !== 'undefined' && !Array.isArray(params.include)) {\n                    params.include = [params.include];\n                }\n                if (params && typeof params.exclude !== 'undefined' && !Array.isArray(params.exclude)) {\n                    params.exclude = [params.exclude];\n                }\n                var param = _qz.tools.extend({ path: path }, params);\n                return _qz.websocket.dataPromise('file.startListening', param);\n            },\n\n            /**\n             * Closes listeners with the provided settings. Omitting the path parameter will result in all listeners closing.\n             *\n             * @param {string} [path] Previously opened directory path of listener to close, or omit to close all.\n             * @param {Object} [params] Object containing file access parameters\n             *  @param {boolean} [params.sandbox=true] If relative location from root is only available to the certificate's connection, otherwise all connections\n             *  @param {boolean} [params.shared=true] If relative location from root is accessible to all users on the system, otherwise just the current user\n             * @returns {Promise<null|Error>}\n             *\n             * @memberof qz.file\n             */\n            stopListening: function(path, params) {\n                var param = _qz.tools.extend({ path: path }, params);\n                return _qz.websocket.dataPromise('file.stopListening', param);\n            },\n\n            /**\n             * List of functions called for any response from a file listener.\n             *  For ERROR types event data will contain, <code>{string} message</code>.\n             *  For ACTION types event data will contain, <code>{string} file {string} eventType {string} [data]</code>.\n             *\n             * @param {Function|Array<Function>} calls Single or array of <code>Function({Object} eventData)</code> calls.\n             * @since 2.1.0\n             *\n             * @memberof qz.file\n             */\n            setFileCallbacks: function(calls) {\n                _qz.file.fileCallbacks = calls;\n            }\n        },\n\n        /**\n         * Calls related to networking information\n         * @namespace qz.networking\n         * @since 2.1.0\n         */\n        networking: {\n            /**\n             * @param {string} [hostname] Hostname to try to connect to when determining network interfaces, defaults to \"google.com\"\n             * @param {number} [port] Port to use with custom hostname, defaults to 443\n             * @returns {Promise<Object|Error>} Connected system's network information.\n             *\n             * @memberof qz.networking\n             * @since 2.1.0\n             */\n            device: function(hostname, port) {\n                // Wrap 2.0\n                if (_qz.tools.isVersion(2, 0)) {\n                    return _qz.compatible.networking(hostname, port, null, null, function(data) {\n                        return { ip: data.ipAddress, mac: data.macAddress };\n                    });\n                }\n                // Use 2.1\n                return _qz.websocket.dataPromise('networking.device', {\n                    hostname: hostname,\n                    port: port\n                });\n            },\n\n            /**\n             * Get computer hostname\n             *\n             * @param {string} [hostname] DEPRECATED Hostname to try to connect to when determining network interfaces, defaults to \"google.com\"\n             * @param {number} [port] DEPRECATED Port to use with custom hostname, defaults to 443\n             * @returns {Promise<string|Error>} Connected system's hostname.\n             *\n             * @memberof qz.networking\n             * @since 2.2.2\n             */\n            hostname: function(hostname, port) {\n                // Wrap < 2.2.2\n                if (_qz.tools.versionCompare(2, 2, 2) < 0) {\n                    return _qz.tools.promise(function(resolve, reject) {\n                        _qz.websocket.dataPromise('networking.device', { hostname: hostname, port: port }).then(function(device) {\n                            console.log(device);\n                            resolve(device.hostname);\n                        });\n                    });\n                } else {\n                    return _qz.websocket.dataPromise('networking.hostname');\n                }\n            },\n\n            /**\n             * @param {string} [hostname] Hostname to try to connect to when determining network interfaces, defaults to \"google.com\"\n             * @param {number} [port] Port to use with custom hostname, defaults to 443\n             * @returns {Promise<Array<Object>|Error>} Connected system's network information.\n             *\n             * @memberof qz.networking\n             * @since 2.1.0\n             */\n            devices: function(hostname, port) {\n                // Wrap 2.0\n                if (_qz.tools.isVersion(2, 0)) {\n                    return _qz.compatible.networking(hostname, port, null, null, function(data) {\n                        return [{ ip: data.ipAddress, mac: data.macAddress }];\n                    });\n                }\n                // Use 2.1\n                return _qz.websocket.dataPromise('networking.devices', {\n                    hostname: hostname,\n                    port: port\n                });\n            }\n        },\n\n\n        /**\n         * Calls related to signing connection requests.\n         * @namespace qz.security\n         */\n        security: {\n            /**\n             * Set promise resolver for calls to acquire the site's certificate.\n             *\n             * @param {Function|AsyncFunction|Promise<string>} promiseHandler Either a function that will be used as a promise resolver (of format <code>Function({function} resolve, {function}reject)</code>),\n             *     an async function, or a promise. Any of which should return the public certificate via their respective <code>resolve</code> call.\n             * @param {Object} [options] Configuration options for the certificate resolver\n             *  @param {boolean} [options.rejectOnFailure=[false]] Overrides default behavior to call resolve with a blank certificate on failure.\n             * @memberof qz.security\n             */\n            setCertificatePromise: function(promiseHandler, options) {\n                _qz.security.certHandler = promiseHandler;\n                _qz.security.rejectOnCertFailure = !!(options && options.rejectOnFailure);\n            },\n\n            /**\n             * Set promise factory for calls to sign API calls.\n             *\n             * @param {Function|AsyncFunction} promiseFactory Either a function that accepts a string parameter of the data to be signed\n             *     and returns a function to be used as a promise resolver (of format <code>Function({function} resolve, {function}reject)</code>),\n             *     or an async function that can take a string parameter of the data to be signed. Either of which should return the signed contents of\n             *     the passed string parameter via their respective <code>resolve</code> call.\n             *\n             * @example\n             *  qz.security.setSignaturePromise(function(dataToSign) {\n             *    return function(resolve, reject) {\n             *      $.ajax(\"/signing-url?data=\" + dataToSign).then(resolve, reject);\n             *    }\n             *  })\n             *\n             * @memberof qz.security\n             */\n            setSignaturePromise: function(promiseFactory) {\n                _qz.security.signatureFactory = promiseFactory;\n            },\n\n            /**\n             * Set which signing algorithm QZ will check signatures against.\n             *\n             * @param {string} algorithm The algorithm used in signing. Valid values: <code>[SHA1 | SHA256 | SHA512]</code>\n             * @since 2.1.0\n             *\n             * @memberof qz.security\n             */\n            setSignatureAlgorithm: function(algorithm) {\n                //warn for incompatibilities if known\n                if (!_qz.compatible.algorithm()) {\n                    return;\n                }\n\n                if ([\"SHA1\", \"SHA256\", \"SHA512\"].indexOf(algorithm.toUpperCase()) < 0) {\n                    _qz.log.error(\"Signing algorithm '\" + algorithm + \"' is not supported.\");\n                } else {\n                    _qz.security.signAlgorithm = algorithm;\n                }\n            },\n\n            /**\n             * Get the signing algorithm QZ will be checking signatures against.\n             *\n             * @returns {string} The algorithm used in signing.\n             * @since 2.1.0\n             *\n             * @memberof qz.security\n             */\n            getSignatureAlgorithm: function() {\n                return _qz.security.signAlgorithm;\n            }\n        },\n\n        /**\n         * Calls related to compatibility adjustments\n         * @namespace qz.api\n         */\n        api: {\n            /**\n             * Show or hide QZ api debugging statements in the browser console.\n             *\n             * @param {boolean} show Whether the debugging logs for QZ should be shown. Hidden by default.\n             * @returns {boolean} Value of debugging flag\n             * @memberof qz.api\n             */\n            showDebug: function(show) {\n                return (_qz.DEBUG = show);\n            },\n\n            /**\n             * Get version of connected QZ Tray application.\n             *\n             * @returns {Promise<string|Error>} Version number of QZ Tray.\n             *\n             * @memberof qz.api\n             */\n            getVersion: function() {\n                return _qz.websocket.dataPromise('getVersion');\n            },\n\n            /**\n             * Checks for the specified version of connected QZ Tray application.\n             *\n             * @param {string|number} [major] Major version to check\n             * @param {string|number} [minor] Minor version to check\n             * @param {string|number} [patch] Patch version to check\n             *\n             * @memberof qz.api\n             */\n            isVersion: _qz.tools.isVersion,\n\n            /**\n             * Checks if the connected QZ Tray application is greater than the specified version.\n             *\n             * @param {string|number} major Major version to check\n             * @param {string|number} [minor] Minor version to check\n             * @param {string|number} [patch] Patch version to check\n             * @param {string|number} [build] Build version to check\n             * @returns {boolean} True if connected version is greater than the version specified.\n             *\n             * @memberof qz.api\n             * @since 2.1.0-4\n             */\n            isVersionGreater: function(major, minor, patch, build) {\n                return _qz.tools.versionCompare(major, minor, patch, build) > 0;\n            },\n\n            /**\n             * Checks if the connected QZ Tray application is less than the specified version.\n             *\n             * @param {string|number} major Major version to check\n             * @param {string|number} [minor] Minor version to check\n             * @param {string|number} [patch] Patch version to check\n             * @param {string|number} [build] Build version to check\n             * @returns {boolean} True if connected version is less than the version specified.\n             *\n             * @memberof qz.api\n             * @since 2.1.0-4\n             */\n            isVersionLess: function(major, minor, patch, build) {\n                return _qz.tools.versionCompare(major, minor, patch, build) < 0;\n            },\n\n            /**\n             * Change the promise library used by QZ API.\n             * Should be called before any initialization to avoid possible errors.\n             *\n             * @param {Function} promiser <code>Function({function} resolver)</code> called to create new promises.\n             *\n             * @memberof qz.api\n             */\n            setPromiseType: function(promiser) {\n                _qz.tools.promise = promiser;\n            },\n\n            /**\n             * Change the SHA-256 hashing function used by QZ API.\n             * Should be called before any initialization to avoid possible errors.\n             *\n             * @param {Function} hasher <code>Function({function} message)</code> called to create hash of passed string.\n             *\n             * @memberof qz.api\n             */\n            setSha256Type: function(hasher) {\n                _qz.tools.hash = hasher;\n            },\n\n            /**\n             * Change the WebSocket handler.\n             * Should be called before any initialization to avoid possible errors.\n             *\n             * @param {Function} ws <code>Function({function} WebSocket)</code> called to override the internal WebSocket handler.\n             *\n             * @memberof qz.api\n             */\n            setWebSocketType: function(ws) {\n                _qz.tools.ws = ws;\n            }\n        },\n\n        /**\n         * Version of this JavaScript library\n         *\n         * @constant {string}\n         *\n         * @memberof qz\n         */\n        version: _qz.VERSION\n    };\n\n    return qz;\n})();\n\n\n(function() {\n    if (typeof define === 'function' && define.amd) {\n        define(qz);\n    } else if (typeof exports === 'object') {\n        module.exports = qz;\n    } else {\n        window.qz = qz;\n    }\n})();\n"]}