diff --git a/dist/edfdecoder.cjs.js b/dist/edfdecoder.cjs.js index db0b350..e095498 100644 --- a/dist/edfdecoder.cjs.js +++ b/dist/edfdecoder.cjs.js @@ -1440,8 +1440,16 @@ var EdfDecoder = function () { offset += 8; var date = recordingStartDate.split("."); + // y2k + var year = 1900; + if (date[2] >= 85) { + year = 1900 + Number(date[2]); + } else { + year = 2000 + Number(date[2]); + } var time = recordingStartTime.split("."); - header.recordingDate = new Date(date[2], date[1], date[0], time[0], time[1], time[2], 0); + + header.recordingDate = new Date(year, Number(date[1] - 1), date[0], time[0], time[1], time[2], 0); // 8 ascii : number of bytes in header record header.nbBytesHeaderRecord = parseInt(codecutils.CodecUtils.getString8FromBuffer(this._inputBuffer, 8, offset).trim()); diff --git a/dist/edfdecoder.cjs.js.map b/dist/edfdecoder.cjs.js.map index 1eb0ad0..a06ce92 100644 --- a/dist/edfdecoder.cjs.js.map +++ b/dist/edfdecoder.cjs.js.map @@ -1 +1 @@ -{"version":3,"file":"edfdecoder.cjs.js","sources":["../node_modules/codecutils/dist/codecutils.umd.js","../src/Edf.js","../src/EdfDecoder.js"],"sourcesContent":["(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n\ttypeof define === 'function' && define.amd ? define(['exports'], factory) :\n\t(factory((global.codecutils = {})));\n}(this, (function (exports) { 'use strict';\n\n\tfunction createCommonjsModule(fn, module) {\n\t\treturn module = { exports: {} }, fn(module, module.exports), module.exports;\n\t}\n\n\tvar traverse_1 = createCommonjsModule(function (module) {\n\tvar traverse = module.exports = function (obj) {\n\t return new Traverse(obj);\n\t};\n\n\tfunction Traverse (obj) {\n\t this.value = obj;\n\t}\n\n\tTraverse.prototype.get = function (ps) {\n\t var node = this.value;\n\t for (var i = 0; i < ps.length; i ++) {\n\t var key = ps[i];\n\t if (!node || !hasOwnProperty.call(node, key)) {\n\t node = undefined;\n\t break;\n\t }\n\t node = node[key];\n\t }\n\t return node;\n\t};\n\n\tTraverse.prototype.has = function (ps) {\n\t var node = this.value;\n\t for (var i = 0; i < ps.length; i ++) {\n\t var key = ps[i];\n\t if (!node || !hasOwnProperty.call(node, key)) {\n\t return false;\n\t }\n\t node = node[key];\n\t }\n\t return true;\n\t};\n\n\tTraverse.prototype.set = function (ps, value) {\n\t var node = this.value;\n\t for (var i = 0; i < ps.length - 1; i ++) {\n\t var key = ps[i];\n\t if (!hasOwnProperty.call(node, key)) node[key] = {};\n\t node = node[key];\n\t }\n\t node[ps[i]] = value;\n\t return value;\n\t};\n\n\tTraverse.prototype.map = function (cb) {\n\t return walk(this.value, cb, true);\n\t};\n\n\tTraverse.prototype.forEach = function (cb) {\n\t this.value = walk(this.value, cb, false);\n\t return this.value;\n\t};\n\n\tTraverse.prototype.reduce = function (cb, init) {\n\t var skip = arguments.length === 1;\n\t var acc = skip ? this.value : init;\n\t this.forEach(function (x) {\n\t if (!this.isRoot || !skip) {\n\t acc = cb.call(this, acc, x);\n\t }\n\t });\n\t return acc;\n\t};\n\n\tTraverse.prototype.paths = function () {\n\t var acc = [];\n\t this.forEach(function (x) {\n\t acc.push(this.path); \n\t });\n\t return acc;\n\t};\n\n\tTraverse.prototype.nodes = function () {\n\t var acc = [];\n\t this.forEach(function (x) {\n\t acc.push(this.node);\n\t });\n\t return acc;\n\t};\n\n\tTraverse.prototype.clone = function () {\n\t var parents = [], nodes = [];\n\t \n\t return (function clone (src) {\n\t for (var i = 0; i < parents.length; i++) {\n\t if (parents[i] === src) {\n\t return nodes[i];\n\t }\n\t }\n\t \n\t if (typeof src === 'object' && src !== null) {\n\t var dst = copy(src);\n\t \n\t parents.push(src);\n\t nodes.push(dst);\n\t \n\t forEach(objectKeys(src), function (key) {\n\t dst[key] = clone(src[key]);\n\t });\n\t \n\t parents.pop();\n\t nodes.pop();\n\t return dst;\n\t }\n\t else {\n\t return src;\n\t }\n\t })(this.value);\n\t};\n\n\tfunction walk (root, cb, immutable) {\n\t var path = [];\n\t var parents = [];\n\t var alive = true;\n\t \n\t return (function walker (node_) {\n\t var node = immutable ? copy(node_) : node_;\n\t var modifiers = {};\n\t \n\t var keepGoing = true;\n\t \n\t var state = {\n\t node : node,\n\t node_ : node_,\n\t path : [].concat(path),\n\t parent : parents[parents.length - 1],\n\t parents : parents,\n\t key : path.slice(-1)[0],\n\t isRoot : path.length === 0,\n\t level : path.length,\n\t circular : null,\n\t update : function (x, stopHere) {\n\t if (!state.isRoot) {\n\t state.parent.node[state.key] = x;\n\t }\n\t state.node = x;\n\t if (stopHere) keepGoing = false;\n\t },\n\t 'delete' : function (stopHere) {\n\t delete state.parent.node[state.key];\n\t if (stopHere) keepGoing = false;\n\t },\n\t remove : function (stopHere) {\n\t if (isArray(state.parent.node)) {\n\t state.parent.node.splice(state.key, 1);\n\t }\n\t else {\n\t delete state.parent.node[state.key];\n\t }\n\t if (stopHere) keepGoing = false;\n\t },\n\t keys : null,\n\t before : function (f) { modifiers.before = f; },\n\t after : function (f) { modifiers.after = f; },\n\t pre : function (f) { modifiers.pre = f; },\n\t post : function (f) { modifiers.post = f; },\n\t stop : function () { alive = false; },\n\t block : function () { keepGoing = false; }\n\t };\n\t \n\t if (!alive) return state;\n\t \n\t function updateState() {\n\t if (typeof state.node === 'object' && state.node !== null) {\n\t if (!state.keys || state.node_ !== state.node) {\n\t state.keys = objectKeys(state.node);\n\t }\n\t \n\t state.isLeaf = state.keys.length == 0;\n\t \n\t for (var i = 0; i < parents.length; i++) {\n\t if (parents[i].node_ === node_) {\n\t state.circular = parents[i];\n\t break;\n\t }\n\t }\n\t }\n\t else {\n\t state.isLeaf = true;\n\t state.keys = null;\n\t }\n\t \n\t state.notLeaf = !state.isLeaf;\n\t state.notRoot = !state.isRoot;\n\t }\n\t \n\t updateState();\n\t \n\t // use return values to update if defined\n\t var ret = cb.call(state, state.node);\n\t if (ret !== undefined && state.update) state.update(ret);\n\t \n\t if (modifiers.before) modifiers.before.call(state, state.node);\n\t \n\t if (!keepGoing) return state;\n\t \n\t if (typeof state.node == 'object'\n\t && state.node !== null && !state.circular) {\n\t parents.push(state);\n\t \n\t updateState();\n\t \n\t forEach(state.keys, function (key, i) {\n\t path.push(key);\n\t \n\t if (modifiers.pre) modifiers.pre.call(state, state.node[key], key);\n\t \n\t var child = walker(state.node[key]);\n\t if (immutable && hasOwnProperty.call(state.node, key)) {\n\t state.node[key] = child.node;\n\t }\n\t \n\t child.isLast = i == state.keys.length - 1;\n\t child.isFirst = i == 0;\n\t \n\t if (modifiers.post) modifiers.post.call(state, child);\n\t \n\t path.pop();\n\t });\n\t parents.pop();\n\t }\n\t \n\t if (modifiers.after) modifiers.after.call(state, state.node);\n\t \n\t return state;\n\t })(root).node;\n\t}\n\n\tfunction copy (src) {\n\t if (typeof src === 'object' && src !== null) {\n\t var dst;\n\t \n\t if (isArray(src)) {\n\t dst = [];\n\t }\n\t else if (isDate(src)) {\n\t dst = new Date(src.getTime ? src.getTime() : src);\n\t }\n\t else if (isRegExp(src)) {\n\t dst = new RegExp(src);\n\t }\n\t else if (isError(src)) {\n\t dst = { message: src.message };\n\t }\n\t else if (isBoolean(src)) {\n\t dst = new Boolean(src);\n\t }\n\t else if (isNumber(src)) {\n\t dst = new Number(src);\n\t }\n\t else if (isString(src)) {\n\t dst = new String(src);\n\t }\n\t else if (Object.create && Object.getPrototypeOf) {\n\t dst = Object.create(Object.getPrototypeOf(src));\n\t }\n\t else if (src.constructor === Object) {\n\t dst = {};\n\t }\n\t else {\n\t var proto =\n\t (src.constructor && src.constructor.prototype)\n\t || src.__proto__\n\t || {}\n\t ;\n\t var T = function () {};\n\t T.prototype = proto;\n\t dst = new T;\n\t }\n\t \n\t forEach(objectKeys(src), function (key) {\n\t dst[key] = src[key];\n\t });\n\t return dst;\n\t }\n\t else return src;\n\t}\n\n\tvar objectKeys = Object.keys || function keys (obj) {\n\t var res = [];\n\t for (var key in obj) res.push(key);\n\t return res;\n\t};\n\n\tfunction toS (obj) { return Object.prototype.toString.call(obj) }\n\tfunction isDate (obj) { return toS(obj) === '[object Date]' }\n\tfunction isRegExp (obj) { return toS(obj) === '[object RegExp]' }\n\tfunction isError (obj) { return toS(obj) === '[object Error]' }\n\tfunction isBoolean (obj) { return toS(obj) === '[object Boolean]' }\n\tfunction isNumber (obj) { return toS(obj) === '[object Number]' }\n\tfunction isString (obj) { return toS(obj) === '[object String]' }\n\n\tvar isArray = Array.isArray || function isArray (xs) {\n\t return Object.prototype.toString.call(xs) === '[object Array]';\n\t};\n\n\tvar forEach = function (xs, fn) {\n\t if (xs.forEach) return xs.forEach(fn)\n\t else for (var i = 0; i < xs.length; i++) {\n\t fn(xs[i], i, xs);\n\t }\n\t};\n\n\tforEach(objectKeys(Traverse.prototype), function (key) {\n\t traverse[key] = function (obj) {\n\t var args = [].slice.call(arguments, 1);\n\t var t = new Traverse(obj);\n\t return t[key].apply(t, args);\n\t };\n\t});\n\n\tvar hasOwnProperty = Object.hasOwnProperty || function (obj, key) {\n\t return key in obj;\n\t};\n\t});\n\n\tvar classCallCheck = function (instance, Constructor) {\n\t if (!(instance instanceof Constructor)) {\n\t throw new TypeError(\"Cannot call a class as a function\");\n\t }\n\t};\n\n\tvar createClass = function () {\n\t function defineProperties(target, props) {\n\t for (var i = 0; i < props.length; i++) {\n\t var descriptor = props[i];\n\t descriptor.enumerable = descriptor.enumerable || false;\n\t descriptor.configurable = true;\n\t if (\"value\" in descriptor) descriptor.writable = true;\n\t Object.defineProperty(target, descriptor.key, descriptor);\n\t }\n\t }\n\n\t return function (Constructor, protoProps, staticProps) {\n\t if (protoProps) defineProperties(Constructor.prototype, protoProps);\n\t if (staticProps) defineProperties(Constructor, staticProps);\n\t return Constructor;\n\t };\n\t}();\n\n\t/**\n\t* The CodecUtils class gather some static methods that can be useful while\n\t* encodeing/decoding data.\n\t* CodecUtils does not have a constructor, don't try to instanciate it.\n\t*/\n\n\tvar CodecUtils = function () {\n\t function CodecUtils() {\n\t classCallCheck(this, CodecUtils);\n\t }\n\n\t createClass(CodecUtils, null, [{\n\t key: \"isPlatformLittleEndian\",\n\n\n\t /**\n\t * Get whether or not the platform is using little endian.\n\t * @return {Boolen } true if the platform is little endian, false if big endian\n\t */\n\t value: function isPlatformLittleEndian() {\n\t var a = new Uint32Array([0x12345678]);\n\t var b = new Uint8Array(a.buffer, a.byteOffset, a.byteLength);\n\t return b[0] != 0x12;\n\t }\n\n\t /**\n\t * convert an ArrayBuffer into a unicode string (2 bytes for each char)\n\t * Note: this method was kindly borrowed from Google Closure Compiler:\n\t * https://github.com/google/closure-library/blob/master/closure/goog/crypt/crypt.js\n\t * @param {ArrayBuffer} buf - input ArrayBuffer\n\t * @return {String} a string compatible with Unicode characters\n\t */\n\n\t }, {\n\t key: \"arrayBufferToUnicode\",\n\t value: function arrayBufferToUnicode(buff) {\n\t var buffUint8 = new Uint8Array(buff);\n\t var out = [],\n\t pos = 0,\n\t c = 0;\n\n\t while (pos < buffUint8.length) {\n\t var c1 = buffUint8[pos++];\n\t if (c1 < 128) {\n\t if (c1 < 32 && c1 != 10 && c1 != 13 && c1 != 9 || c1 == 127) {\n\t console.warn(\"Invalid string: non-printable characters\");\n\t return null;\n\t }\n\t out[c++] = String.fromCharCode(c1);\n\t } else if (c1 > 191 && c1 < 224) {\n\t var c2 = buffUint8[pos++];\n\t out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);\n\t } else if (c1 > 239 && c1 < 365) {\n\t // Surrogate Pair\n\t var c2 = buffUint8[pos++];\n\t var c3 = buffUint8[pos++];\n\t var c4 = buffUint8[pos++];\n\t var u = ((c1 & 7) << 18 | (c2 & 63) << 12 | (c3 & 63) << 6 | c4 & 63) - 0x10000;\n\t out[c++] = String.fromCharCode(0xD800 + (u >> 10));\n\t out[c++] = String.fromCharCode(0xDC00 + (u & 1023));\n\t } else {\n\t var c2 = buffUint8[pos++];\n\t var c3 = buffUint8[pos++];\n\t var code = (c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63;\n\t if (code === 0xFFFD) {\n\t console.warn(\"Invalid string: a REPLACEMENT CHARACTER was spotted\");\n\t return null;\n\t }\n\t out[c++] = String.fromCharCode(code);\n\t }\n\t }\n\t return out.join('');\n\t }\n\t }, {\n\t key: \"unicodeToArrayBuffer\",\n\n\n\t /**\n\t * convert a unicode string into an ArrayBuffer\n\t * Note that the str is a regular string but it will be encoded with\n\t * 2 bytes per char instead of 1 ( ASCII uses 1 byte/char ).\n\t * Note: this method was kindly borrowed from Google Closure Compiler:\n\t * https://github.com/google/closure-library/blob/master/closure/goog/crypt/crypt.js\n\t * @param {String} str - string to encode\n\t * @return {ArrayBuffer} the output ArrayBuffer\n\t */\n\t value: function unicodeToArrayBuffer(str) {\n\t var out = [],\n\t p = 0;\n\t for (var i = 0; i < str.length; i++) {\n\t var c = str.charCodeAt(i);\n\t if (c < 128) {\n\t out[p++] = c;\n\t } else if (c < 2048) {\n\t out[p++] = c >> 6 | 192;\n\t out[p++] = c & 63 | 128;\n\t } else if ((c & 0xFC00) == 0xD800 && i + 1 < str.length && (str.charCodeAt(i + 1) & 0xFC00) == 0xDC00) {\n\t // Surrogate Pair\n\t c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);\n\t out[p++] = c >> 18 | 240;\n\t out[p++] = c >> 12 & 63 | 128;\n\t out[p++] = c >> 6 & 63 | 128;\n\t out[p++] = c & 63 | 128;\n\t } else {\n\t out[p++] = c >> 12 | 224;\n\t out[p++] = c >> 6 & 63 | 128;\n\t out[p++] = c & 63 | 128;\n\t }\n\t }\n\n\t // make a buffer out of the array\n\t return new Uint8Array(out).buffer;\n\t }\n\t }, {\n\t key: \"arrayBufferToString8\",\n\n\n\t /**\n\t * Convert an ArrayBuffer into a ASCII string (1 byte for each char)\n\t * @param {ArrayBuffer} buf - buffer to convert into ASCII string\n\t * @return {String} the output string\n\t */\n\t value: function arrayBufferToString8(buf) {\n\t return String.fromCharCode.apply(null, new Uint8Array(buf));\n\t }\n\n\t /**\n\t * Convert a ASCII string into an ArrayBuffer.\n\t * Note that the str is a regular string, it will be encoded with 1 byte per char\n\t * @param {String} str - string to encode\n\t * @return {ArrayBuffer}\n\t */\n\n\t }, {\n\t key: \"string8ToArrayBuffer\",\n\t value: function string8ToArrayBuffer(str) {\n\t var buf = new ArrayBuffer(str.length);\n\t var bufView = new Uint8Array(buf);\n\t for (var i = 0; i < str.length; i++) {\n\t bufView[i] = str.charCodeAt(i);\n\t }\n\t return buf;\n\t }\n\n\t /**\n\t * Write a ASCII string into a buffer\n\t * @param {String} str - a string that contains only ASCII characters\n\t * @param {ArrayBuffer} buffer - the buffer where to write the string\n\t * @param {Number} byteOffset - the offset to apply, in number of bytes\n\t */\n\n\t }, {\n\t key: \"setString8InBuffer\",\n\t value: function setString8InBuffer(str, buffer) {\n\t var byteOffset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n\n\t if (byteOffset < 0) {\n\t console.warn(\"The byte offset cannot be negative.\");\n\t return;\n\t }\n\n\t if (!buffer || !(buffer instanceof ArrayBuffer)) {\n\t console.warn(\"The buffer must be a valid ArrayBuffer.\");\n\t return;\n\t }\n\n\t if (str.length + byteOffset > buffer.byteLength) {\n\t console.warn(\"The string is too long to be writen in this buffer.\");\n\t return;\n\t }\n\n\t var bufView = new Uint8Array(buffer);\n\n\t for (var i = 0; i < str.length; i++) {\n\t bufView[i + byteOffset] = str.charCodeAt(i);\n\t }\n\t }\n\n\t /**\n\t * Extract an ASCII string from an ArrayBuffer\n\t * @param {ArrayBuffer} buffer - the buffer\n\t * @param {Number} strLength - number of chars in the string we want\n\t * @param {Number} byteOffset - the offset in number of bytes\n\t * @return {String} the string, or null in case of error\n\t */\n\n\t }, {\n\t key: \"getString8FromBuffer\",\n\t value: function getString8FromBuffer(buffer, strLength) {\n\t var byteOffset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n\n\t if (byteOffset < 0) {\n\t console.warn(\"The byte offset cannot be negative.\");\n\t return null;\n\t }\n\n\t if (!buffer || !(buffer instanceof ArrayBuffer)) {\n\t console.warn(\"The buffer must be a valid ArrayBuffer.\");\n\t return null;\n\t }\n\n\t if (strLength + byteOffset > buffer.byteLength) {\n\t console.warn(\"The string is too long to be writen in this buffer.\");\n\t return null;\n\t }\n\n\t return String.fromCharCode.apply(null, new Uint8Array(buffer, byteOffset, strLength));\n\t }\n\n\t /**\n\t * Serializes a JS object into an ArrayBuffer.\n\t * This is using a unicode JSON intermediate step.\n\t * @param {Object} obj - an object that does not have cyclic structure\n\t * @return {ArrayBuffer} the serialized output\n\t */\n\n\t }, {\n\t key: \"objectToArrayBuffer\",\n\t value: function objectToArrayBuffer(obj) {\n\t var buff = null;\n\t var objCleanClone = CodecUtils.makeSerializeFriendly(obj);\n\n\t try {\n\t var strObj = JSON.stringify(objCleanClone);\n\t buff = CodecUtils.unicodeToArrayBuffer(strObj);\n\t } catch (e) {\n\t console.warn(e);\n\t }\n\n\t return buff;\n\t }\n\n\t /**\n\t * Convert an ArrayBuffer into a JS Object. This uses an intermediate unicode JSON string.\n\t * Of course, this buffer has to come from a serialized object.\n\t * @param {ArrayBuffer} buff - the ArrayBuffer that hides some object\n\t * @return {Object} the deserialized object\n\t */\n\n\t }, {\n\t key: \"ArrayBufferToObject\",\n\t value: function ArrayBufferToObject(buff) {\n\t var obj = null;\n\n\t try {\n\t var strObj = CodecUtils.arrayBufferToUnicode(buff);\n\t obj = JSON.parse(strObj);\n\t } catch (e) {\n\t console.warn(e);\n\t }\n\n\t return obj;\n\t }\n\n\t /**\n\t * Get if wether of not the arg is a typed array\n\t * @param {Object} obj - possibly a typed array, or maybe not\n\t * @return {Boolean} true if obj is a typed array\n\t */\n\n\t }, {\n\t key: \"isTypedArray\",\n\t value: function isTypedArray(obj) {\n\t return obj instanceof Int8Array || obj instanceof Uint8Array || obj instanceof Uint8ClampedArray || obj instanceof Int16Array || obj instanceof Uint16Array || obj instanceof Int32Array || obj instanceof Uint32Array || obj instanceof Float32Array || obj instanceof Float64Array;\n\t }\n\n\t /**\n\t * Merge some ArrayBuffes in a single one\n\t * @param {Array} arrayOfBuffers - some ArrayBuffers\n\t * @return {ArrayBuffer} the larger merged buffer\n\t */\n\n\t }, {\n\t key: \"mergeBuffers\",\n\t value: function mergeBuffers(arrayOfBuffers) {\n\t var totalByteSize = 0;\n\n\t for (var i = 0; i < arrayOfBuffers.length; i++) {\n\t totalByteSize += arrayOfBuffers[i].byteLength;\n\t }\n\n\t var concatArray = new Uint8Array(totalByteSize);\n\n\t var offset = 0;\n\t for (var i = 0; i < arrayOfBuffers.length; i++) {\n\t concatArray.set(new Uint8Array(arrayOfBuffers[i]), offset);\n\t offset += arrayOfBuffers[i].byteLength;\n\t }\n\n\t return concatArray.buffer;\n\t }\n\n\t /**\n\t * In a browser, the global object is `window` while in Node, it's `GLOBAL`.\n\t * This method return the one that is relevant to the execution context.\n\t * @return {Object} the global object\n\t */\n\n\t }, {\n\t key: \"getGlobalObject\",\n\t value: function getGlobalObject() {\n\t var constructorHost = null;\n\n\t try {\n\t constructorHost = window; // in a web browser\n\t } catch (e) {\n\t try {\n\t constructorHost = GLOBAL; // in node\n\t } catch (e) {\n\t console.warn(\"You are not in a Javascript environment?? Weird.\");\n\t return null;\n\t }\n\t }\n\t return constructorHost;\n\t }\n\n\t /**\n\t * Extract a typed array from an arbitrary buffer, with an arbitrary offset\n\t * @param {ArrayBuffer} buffer - the buffer from which we extract data\n\t * @param {Number} byteOffset - offset from the begining of buffer\n\t * @param {Function} arrayType - function object, actually the constructor of the output array\n\t * @param {Number} numberOfElements - nb of elem we want to fetch from the buffer\n\t * @return {TypedArray} output of type given by arg arrayType - this is a copy, not a view\n\t */\n\n\t }, {\n\t key: \"extractTypedArray\",\n\t value: function extractTypedArray(buffer, byteOffset, arrayType, numberOfElements) {\n\t if (!buffer) {\n\t console.warn(\"Input Buffer is null.\");\n\t return null;\n\t }\n\n\t if (!(buffer instanceof ArrayBuffer)) {\n\t console.warn(\"Buffer must be of type ArrayBuffer\");\n\t return null;\n\t }\n\n\t if (numberOfElements <= 0) {\n\t console.warn(\"The number of elements to fetch must be greater than 0\");\n\t return null;\n\t }\n\n\t if (byteOffset < 0) {\n\t console.warn(\"The byte offset must be possitive or 0\");\n\t return null;\n\t }\n\n\t if (byteOffset >= buffer.byteLength) {\n\t console.warn(\"The offset cannot be larger than the size of the buffer.\");\n\t return null;\n\t }\n\n\t if (arrayType instanceof Function && !(\"BYTES_PER_ELEMENT\" in arrayType)) {\n\t console.warn(\"ArrayType must be a typed array constructor function.\");\n\t return null;\n\t }\n\n\t if (arrayType.BYTES_PER_ELEMENT * numberOfElements + byteOffset > buffer.byteLength) {\n\t console.warn(\"The requested number of elements is too large for this buffer\");\n\t return;\n\t }\n\n\t var slicedBuff = buffer.slice(byteOffset, byteOffset + numberOfElements * arrayType.BYTES_PER_ELEMENT);\n\t return new arrayType(slicedBuff);\n\t }\n\n\t /**\n\t * Get some info about the given TypedArray\n\t * @param {TypedArray} typedArray - one of the typed array\n\t * @return {Object} in form of {type: String, signed: Boolean, bytesPerElements: Number, byteLength: Number, length: Number}\n\t */\n\n\t }, {\n\t key: \"getTypedArrayInfo\",\n\t value: function getTypedArrayInfo(typedArray) {\n\t var type = null;\n\t var signed = false;\n\n\t if (typedArray instanceof Int8Array) {\n\t type = \"int\";\n\t signed = false;\n\t } else if (typedArray instanceof Uint8Array) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Uint8ClampedArray) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Int16Array) {\n\t type = \"int\";\n\t signed = false;\n\t } else if (typedArray instanceof Uint16Array) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Int32Array) {\n\t type = \"int\";\n\t signed = false;\n\t } else if (typedArray instanceof Uint32Array) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Float32Array) {\n\t type = \"float\";\n\t signed = false;\n\t } else if (typedArray instanceof Float64Array) {\n\t type = \"float\";\n\t signed = false;\n\t }\n\n\t return {\n\t type: type,\n\t signed: signed,\n\t bytesPerElements: typedArray.BYTES_PER_ELEMENT,\n\t byteLength: typedArray.byteLength,\n\t length: typedArray.length\n\t };\n\t }\n\n\t /**\n\t * Counts the number of typed array obj has as attributes\n\t * @param {Object} obj - an Object\n\t * @return {Number} the number of typed array\n\t */\n\n\t }, {\n\t key: \"howManyTypedArrayAttributes\",\n\t value: function howManyTypedArrayAttributes(obj) {\n\t var typArrCounter = 0;\n\t traverse_1(obj).forEach(function (x) {\n\t typArrCounter += CodecUtils.isTypedArray(x);\n\t });\n\t return typArrCounter;\n\t }\n\n\t /**\n\t * Check if the given object contains any circular reference.\n\t * (Circular ref are non serilizable easily, we want to spot them)\n\t * @param {Object} obj - An object to check\n\t * @return {Boolean} true if obj contains circular refm false if not\n\t */\n\n\t }, {\n\t key: \"hasCircularReference\",\n\t value: function hasCircularReference(obj) {\n\t var hasCircular = false;\n\t traverse_1(obj).forEach(function (x) {\n\t if (this.circular) {\n\t hasCircular = true;\n\t }\n\t });\n\t return hasCircular;\n\t }\n\n\t /**\n\t * Remove circular dependencies from an object and return a circularRef-free version\n\t * of the object (does not change the original obj), of null if no circular ref was found\n\t * @param {Object} obj - An object to check\n\t * @return {Object} a circular-ref free object copy if any was found, or null if no circ was found\n\t */\n\n\t }, {\n\t key: \"removeCircularReference\",\n\t value: function removeCircularReference(obj) {\n\t var hasCircular = false;\n\t var noCircRefObj = traverse_1(obj).map(function (x) {\n\t if (this.circular) {\n\t this.remove();\n\t hasCircular = true;\n\t }\n\t });\n\t return hasCircular ? noCircRefObj : null;\n\t }\n\n\t /**\n\t * Clone the object and replace the typed array attributes by regular Arrays.\n\t * @param {Object} obj - an object to alter\n\t * @return {Object} the clone if ant typed array were changed, or null if was obj didnt contain any typed array.\n\t */\n\n\t }, {\n\t key: \"replaceTypedArrayAttributesByArrays\",\n\t value: function replaceTypedArrayAttributesByArrays(obj) {\n\t var hasTypedArray = false;\n\n\t var noTypedArrClone = traverse_1(obj).map(function (x) {\n\t if (CodecUtils.isTypedArray(x)) {\n\t // here, we cannot call .length directly because traverse.map already serialized\n\t // typed arrays into regular objects\n\t var origSize = Object.keys(x).length;\n\t var untypedArray = new Array(origSize);\n\n\t for (var i = 0; i < origSize; i++) {\n\t untypedArray[i] = x[i];\n\t }\n\t this.update(untypedArray);\n\t hasTypedArray = true;\n\t }\n\t });\n\t return hasTypedArray ? noTypedArrClone : null;\n\t }\n\n\t /**\n\t * Creates a clone, does not alter the original object.\n\t * Remove circular dependencies and replace typed arrays by regular arrays.\n\t * Both will make the serialization possible and more reliable.\n\t * @param {Object} obj - the object to make serialization friendly\n\t * @return {Object} a clean clone, or null if nothing was done\n\t */\n\n\t }, {\n\t key: \"makeSerializeFriendly\",\n\t value: function makeSerializeFriendly(obj) {\n\t var newObj = obj;\n\t var noCircular = CodecUtils.removeCircularReference(newObj);\n\n\t if (noCircular) newObj = noCircular;\n\n\t var noTypedArr = CodecUtils.replaceTypedArrayAttributesByArrays(newObj);\n\n\t if (noTypedArr) newObj = noTypedArr;\n\n\t return newObj;\n\t }\n\n\t /**\n\t * Check if a string is valid or not. A string is considered as invalid if it has\n\t * unicode \"REPLACEMENT CHARACTER\" or non-printable ASCII characters.\n\t * @param {String} str - string to test\n\t * @param {Boolean} forceAll - test the whole string instead of a sample of 1000 charaters\n\t * @return {Boolean} true is the string is valid, false if invalid.\n\t */\n\n\t }, {\n\t key: \"isValidString\",\n\t value: function isValidString(str) {\n\t var forceAll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n\t var strLen = str.length;\n\t var nbSamples = forceAll ? strLen : Math.min(1000, strLen); // a sample of 1000 should be enough\n\t var flagChar = 0xFFFD;\n\t var redFlags = 0;\n\t for (var i = 0; i < nbSamples; i++) {\n\t var code = str.charCodeAt(Math.floor(Math.random() * nbSamples));\n\t if (code === flagChar || code < 32 && code != 10 && code != 13 && code != 9 || code == 127) {\n\t redFlags++;\n\t }\n\t }\n\t return !(redFlags > 0);\n\t }\n\t }]);\n\t return CodecUtils;\n\t}(); /* END of class CodecUtils */\n\n\texports.CodecUtils = CodecUtils;\n\n\tObject.defineProperty(exports, '__esModule', { value: true });\n\n})));\n//# sourceMappingURL=codecutils.umd.js.map\n"," /*\n* Author Jonathan Lurie - http://me.jonahanlurie.fr\n* License MIT\n* Link https://github.com/jonathanlurie/edfdecoder\n* Lab MCIN - http://mcin.ca/ - Montreal Neurological Institute\n*/\n\n\n/**\n* An instance of Edf is usually given as output of an EdfDecoder. It provides an\n* interface with a lot of helper function to query information that were extracted\n* from en *.edf* file, such as header information, getting a signal at a given record\n* or concatenating records of a given signal.\n*\n* Keep in mind that the number of records in an edf file can be decoded by arbitrary\n* measures, or it can be 1 second per records, etc.\n*\n*/\nclass Edf {\n constructor( header, rawSignals, physicalSignals ){\n this._header = header;\n this._physicalSignals = physicalSignals;\n this._rawSignals = rawSignals;\n }\n\n /**\n * Get the duration in second of a single record\n * @return {Number} duration\n */\n getRecordDuration(){\n return this._header.durationDataRecordsSec;\n }\n\n\n /**\n * Get the ID of the recording\n * @return {String} the ID\n */\n getRecordingID(){\n return this._header.localRecordingId;\n }\n\n\n /**\n * get the number of records per signal.\n * Note: most of the time, records from the same signal are contiguous in time.\n * @return {Number} the number of records\n */\n getNumberOfRecords(){\n return this._header.nbDataRecords;\n }\n\n\n /**\n * get the number of signals.\n * Note: a signal can have more than one record\n * @return {Number} the number of signals\n */\n getNumberOfSignals(){\n return this._header.nbSignals;\n }\n\n\n /**\n * Get the patien ID\n * @return {String} ID\n */\n getPatientID(){\n return this._header.patientId;\n }\n\n\n /**\n * Get the date and the time at which the recording has started\n * @return {Date} the date\n */\n getRecordingStartDate(){\n return this._header.recordingDate;\n }\n\n\n /**\n * Get the value of the reserved field, global (from header) or specific to a signal.\n * Notice: reserved are rarely used.\n * @param {Number} index - if not specified, get the header's reserved field. If [0, nbSignals[ get the reserved field specific for the given signal\n * @return {String} the data of the reserved field.\n */\n getReservedField( index=-1 ){\n if( index === -1 ){\n return this._header.reserved;\n }else{\n if( index >= 0 && index < this._header.signalInfo.length ){\n return this._header.signalInfo[index].reserved;\n }\n }\n\n return null;\n }\n\n\n /**\n * Get the digital maximum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalDigitalMax( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].digitalMaximum;\n }\n\n\n /**\n * Get the digital minimum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalDigitalMin( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].digitalMinimum;\n }\n\n\n /**\n * Get the physical minimum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalPhysicalMin( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].physicalMinimum;\n }\n\n\n /**\n * Get the physical maximum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalPhysicalMax( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].physicalMaximum;\n }\n\n\n /**\n * Get the label for a given signal index\n * @param {Number} index - index of the signal\n * @return {String}\n */\n getSignalLabel( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].label;\n }\n\n\n /**\n * Get the number of samples per record for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalNumberOfSamplesPerRecord( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].nbOfSamples;\n }\n\n\n /**\n * Get the unit (dimension label) used for a given signal index.\n * E.g. this can be 'uV' when the signal is an EEG\n * @param {Number} index - index of the signal\n * @return {String} the unit name\n */\n getSignalPhysicalUnit( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].physicalDimension;\n }\n\n\n /**\n * Get the unit prefiltering info for a given signal index.\n * @param {Number} index - index of the signal\n * @return {String} the prefiltering info\n */\n getSignalPrefiltering( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].prefiltering;\n }\n\n\n /**\n * Get the transducer type info for a given signal index.\n * @param {Number} index - index of the signal\n * @return {String} the transducer type info\n */\n getSignalTransducerType( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].transducerType;\n }\n\n\n /**\n * Get the sampling frequency in Hz of a given signal\n * @param {Number} index - index of the signal\n * @return {Number} frequency in Hz\n */\n getSignalSamplingFrequency( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].nbOfSamples / this._header.durationDataRecordsSec;\n }\n\n /**\n * Get the physical (scaled) signal at a given index and record\n * @param {Number} index - index of the signal\n * @param {Number} record - index of the record\n * @return {Float32Array} the physical signal in Float32\n */\n getPhysicalSignal( index, record ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n if( record<0 && record>=this._physicalSignals[index].length ){\n console.warn(\"Record index is out of range\");\n return null;\n }\n\n return this._physicalSignals[index][record];\n }\n\n\n /**\n * Get the raw (digital) signal at a given index and record\n * @param {Number} index - index of the signal\n * @param {Number} record - index of the record\n * @return {Int16Array} the physical signal in Int16\n */\n getRawSignal( index, record ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n if( record<0 && record>=this._rawSignals[index].length ){\n console.warn(\"Record index is out of range\");\n return null;\n }\n\n return this._rawSignals[index][record];\n }\n\n\n\n /**\n * Get concatenated contiguous records of a given signal, the index of the\n * first record and the number of records to concat.\n * Notice: this allocates a new buffer of an extented size.\n * @param {Number} index - index of the signal\n * @param {Number} recordStart - index of the record to start with\n * @param {Number} howMany - Number of records to concatenate\n * @return {Float32Array} the physical signal in Float32\n */\n getPhysicalSignalConcatRecords(index, recordStart=-1, howMany=-1){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n if( recordStart<0 && recordStart>=this._physicalSignals[index].length ){\n console.warn(\"The index recordStart is out of range\");\n return null;\n }\n\n if(recordStart === -1){\n recordStart = 0;\n }\n\n if(howMany === -1){\n howMany = this._physicalSignals[index].length - recordStart;\n }else{\n // we still want to check if what the user put is not out of bound\n if( recordStart + howMany > this._physicalSignals[index].length ){\n console.warn(\"The number of requested records is too large. Forcing only to available records.\");\n howMany = this._physicalSignals[index].length - recordStart; \n }\n\n }\n\n // index of the last one to consider\n var recordEnd = recordStart + howMany - 1;\n\n if( recordEnd<0 && recordEnd>=this._physicalSignals[index].length ){\n console.warn(\"Too many records to concatenate, this goes out of range.\");\n return null;\n }\n\n var totalSize = 0;\n for(var i=recordStart; i 191 && c1 < 224) {\n\t var c2 = buffUint8[pos++];\n\t out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);\n\t } else if (c1 > 239 && c1 < 365) {\n\t // Surrogate Pair\n\t var c2 = buffUint8[pos++];\n\t var c3 = buffUint8[pos++];\n\t var c4 = buffUint8[pos++];\n\t var u = ((c1 & 7) << 18 | (c2 & 63) << 12 | (c3 & 63) << 6 | c4 & 63) - 0x10000;\n\t out[c++] = String.fromCharCode(0xD800 + (u >> 10));\n\t out[c++] = String.fromCharCode(0xDC00 + (u & 1023));\n\t } else {\n\t var c2 = buffUint8[pos++];\n\t var c3 = buffUint8[pos++];\n\t var code = (c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63;\n\t if (code === 0xFFFD) {\n\t console.warn(\"Invalid string: a REPLACEMENT CHARACTER was spotted\");\n\t return null;\n\t }\n\t out[c++] = String.fromCharCode(code);\n\t }\n\t }\n\t return out.join('');\n\t }\n\t }, {\n\t key: \"unicodeToArrayBuffer\",\n\n\n\t /**\n\t * convert a unicode string into an ArrayBuffer\n\t * Note that the str is a regular string but it will be encoded with\n\t * 2 bytes per char instead of 1 ( ASCII uses 1 byte/char ).\n\t * Note: this method was kindly borrowed from Google Closure Compiler:\n\t * https://github.com/google/closure-library/blob/master/closure/goog/crypt/crypt.js\n\t * @param {String} str - string to encode\n\t * @return {ArrayBuffer} the output ArrayBuffer\n\t */\n\t value: function unicodeToArrayBuffer(str) {\n\t var out = [],\n\t p = 0;\n\t for (var i = 0; i < str.length; i++) {\n\t var c = str.charCodeAt(i);\n\t if (c < 128) {\n\t out[p++] = c;\n\t } else if (c < 2048) {\n\t out[p++] = c >> 6 | 192;\n\t out[p++] = c & 63 | 128;\n\t } else if ((c & 0xFC00) == 0xD800 && i + 1 < str.length && (str.charCodeAt(i + 1) & 0xFC00) == 0xDC00) {\n\t // Surrogate Pair\n\t c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);\n\t out[p++] = c >> 18 | 240;\n\t out[p++] = c >> 12 & 63 | 128;\n\t out[p++] = c >> 6 & 63 | 128;\n\t out[p++] = c & 63 | 128;\n\t } else {\n\t out[p++] = c >> 12 | 224;\n\t out[p++] = c >> 6 & 63 | 128;\n\t out[p++] = c & 63 | 128;\n\t }\n\t }\n\n\t // make a buffer out of the array\n\t return new Uint8Array(out).buffer;\n\t }\n\t }, {\n\t key: \"arrayBufferToString8\",\n\n\n\t /**\n\t * Convert an ArrayBuffer into a ASCII string (1 byte for each char)\n\t * @param {ArrayBuffer} buf - buffer to convert into ASCII string\n\t * @return {String} the output string\n\t */\n\t value: function arrayBufferToString8(buf) {\n\t return String.fromCharCode.apply(null, new Uint8Array(buf));\n\t }\n\n\t /**\n\t * Convert a ASCII string into an ArrayBuffer.\n\t * Note that the str is a regular string, it will be encoded with 1 byte per char\n\t * @param {String} str - string to encode\n\t * @return {ArrayBuffer}\n\t */\n\n\t }, {\n\t key: \"string8ToArrayBuffer\",\n\t value: function string8ToArrayBuffer(str) {\n\t var buf = new ArrayBuffer(str.length);\n\t var bufView = new Uint8Array(buf);\n\t for (var i = 0; i < str.length; i++) {\n\t bufView[i] = str.charCodeAt(i);\n\t }\n\t return buf;\n\t }\n\n\t /**\n\t * Write a ASCII string into a buffer\n\t * @param {String} str - a string that contains only ASCII characters\n\t * @param {ArrayBuffer} buffer - the buffer where to write the string\n\t * @param {Number} byteOffset - the offset to apply, in number of bytes\n\t */\n\n\t }, {\n\t key: \"setString8InBuffer\",\n\t value: function setString8InBuffer(str, buffer) {\n\t var byteOffset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n\n\t if (byteOffset < 0) {\n\t console.warn(\"The byte offset cannot be negative.\");\n\t return;\n\t }\n\n\t if (!buffer || !(buffer instanceof ArrayBuffer)) {\n\t console.warn(\"The buffer must be a valid ArrayBuffer.\");\n\t return;\n\t }\n\n\t if (str.length + byteOffset > buffer.byteLength) {\n\t console.warn(\"The string is too long to be writen in this buffer.\");\n\t return;\n\t }\n\n\t var bufView = new Uint8Array(buffer);\n\n\t for (var i = 0; i < str.length; i++) {\n\t bufView[i + byteOffset] = str.charCodeAt(i);\n\t }\n\t }\n\n\t /**\n\t * Extract an ASCII string from an ArrayBuffer\n\t * @param {ArrayBuffer} buffer - the buffer\n\t * @param {Number} strLength - number of chars in the string we want\n\t * @param {Number} byteOffset - the offset in number of bytes\n\t * @return {String} the string, or null in case of error\n\t */\n\n\t }, {\n\t key: \"getString8FromBuffer\",\n\t value: function getString8FromBuffer(buffer, strLength) {\n\t var byteOffset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n\n\t if (byteOffset < 0) {\n\t console.warn(\"The byte offset cannot be negative.\");\n\t return null;\n\t }\n\n\t if (!buffer || !(buffer instanceof ArrayBuffer)) {\n\t console.warn(\"The buffer must be a valid ArrayBuffer.\");\n\t return null;\n\t }\n\n\t if (strLength + byteOffset > buffer.byteLength) {\n\t console.warn(\"The string is too long to be writen in this buffer.\");\n\t return null;\n\t }\n\n\t return String.fromCharCode.apply(null, new Uint8Array(buffer, byteOffset, strLength));\n\t }\n\n\t /**\n\t * Serializes a JS object into an ArrayBuffer.\n\t * This is using a unicode JSON intermediate step.\n\t * @param {Object} obj - an object that does not have cyclic structure\n\t * @return {ArrayBuffer} the serialized output\n\t */\n\n\t }, {\n\t key: \"objectToArrayBuffer\",\n\t value: function objectToArrayBuffer(obj) {\n\t var buff = null;\n\t var objCleanClone = CodecUtils.makeSerializeFriendly(obj);\n\n\t try {\n\t var strObj = JSON.stringify(objCleanClone);\n\t buff = CodecUtils.unicodeToArrayBuffer(strObj);\n\t } catch (e) {\n\t console.warn(e);\n\t }\n\n\t return buff;\n\t }\n\n\t /**\n\t * Convert an ArrayBuffer into a JS Object. This uses an intermediate unicode JSON string.\n\t * Of course, this buffer has to come from a serialized object.\n\t * @param {ArrayBuffer} buff - the ArrayBuffer that hides some object\n\t * @return {Object} the deserialized object\n\t */\n\n\t }, {\n\t key: \"ArrayBufferToObject\",\n\t value: function ArrayBufferToObject(buff) {\n\t var obj = null;\n\n\t try {\n\t var strObj = CodecUtils.arrayBufferToUnicode(buff);\n\t obj = JSON.parse(strObj);\n\t } catch (e) {\n\t console.warn(e);\n\t }\n\n\t return obj;\n\t }\n\n\t /**\n\t * Get if wether of not the arg is a typed array\n\t * @param {Object} obj - possibly a typed array, or maybe not\n\t * @return {Boolean} true if obj is a typed array\n\t */\n\n\t }, {\n\t key: \"isTypedArray\",\n\t value: function isTypedArray(obj) {\n\t return obj instanceof Int8Array || obj instanceof Uint8Array || obj instanceof Uint8ClampedArray || obj instanceof Int16Array || obj instanceof Uint16Array || obj instanceof Int32Array || obj instanceof Uint32Array || obj instanceof Float32Array || obj instanceof Float64Array;\n\t }\n\n\t /**\n\t * Merge some ArrayBuffes in a single one\n\t * @param {Array} arrayOfBuffers - some ArrayBuffers\n\t * @return {ArrayBuffer} the larger merged buffer\n\t */\n\n\t }, {\n\t key: \"mergeBuffers\",\n\t value: function mergeBuffers(arrayOfBuffers) {\n\t var totalByteSize = 0;\n\n\t for (var i = 0; i < arrayOfBuffers.length; i++) {\n\t totalByteSize += arrayOfBuffers[i].byteLength;\n\t }\n\n\t var concatArray = new Uint8Array(totalByteSize);\n\n\t var offset = 0;\n\t for (var i = 0; i < arrayOfBuffers.length; i++) {\n\t concatArray.set(new Uint8Array(arrayOfBuffers[i]), offset);\n\t offset += arrayOfBuffers[i].byteLength;\n\t }\n\n\t return concatArray.buffer;\n\t }\n\n\t /**\n\t * In a browser, the global object is `window` while in Node, it's `GLOBAL`.\n\t * This method return the one that is relevant to the execution context.\n\t * @return {Object} the global object\n\t */\n\n\t }, {\n\t key: \"getGlobalObject\",\n\t value: function getGlobalObject() {\n\t var constructorHost = null;\n\n\t try {\n\t constructorHost = window; // in a web browser\n\t } catch (e) {\n\t try {\n\t constructorHost = GLOBAL; // in node\n\t } catch (e) {\n\t console.warn(\"You are not in a Javascript environment?? Weird.\");\n\t return null;\n\t }\n\t }\n\t return constructorHost;\n\t }\n\n\t /**\n\t * Extract a typed array from an arbitrary buffer, with an arbitrary offset\n\t * @param {ArrayBuffer} buffer - the buffer from which we extract data\n\t * @param {Number} byteOffset - offset from the begining of buffer\n\t * @param {Function} arrayType - function object, actually the constructor of the output array\n\t * @param {Number} numberOfElements - nb of elem we want to fetch from the buffer\n\t * @return {TypedArray} output of type given by arg arrayType - this is a copy, not a view\n\t */\n\n\t }, {\n\t key: \"extractTypedArray\",\n\t value: function extractTypedArray(buffer, byteOffset, arrayType, numberOfElements) {\n\t if (!buffer) {\n\t console.warn(\"Input Buffer is null.\");\n\t return null;\n\t }\n\n\t if (!(buffer instanceof ArrayBuffer)) {\n\t console.warn(\"Buffer must be of type ArrayBuffer\");\n\t return null;\n\t }\n\n\t if (numberOfElements <= 0) {\n\t console.warn(\"The number of elements to fetch must be greater than 0\");\n\t return null;\n\t }\n\n\t if (byteOffset < 0) {\n\t console.warn(\"The byte offset must be possitive or 0\");\n\t return null;\n\t }\n\n\t if (byteOffset >= buffer.byteLength) {\n\t console.warn(\"The offset cannot be larger than the size of the buffer.\");\n\t return null;\n\t }\n\n\t if (arrayType instanceof Function && !(\"BYTES_PER_ELEMENT\" in arrayType)) {\n\t console.warn(\"ArrayType must be a typed array constructor function.\");\n\t return null;\n\t }\n\n\t if (arrayType.BYTES_PER_ELEMENT * numberOfElements + byteOffset > buffer.byteLength) {\n\t console.warn(\"The requested number of elements is too large for this buffer\");\n\t return;\n\t }\n\n\t var slicedBuff = buffer.slice(byteOffset, byteOffset + numberOfElements * arrayType.BYTES_PER_ELEMENT);\n\t return new arrayType(slicedBuff);\n\t }\n\n\t /**\n\t * Get some info about the given TypedArray\n\t * @param {TypedArray} typedArray - one of the typed array\n\t * @return {Object} in form of {type: String, signed: Boolean, bytesPerElements: Number, byteLength: Number, length: Number}\n\t */\n\n\t }, {\n\t key: \"getTypedArrayInfo\",\n\t value: function getTypedArrayInfo(typedArray) {\n\t var type = null;\n\t var signed = false;\n\n\t if (typedArray instanceof Int8Array) {\n\t type = \"int\";\n\t signed = false;\n\t } else if (typedArray instanceof Uint8Array) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Uint8ClampedArray) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Int16Array) {\n\t type = \"int\";\n\t signed = false;\n\t } else if (typedArray instanceof Uint16Array) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Int32Array) {\n\t type = \"int\";\n\t signed = false;\n\t } else if (typedArray instanceof Uint32Array) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Float32Array) {\n\t type = \"float\";\n\t signed = false;\n\t } else if (typedArray instanceof Float64Array) {\n\t type = \"float\";\n\t signed = false;\n\t }\n\n\t return {\n\t type: type,\n\t signed: signed,\n\t bytesPerElements: typedArray.BYTES_PER_ELEMENT,\n\t byteLength: typedArray.byteLength,\n\t length: typedArray.length\n\t };\n\t }\n\n\t /**\n\t * Counts the number of typed array obj has as attributes\n\t * @param {Object} obj - an Object\n\t * @return {Number} the number of typed array\n\t */\n\n\t }, {\n\t key: \"howManyTypedArrayAttributes\",\n\t value: function howManyTypedArrayAttributes(obj) {\n\t var typArrCounter = 0;\n\t traverse_1(obj).forEach(function (x) {\n\t typArrCounter += CodecUtils.isTypedArray(x);\n\t });\n\t return typArrCounter;\n\t }\n\n\t /**\n\t * Check if the given object contains any circular reference.\n\t * (Circular ref are non serilizable easily, we want to spot them)\n\t * @param {Object} obj - An object to check\n\t * @return {Boolean} true if obj contains circular refm false if not\n\t */\n\n\t }, {\n\t key: \"hasCircularReference\",\n\t value: function hasCircularReference(obj) {\n\t var hasCircular = false;\n\t traverse_1(obj).forEach(function (x) {\n\t if (this.circular) {\n\t hasCircular = true;\n\t }\n\t });\n\t return hasCircular;\n\t }\n\n\t /**\n\t * Remove circular dependencies from an object and return a circularRef-free version\n\t * of the object (does not change the original obj), of null if no circular ref was found\n\t * @param {Object} obj - An object to check\n\t * @return {Object} a circular-ref free object copy if any was found, or null if no circ was found\n\t */\n\n\t }, {\n\t key: \"removeCircularReference\",\n\t value: function removeCircularReference(obj) {\n\t var hasCircular = false;\n\t var noCircRefObj = traverse_1(obj).map(function (x) {\n\t if (this.circular) {\n\t this.remove();\n\t hasCircular = true;\n\t }\n\t });\n\t return hasCircular ? noCircRefObj : null;\n\t }\n\n\t /**\n\t * Clone the object and replace the typed array attributes by regular Arrays.\n\t * @param {Object} obj - an object to alter\n\t * @return {Object} the clone if ant typed array were changed, or null if was obj didnt contain any typed array.\n\t */\n\n\t }, {\n\t key: \"replaceTypedArrayAttributesByArrays\",\n\t value: function replaceTypedArrayAttributesByArrays(obj) {\n\t var hasTypedArray = false;\n\n\t var noTypedArrClone = traverse_1(obj).map(function (x) {\n\t if (CodecUtils.isTypedArray(x)) {\n\t // here, we cannot call .length directly because traverse.map already serialized\n\t // typed arrays into regular objects\n\t var origSize = Object.keys(x).length;\n\t var untypedArray = new Array(origSize);\n\n\t for (var i = 0; i < origSize; i++) {\n\t untypedArray[i] = x[i];\n\t }\n\t this.update(untypedArray);\n\t hasTypedArray = true;\n\t }\n\t });\n\t return hasTypedArray ? noTypedArrClone : null;\n\t }\n\n\t /**\n\t * Creates a clone, does not alter the original object.\n\t * Remove circular dependencies and replace typed arrays by regular arrays.\n\t * Both will make the serialization possible and more reliable.\n\t * @param {Object} obj - the object to make serialization friendly\n\t * @return {Object} a clean clone, or null if nothing was done\n\t */\n\n\t }, {\n\t key: \"makeSerializeFriendly\",\n\t value: function makeSerializeFriendly(obj) {\n\t var newObj = obj;\n\t var noCircular = CodecUtils.removeCircularReference(newObj);\n\n\t if (noCircular) newObj = noCircular;\n\n\t var noTypedArr = CodecUtils.replaceTypedArrayAttributesByArrays(newObj);\n\n\t if (noTypedArr) newObj = noTypedArr;\n\n\t return newObj;\n\t }\n\n\t /**\n\t * Check if a string is valid or not. A string is considered as invalid if it has\n\t * unicode \"REPLACEMENT CHARACTER\" or non-printable ASCII characters.\n\t * @param {String} str - string to test\n\t * @param {Boolean} forceAll - test the whole string instead of a sample of 1000 charaters\n\t * @return {Boolean} true is the string is valid, false if invalid.\n\t */\n\n\t }, {\n\t key: \"isValidString\",\n\t value: function isValidString(str) {\n\t var forceAll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n\t var strLen = str.length;\n\t var nbSamples = forceAll ? strLen : Math.min(1000, strLen); // a sample of 1000 should be enough\n\t var flagChar = 0xFFFD;\n\t var redFlags = 0;\n\t for (var i = 0; i < nbSamples; i++) {\n\t var code = str.charCodeAt(Math.floor(Math.random() * nbSamples));\n\t if (code === flagChar || code < 32 && code != 10 && code != 13 && code != 9 || code == 127) {\n\t redFlags++;\n\t }\n\t }\n\t return !(redFlags > 0);\n\t }\n\t }]);\n\t return CodecUtils;\n\t}(); /* END of class CodecUtils */\n\n\texports.CodecUtils = CodecUtils;\n\n\tObject.defineProperty(exports, '__esModule', { value: true });\n\n})));\n//# sourceMappingURL=codecutils.umd.js.map\n"," /*\n* Author Jonathan Lurie - http://me.jonahanlurie.fr\n* License MIT\n* Link https://github.com/jonathanlurie/edfdecoder\n* Lab MCIN - http://mcin.ca/ - Montreal Neurological Institute\n*/\n\n\n/**\n* An instance of Edf is usually given as output of an EdfDecoder. It provides an\n* interface with a lot of helper function to query information that were extracted\n* from en *.edf* file, such as header information, getting a signal at a given record\n* or concatenating records of a given signal.\n*\n* Keep in mind that the number of records in an edf file can be decoded by arbitrary\n* measures, or it can be 1 second per records, etc.\n*\n*/\nclass Edf {\n constructor( header, rawSignals, physicalSignals ){\n this._header = header;\n this._physicalSignals = physicalSignals;\n this._rawSignals = rawSignals;\n }\n\n /**\n * Get the duration in second of a single record\n * @return {Number} duration\n */\n getRecordDuration(){\n return this._header.durationDataRecordsSec;\n }\n\n\n /**\n * Get the ID of the recording\n * @return {String} the ID\n */\n getRecordingID(){\n return this._header.localRecordingId;\n }\n\n\n /**\n * get the number of records per signal.\n * Note: most of the time, records from the same signal are contiguous in time.\n * @return {Number} the number of records\n */\n getNumberOfRecords(){\n return this._header.nbDataRecords;\n }\n\n\n /**\n * get the number of signals.\n * Note: a signal can have more than one record\n * @return {Number} the number of signals\n */\n getNumberOfSignals(){\n return this._header.nbSignals;\n }\n\n\n /**\n * Get the patien ID\n * @return {String} ID\n */\n getPatientID(){\n return this._header.patientId;\n }\n\n\n /**\n * Get the date and the time at which the recording has started\n * @return {Date} the date\n */\n getRecordingStartDate(){\n return this._header.recordingDate;\n }\n\n\n /**\n * Get the value of the reserved field, global (from header) or specific to a signal.\n * Notice: reserved are rarely used.\n * @param {Number} index - if not specified, get the header's reserved field. If [0, nbSignals[ get the reserved field specific for the given signal\n * @return {String} the data of the reserved field.\n */\n getReservedField( index=-1 ){\n if( index === -1 ){\n return this._header.reserved;\n }else{\n if( index >= 0 && index < this._header.signalInfo.length ){\n return this._header.signalInfo[index].reserved;\n }\n }\n\n return null;\n }\n\n\n /**\n * Get the digital maximum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalDigitalMax( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].digitalMaximum;\n }\n\n\n /**\n * Get the digital minimum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalDigitalMin( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].digitalMinimum;\n }\n\n\n /**\n * Get the physical minimum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalPhysicalMin( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].physicalMinimum;\n }\n\n\n /**\n * Get the physical maximum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalPhysicalMax( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].physicalMaximum;\n }\n\n\n /**\n * Get the label for a given signal index\n * @param {Number} index - index of the signal\n * @return {String}\n */\n getSignalLabel( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].label;\n }\n\n\n /**\n * Get the number of samples per record for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalNumberOfSamplesPerRecord( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].nbOfSamples;\n }\n\n\n /**\n * Get the unit (dimension label) used for a given signal index.\n * E.g. this can be 'uV' when the signal is an EEG\n * @param {Number} index - index of the signal\n * @return {String} the unit name\n */\n getSignalPhysicalUnit( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].physicalDimension;\n }\n\n\n /**\n * Get the unit prefiltering info for a given signal index.\n * @param {Number} index - index of the signal\n * @return {String} the prefiltering info\n */\n getSignalPrefiltering( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].prefiltering;\n }\n\n\n /**\n * Get the transducer type info for a given signal index.\n * @param {Number} index - index of the signal\n * @return {String} the transducer type info\n */\n getSignalTransducerType( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].transducerType;\n }\n\n\n /**\n * Get the sampling frequency in Hz of a given signal\n * @param {Number} index - index of the signal\n * @return {Number} frequency in Hz\n */\n getSignalSamplingFrequency( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].nbOfSamples / this._header.durationDataRecordsSec;\n }\n\n /**\n * Get the physical (scaled) signal at a given index and record\n * @param {Number} index - index of the signal\n * @param {Number} record - index of the record\n * @return {Float32Array} the physical signal in Float32\n */\n getPhysicalSignal( index, record ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n if( record<0 && record>=this._physicalSignals[index].length ){\n console.warn(\"Record index is out of range\");\n return null;\n }\n\n return this._physicalSignals[index][record];\n }\n\n\n /**\n * Get the raw (digital) signal at a given index and record\n * @param {Number} index - index of the signal\n * @param {Number} record - index of the record\n * @return {Int16Array} the physical signal in Int16\n */\n getRawSignal( index, record ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n if( record<0 && record>=this._rawSignals[index].length ){\n console.warn(\"Record index is out of range\");\n return null;\n }\n\n return this._rawSignals[index][record];\n }\n\n\n\n /**\n * Get concatenated contiguous records of a given signal, the index of the\n * first record and the number of records to concat.\n * Notice: this allocates a new buffer of an extented size.\n * @param {Number} index - index of the signal\n * @param {Number} recordStart - index of the record to start with\n * @param {Number} howMany - Number of records to concatenate\n * @return {Float32Array} the physical signal in Float32\n */\n getPhysicalSignalConcatRecords(index, recordStart=-1, howMany=-1){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n if( recordStart<0 && recordStart>=this._physicalSignals[index].length ){\n console.warn(\"The index recordStart is out of range\");\n return null;\n }\n\n if(recordStart === -1){\n recordStart = 0;\n }\n\n if(howMany === -1){\n howMany = this._physicalSignals[index].length - recordStart;\n }else{\n // we still want to check if what the user put is not out of bound\n if( recordStart + howMany > this._physicalSignals[index].length ){\n console.warn(\"The number of requested records is too large. Forcing only to available records.\");\n howMany = this._physicalSignals[index].length - recordStart; \n }\n\n }\n\n // index of the last one to consider\n var recordEnd = recordStart + howMany - 1;\n\n if( recordEnd<0 && recordEnd>=this._physicalSignals[index].length ){\n console.warn(\"Too many records to concatenate, this goes out of range.\");\n return null;\n }\n\n var totalSize = 0;\n for(var i=recordStart; i= 85) {\n year = 1900 + Number(date[2]);\n } else {\n year = 2000 + Number(date[2]);\n }\n var time = recordingStartTime.split(\".\");\n\n header.recordingDate = new Date( year, Number(date[1] - 1), date[0], time[0], time[1], time[2], 0 );\n\n // 8 ascii : number of bytes in header record\n header.nbBytesHeaderRecord = parseInt( codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 8, offset).trim() );\n offset += 8;\n\n // 44 ascii : reserved\n header.reserved = codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 44, offset);\n offset += 44;\n\n // 8 ascii : number of data records (-1 if unknown)\n header.nbDataRecords = parseInt( codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 8, offset).trim() );\n offset += 8;\n\n // 8 ascii : duration of a data record, in seconds\n header.durationDataRecordsSec = parseInt( codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 8, offset).trim() );\n offset += 8;\n\n // 4 ascii : number of signals (ns) in data record\n header.nbSignals = parseInt( codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 4, offset).trim() );\n offset += 4;\n\n // the following fields occurs ns time in a row each\n var that = this;\n function getAllSections( sizeOfEachThing ){\n var allThings = [];\n for(var i=0; i= 0 && index < this._header.signalInfo.length ){ - return this._header.signalInfo[index].reserved; - } + }, { + key: "getNumberOfRecords", + value: function getNumberOfRecords() { + return this._header.nbDataRecords; } - return null; - } + /** + * get the number of signals. + * Note: a signal can have more than one record + * @return {Number} the number of signals + */ - - /** - * Get the digital maximum for a given signal index - * @param {Number} index - index of the signal - * @return {Number} - */ - getSignalDigitalMax( index ){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); - return null; + }, { + key: "getNumberOfSignals", + value: function getNumberOfSignals() { + return this._header.nbSignals; } - return this._header.signalInfo[index].digitalMaximum; - } - + /** + * Get the patien ID + * @return {String} ID + */ - /** - * Get the digital minimum for a given signal index - * @param {Number} index - index of the signal - * @return {Number} - */ - getSignalDigitalMin( index ){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); - return null; + }, { + key: "getPatientID", + value: function getPatientID() { + return this._header.patientId; } - return this._header.signalInfo[index].digitalMinimum; - } - + /** + * Get the date and the time at which the recording has started + * @return {Date} the date + */ - /** - * Get the physical minimum for a given signal index - * @param {Number} index - index of the signal - * @return {Number} - */ - getSignalPhysicalMin( index ){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); - return null; + }, { + key: "getRecordingStartDate", + value: function getRecordingStartDate() { + return this._header.recordingDate; } - return this._header.signalInfo[index].physicalMinimum; - } - + /** + * Get the value of the reserved field, global (from header) or specific to a signal. + * Notice: reserved are rarely used. + * @param {Number} index - if not specified, get the header's reserved field. If [0, nbSignals[ get the reserved field specific for the given signal + * @return {String} the data of the reserved field. + */ + + }, { + key: "getReservedField", + value: function getReservedField() { + var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : -1; + + if (index === -1) { + return this._header.reserved; + } else { + if (index >= 0 && index < this._header.signalInfo.length) { + return this._header.signalInfo[index].reserved; + } + } - /** - * Get the physical maximum for a given signal index - * @param {Number} index - index of the signal - * @return {Number} - */ - getSignalPhysicalMax( index ){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); return null; } - return this._header.signalInfo[index].physicalMaximum; - } - + /** + * Get the digital maximum for a given signal index + * @param {Number} index - index of the signal + * @return {Number} + */ + + }, { + key: "getSignalDigitalMax", + value: function getSignalDigitalMax(index) { + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; + } - /** - * Get the label for a given signal index - * @param {Number} index - index of the signal - * @return {String} - */ - getSignalLabel( index ){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); - return null; + return this._header.signalInfo[index].digitalMaximum; } - return this._header.signalInfo[index].label; - } - + /** + * Get the digital minimum for a given signal index + * @param {Number} index - index of the signal + * @return {Number} + */ + + }, { + key: "getSignalDigitalMin", + value: function getSignalDigitalMin(index) { + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; + } - /** - * Get the number of samples per record for a given signal index - * @param {Number} index - index of the signal - * @return {Number} - */ - getSignalNumberOfSamplesPerRecord( index ){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); - return null; + return this._header.signalInfo[index].digitalMinimum; } - return this._header.signalInfo[index].nbOfSamples; - } - + /** + * Get the physical minimum for a given signal index + * @param {Number} index - index of the signal + * @return {Number} + */ + + }, { + key: "getSignalPhysicalMin", + value: function getSignalPhysicalMin(index) { + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; + } - /** - * Get the unit (dimension label) used for a given signal index. - * E.g. this can be 'uV' when the signal is an EEG - * @param {Number} index - index of the signal - * @return {String} the unit name - */ - getSignalPhysicalUnit( index ){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); - return null; + return this._header.signalInfo[index].physicalMinimum; } - return this._header.signalInfo[index].physicalDimension; - } - + /** + * Get the physical maximum for a given signal index + * @param {Number} index - index of the signal + * @return {Number} + */ + + }, { + key: "getSignalPhysicalMax", + value: function getSignalPhysicalMax(index) { + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; + } - /** - * Get the unit prefiltering info for a given signal index. - * @param {Number} index - index of the signal - * @return {String} the prefiltering info - */ - getSignalPrefiltering( index ){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); - return null; + return this._header.signalInfo[index].physicalMaximum; } - return this._header.signalInfo[index].prefiltering; - } - + /** + * Get the label for a given signal index + * @param {Number} index - index of the signal + * @return {String} + */ + + }, { + key: "getSignalLabel", + value: function getSignalLabel(index) { + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; + } - /** - * Get the transducer type info for a given signal index. - * @param {Number} index - index of the signal - * @return {String} the transducer type info - */ - getSignalTransducerType( index ){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); - return null; + return this._header.signalInfo[index].label; } - return this._header.signalInfo[index].transducerType; - } - + /** + * Get the number of samples per record for a given signal index + * @param {Number} index - index of the signal + * @return {Number} + */ + + }, { + key: "getSignalNumberOfSamplesPerRecord", + value: function getSignalNumberOfSamplesPerRecord(index) { + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; + } - /** - * Get the sampling frequency in Hz of a given signal - * @param {Number} index - index of the signal - * @return {Number} frequency in Hz - */ - getSignalSamplingFrequency( index ){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); - return null; + return this._header.signalInfo[index].nbOfSamples; } - return this._header.signalInfo[index].nbOfSamples / this._header.durationDataRecordsSec; - } + /** + * Get the unit (dimension label) used for a given signal index. + * E.g. this can be 'uV' when the signal is an EEG + * @param {Number} index - index of the signal + * @return {String} the unit name + */ + + }, { + key: "getSignalPhysicalUnit", + value: function getSignalPhysicalUnit(index) { + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; + } - /** - * Get the physical (scaled) signal at a given index and record - * @param {Number} index - index of the signal - * @param {Number} record - index of the record - * @return {Float32Array} the physical signal in Float32 - */ - getPhysicalSignal( index, record ){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); - return null; + return this._header.signalInfo[index].physicalDimension; } - if( record<0 && record>=this._physicalSignals[index].length ){ - console.warn("Record index is out of range"); - return null; - } + /** + * Get the unit prefiltering info for a given signal index. + * @param {Number} index - index of the signal + * @return {String} the prefiltering info + */ + + }, { + key: "getSignalPrefiltering", + value: function getSignalPrefiltering(index) { + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; + } - return this._physicalSignals[index][record]; - } + return this._header.signalInfo[index].prefiltering; + } + /** + * Get the transducer type info for a given signal index. + * @param {Number} index - index of the signal + * @return {String} the transducer type info + */ + + }, { + key: "getSignalTransducerType", + value: function getSignalTransducerType(index) { + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; + } - /** - * Get the raw (digital) signal at a given index and record - * @param {Number} index - index of the signal - * @param {Number} record - index of the record - * @return {Int16Array} the physical signal in Int16 - */ - getRawSignal( index, record ){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); - return null; + return this._header.signalInfo[index].transducerType; } - if( record<0 && record>=this._rawSignals[index].length ){ - console.warn("Record index is out of range"); - return null; - } + /** + * Get the sampling frequency in Hz of a given signal + * @param {Number} index - index of the signal + * @return {Number} frequency in Hz + */ + + }, { + key: "getSignalSamplingFrequency", + value: function getSignalSamplingFrequency(index) { + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; + } - return this._rawSignals[index][record]; - } + return this._header.signalInfo[index].nbOfSamples / this._header.durationDataRecordsSec; + } + /** + * Get the physical (scaled) signal at a given index and record + * @param {Number} index - index of the signal + * @param {Number} record - index of the record + * @return {Float32Array} the physical signal in Float32 + */ + + }, { + key: "getPhysicalSignal", + value: function getPhysicalSignal(index, record) { + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; + } + if (record < 0 && record >= this._physicalSignals[index].length) { + console.warn("Record index is out of range"); + return null; + } - /** - * Get concatenated contiguous records of a given signal, the index of the - * first record and the number of records to concat. - * Notice: this allocates a new buffer of an extented size. - * @param {Number} index - index of the signal - * @param {Number} recordStart - index of the record to start with - * @param {Number} howMany - Number of records to concatenate - * @return {Float32Array} the physical signal in Float32 - */ - getPhysicalSignalConcatRecords(index, recordStart=-1, howMany=-1){ - if( index < 0 || index >= this._header.signalInfo.length ){ - console.warn("Signal index is out of range"); - return null; + return this._physicalSignals[index][record]; } - if( recordStart<0 && recordStart>=this._physicalSignals[index].length ){ - console.warn("The index recordStart is out of range"); - return null; - } + /** + * Get the raw (digital) signal at a given index and record + * @param {Number} index - index of the signal + * @param {Number} record - index of the record + * @return {Int16Array} the physical signal in Int16 + */ + + }, { + key: "getRawSignal", + value: function getRawSignal(index, record) { + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; + } + + if (record < 0 && record >= this._rawSignals[index].length) { + console.warn("Record index is out of range"); + return null; + } - if(recordStart === -1){ - recordStart = 0; + return this._rawSignals[index][record]; } - if(howMany === -1){ - howMany = this._physicalSignals[index].length - recordStart; - }else{ - // we still want to check if what the user put is not out of bound - if( recordStart + howMany > this._physicalSignals[index].length ){ - console.warn("The number of requested records is too large. Forcing only to available records."); - howMany = this._physicalSignals[index].length - recordStart; + /** + * Get concatenated contiguous records of a given signal, the index of the + * first record and the number of records to concat. + * Notice: this allocates a new buffer of an extented size. + * @param {Number} index - index of the signal + * @param {Number} recordStart - index of the record to start with + * @param {Number} howMany - Number of records to concatenate + * @return {Float32Array} the physical signal in Float32 + */ + + }, { + key: "getPhysicalSignalConcatRecords", + value: function getPhysicalSignalConcatRecords(index) { + var recordStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1; + var howMany = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : -1; + + if (index < 0 || index >= this._header.signalInfo.length) { + console.warn("Signal index is out of range"); + return null; } - } + if (recordStart < 0 && recordStart >= this._physicalSignals[index].length) { + console.warn("The index recordStart is out of range"); + return null; + } - // index of the last one to consider - var recordEnd = recordStart + howMany - 1; + if (recordStart === -1) { + recordStart = 0; + } - if( recordEnd<0 && recordEnd>=this._physicalSignals[index].length ){ - console.warn("Too many records to concatenate, this goes out of range."); - return null; - } + if (howMany === -1) { + howMany = this._physicalSignals[index].length - recordStart; + } else { + // we still want to check if what the user put is not out of bound + if (recordStart + howMany > this._physicalSignals[index].length) { + console.warn("The number of requested records is too large. Forcing only to available records."); + howMany = this._physicalSignals[index].length - recordStart; + } + } - var totalSize = 0; - for(var i=recordStart; i= this._physicalSignals[index].length) { + console.warn("Too many records to concatenate, this goes out of range."); + return null; + } - for(var i=recordStart; i= 85) { + year = 1900 + Number(date[2]); + } else { + year = 2000 + Number(date[2]); + } + var time = recordingStartTime.split("."); - // 44 ascii : reserved - header.reserved = codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 44, offset); - offset += 44; + header.recordingDate = new Date(year, Number(date[1] - 1), date[0], time[0], time[1], time[2], 0); - // 8 ascii : number of data records (-1 if unknown) - header.nbDataRecords = parseInt( codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 8, offset).trim() ); - offset += 8; + // 8 ascii : number of bytes in header record + header.nbBytesHeaderRecord = parseInt(codecutils.CodecUtils.getString8FromBuffer(this._inputBuffer, 8, offset).trim()); + offset += 8; - // 8 ascii : duration of a data record, in seconds - header.durationDataRecordsSec = parseInt( codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 8, offset).trim() ); - offset += 8; + // 44 ascii : reserved + header.reserved = codecutils.CodecUtils.getString8FromBuffer(this._inputBuffer, 44, offset); + offset += 44; - // 4 ascii : number of signals (ns) in data record - header.nbSignals = parseInt( codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 4, offset).trim() ); - offset += 4; + // 8 ascii : number of data records (-1 if unknown) + header.nbDataRecords = parseInt(codecutils.CodecUtils.getString8FromBuffer(this._inputBuffer, 8, offset).trim()); + offset += 8; - // the following fields occurs ns time in a row each - var that = this; - function getAllSections( sizeOfEachThing ){ - var allThings = []; - for(var i=0; i 191 && c1 < 224) {\n\t var c2 = buffUint8[pos++];\n\t out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);\n\t } else if (c1 > 239 && c1 < 365) {\n\t // Surrogate Pair\n\t var c2 = buffUint8[pos++];\n\t var c3 = buffUint8[pos++];\n\t var c4 = buffUint8[pos++];\n\t var u = ((c1 & 7) << 18 | (c2 & 63) << 12 | (c3 & 63) << 6 | c4 & 63) - 0x10000;\n\t out[c++] = String.fromCharCode(0xD800 + (u >> 10));\n\t out[c++] = String.fromCharCode(0xDC00 + (u & 1023));\n\t } else {\n\t var c2 = buffUint8[pos++];\n\t var c3 = buffUint8[pos++];\n\t var code = (c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63;\n\t if (code === 0xFFFD) {\n\t console.warn(\"Invalid string: a REPLACEMENT CHARACTER was spotted\");\n\t return null;\n\t }\n\t out[c++] = String.fromCharCode(code);\n\t }\n\t }\n\t return out.join('');\n\t }\n\t }, {\n\t key: \"unicodeToArrayBuffer\",\n\n\n\t /**\n\t * convert a unicode string into an ArrayBuffer\n\t * Note that the str is a regular string but it will be encoded with\n\t * 2 bytes per char instead of 1 ( ASCII uses 1 byte/char ).\n\t * Note: this method was kindly borrowed from Google Closure Compiler:\n\t * https://github.com/google/closure-library/blob/master/closure/goog/crypt/crypt.js\n\t * @param {String} str - string to encode\n\t * @return {ArrayBuffer} the output ArrayBuffer\n\t */\n\t value: function unicodeToArrayBuffer(str) {\n\t var out = [],\n\t p = 0;\n\t for (var i = 0; i < str.length; i++) {\n\t var c = str.charCodeAt(i);\n\t if (c < 128) {\n\t out[p++] = c;\n\t } else if (c < 2048) {\n\t out[p++] = c >> 6 | 192;\n\t out[p++] = c & 63 | 128;\n\t } else if ((c & 0xFC00) == 0xD800 && i + 1 < str.length && (str.charCodeAt(i + 1) & 0xFC00) == 0xDC00) {\n\t // Surrogate Pair\n\t c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);\n\t out[p++] = c >> 18 | 240;\n\t out[p++] = c >> 12 & 63 | 128;\n\t out[p++] = c >> 6 & 63 | 128;\n\t out[p++] = c & 63 | 128;\n\t } else {\n\t out[p++] = c >> 12 | 224;\n\t out[p++] = c >> 6 & 63 | 128;\n\t out[p++] = c & 63 | 128;\n\t }\n\t }\n\n\t // make a buffer out of the array\n\t return new Uint8Array(out).buffer;\n\t }\n\t }, {\n\t key: \"arrayBufferToString8\",\n\n\n\t /**\n\t * Convert an ArrayBuffer into a ASCII string (1 byte for each char)\n\t * @param {ArrayBuffer} buf - buffer to convert into ASCII string\n\t * @return {String} the output string\n\t */\n\t value: function arrayBufferToString8(buf) {\n\t return String.fromCharCode.apply(null, new Uint8Array(buf));\n\t }\n\n\t /**\n\t * Convert a ASCII string into an ArrayBuffer.\n\t * Note that the str is a regular string, it will be encoded with 1 byte per char\n\t * @param {String} str - string to encode\n\t * @return {ArrayBuffer}\n\t */\n\n\t }, {\n\t key: \"string8ToArrayBuffer\",\n\t value: function string8ToArrayBuffer(str) {\n\t var buf = new ArrayBuffer(str.length);\n\t var bufView = new Uint8Array(buf);\n\t for (var i = 0; i < str.length; i++) {\n\t bufView[i] = str.charCodeAt(i);\n\t }\n\t return buf;\n\t }\n\n\t /**\n\t * Write a ASCII string into a buffer\n\t * @param {String} str - a string that contains only ASCII characters\n\t * @param {ArrayBuffer} buffer - the buffer where to write the string\n\t * @param {Number} byteOffset - the offset to apply, in number of bytes\n\t */\n\n\t }, {\n\t key: \"setString8InBuffer\",\n\t value: function setString8InBuffer(str, buffer) {\n\t var byteOffset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n\n\t if (byteOffset < 0) {\n\t console.warn(\"The byte offset cannot be negative.\");\n\t return;\n\t }\n\n\t if (!buffer || !(buffer instanceof ArrayBuffer)) {\n\t console.warn(\"The buffer must be a valid ArrayBuffer.\");\n\t return;\n\t }\n\n\t if (str.length + byteOffset > buffer.byteLength) {\n\t console.warn(\"The string is too long to be writen in this buffer.\");\n\t return;\n\t }\n\n\t var bufView = new Uint8Array(buffer);\n\n\t for (var i = 0; i < str.length; i++) {\n\t bufView[i + byteOffset] = str.charCodeAt(i);\n\t }\n\t }\n\n\t /**\n\t * Extract an ASCII string from an ArrayBuffer\n\t * @param {ArrayBuffer} buffer - the buffer\n\t * @param {Number} strLength - number of chars in the string we want\n\t * @param {Number} byteOffset - the offset in number of bytes\n\t * @return {String} the string, or null in case of error\n\t */\n\n\t }, {\n\t key: \"getString8FromBuffer\",\n\t value: function getString8FromBuffer(buffer, strLength) {\n\t var byteOffset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n\n\t if (byteOffset < 0) {\n\t console.warn(\"The byte offset cannot be negative.\");\n\t return null;\n\t }\n\n\t if (!buffer || !(buffer instanceof ArrayBuffer)) {\n\t console.warn(\"The buffer must be a valid ArrayBuffer.\");\n\t return null;\n\t }\n\n\t if (strLength + byteOffset > buffer.byteLength) {\n\t console.warn(\"The string is too long to be writen in this buffer.\");\n\t return null;\n\t }\n\n\t return String.fromCharCode.apply(null, new Uint8Array(buffer, byteOffset, strLength));\n\t }\n\n\t /**\n\t * Serializes a JS object into an ArrayBuffer.\n\t * This is using a unicode JSON intermediate step.\n\t * @param {Object} obj - an object that does not have cyclic structure\n\t * @return {ArrayBuffer} the serialized output\n\t */\n\n\t }, {\n\t key: \"objectToArrayBuffer\",\n\t value: function objectToArrayBuffer(obj) {\n\t var buff = null;\n\t var objCleanClone = CodecUtils.makeSerializeFriendly(obj);\n\n\t try {\n\t var strObj = JSON.stringify(objCleanClone);\n\t buff = CodecUtils.unicodeToArrayBuffer(strObj);\n\t } catch (e) {\n\t console.warn(e);\n\t }\n\n\t return buff;\n\t }\n\n\t /**\n\t * Convert an ArrayBuffer into a JS Object. This uses an intermediate unicode JSON string.\n\t * Of course, this buffer has to come from a serialized object.\n\t * @param {ArrayBuffer} buff - the ArrayBuffer that hides some object\n\t * @return {Object} the deserialized object\n\t */\n\n\t }, {\n\t key: \"ArrayBufferToObject\",\n\t value: function ArrayBufferToObject(buff) {\n\t var obj = null;\n\n\t try {\n\t var strObj = CodecUtils.arrayBufferToUnicode(buff);\n\t obj = JSON.parse(strObj);\n\t } catch (e) {\n\t console.warn(e);\n\t }\n\n\t return obj;\n\t }\n\n\t /**\n\t * Get if wether of not the arg is a typed array\n\t * @param {Object} obj - possibly a typed array, or maybe not\n\t * @return {Boolean} true if obj is a typed array\n\t */\n\n\t }, {\n\t key: \"isTypedArray\",\n\t value: function isTypedArray(obj) {\n\t return obj instanceof Int8Array || obj instanceof Uint8Array || obj instanceof Uint8ClampedArray || obj instanceof Int16Array || obj instanceof Uint16Array || obj instanceof Int32Array || obj instanceof Uint32Array || obj instanceof Float32Array || obj instanceof Float64Array;\n\t }\n\n\t /**\n\t * Merge some ArrayBuffes in a single one\n\t * @param {Array} arrayOfBuffers - some ArrayBuffers\n\t * @return {ArrayBuffer} the larger merged buffer\n\t */\n\n\t }, {\n\t key: \"mergeBuffers\",\n\t value: function mergeBuffers(arrayOfBuffers) {\n\t var totalByteSize = 0;\n\n\t for (var i = 0; i < arrayOfBuffers.length; i++) {\n\t totalByteSize += arrayOfBuffers[i].byteLength;\n\t }\n\n\t var concatArray = new Uint8Array(totalByteSize);\n\n\t var offset = 0;\n\t for (var i = 0; i < arrayOfBuffers.length; i++) {\n\t concatArray.set(new Uint8Array(arrayOfBuffers[i]), offset);\n\t offset += arrayOfBuffers[i].byteLength;\n\t }\n\n\t return concatArray.buffer;\n\t }\n\n\t /**\n\t * In a browser, the global object is `window` while in Node, it's `GLOBAL`.\n\t * This method return the one that is relevant to the execution context.\n\t * @return {Object} the global object\n\t */\n\n\t }, {\n\t key: \"getGlobalObject\",\n\t value: function getGlobalObject() {\n\t var constructorHost = null;\n\n\t try {\n\t constructorHost = window; // in a web browser\n\t } catch (e) {\n\t try {\n\t constructorHost = GLOBAL; // in node\n\t } catch (e) {\n\t console.warn(\"You are not in a Javascript environment?? Weird.\");\n\t return null;\n\t }\n\t }\n\t return constructorHost;\n\t }\n\n\t /**\n\t * Extract a typed array from an arbitrary buffer, with an arbitrary offset\n\t * @param {ArrayBuffer} buffer - the buffer from which we extract data\n\t * @param {Number} byteOffset - offset from the begining of buffer\n\t * @param {Function} arrayType - function object, actually the constructor of the output array\n\t * @param {Number} numberOfElements - nb of elem we want to fetch from the buffer\n\t * @return {TypedArray} output of type given by arg arrayType - this is a copy, not a view\n\t */\n\n\t }, {\n\t key: \"extractTypedArray\",\n\t value: function extractTypedArray(buffer, byteOffset, arrayType, numberOfElements) {\n\t if (!buffer) {\n\t console.warn(\"Input Buffer is null.\");\n\t return null;\n\t }\n\n\t if (!(buffer instanceof ArrayBuffer)) {\n\t console.warn(\"Buffer must be of type ArrayBuffer\");\n\t return null;\n\t }\n\n\t if (numberOfElements <= 0) {\n\t console.warn(\"The number of elements to fetch must be greater than 0\");\n\t return null;\n\t }\n\n\t if (byteOffset < 0) {\n\t console.warn(\"The byte offset must be possitive or 0\");\n\t return null;\n\t }\n\n\t if (byteOffset >= buffer.byteLength) {\n\t console.warn(\"The offset cannot be larger than the size of the buffer.\");\n\t return null;\n\t }\n\n\t if (arrayType instanceof Function && !(\"BYTES_PER_ELEMENT\" in arrayType)) {\n\t console.warn(\"ArrayType must be a typed array constructor function.\");\n\t return null;\n\t }\n\n\t if (arrayType.BYTES_PER_ELEMENT * numberOfElements + byteOffset > buffer.byteLength) {\n\t console.warn(\"The requested number of elements is too large for this buffer\");\n\t return;\n\t }\n\n\t var slicedBuff = buffer.slice(byteOffset, byteOffset + numberOfElements * arrayType.BYTES_PER_ELEMENT);\n\t return new arrayType(slicedBuff);\n\t }\n\n\t /**\n\t * Get some info about the given TypedArray\n\t * @param {TypedArray} typedArray - one of the typed array\n\t * @return {Object} in form of {type: String, signed: Boolean, bytesPerElements: Number, byteLength: Number, length: Number}\n\t */\n\n\t }, {\n\t key: \"getTypedArrayInfo\",\n\t value: function getTypedArrayInfo(typedArray) {\n\t var type = null;\n\t var signed = false;\n\n\t if (typedArray instanceof Int8Array) {\n\t type = \"int\";\n\t signed = false;\n\t } else if (typedArray instanceof Uint8Array) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Uint8ClampedArray) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Int16Array) {\n\t type = \"int\";\n\t signed = false;\n\t } else if (typedArray instanceof Uint16Array) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Int32Array) {\n\t type = \"int\";\n\t signed = false;\n\t } else if (typedArray instanceof Uint32Array) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Float32Array) {\n\t type = \"float\";\n\t signed = false;\n\t } else if (typedArray instanceof Float64Array) {\n\t type = \"float\";\n\t signed = false;\n\t }\n\n\t return {\n\t type: type,\n\t signed: signed,\n\t bytesPerElements: typedArray.BYTES_PER_ELEMENT,\n\t byteLength: typedArray.byteLength,\n\t length: typedArray.length\n\t };\n\t }\n\n\t /**\n\t * Counts the number of typed array obj has as attributes\n\t * @param {Object} obj - an Object\n\t * @return {Number} the number of typed array\n\t */\n\n\t }, {\n\t key: \"howManyTypedArrayAttributes\",\n\t value: function howManyTypedArrayAttributes(obj) {\n\t var typArrCounter = 0;\n\t traverse_1(obj).forEach(function (x) {\n\t typArrCounter += CodecUtils.isTypedArray(x);\n\t });\n\t return typArrCounter;\n\t }\n\n\t /**\n\t * Check if the given object contains any circular reference.\n\t * (Circular ref are non serilizable easily, we want to spot them)\n\t * @param {Object} obj - An object to check\n\t * @return {Boolean} true if obj contains circular refm false if not\n\t */\n\n\t }, {\n\t key: \"hasCircularReference\",\n\t value: function hasCircularReference(obj) {\n\t var hasCircular = false;\n\t traverse_1(obj).forEach(function (x) {\n\t if (this.circular) {\n\t hasCircular = true;\n\t }\n\t });\n\t return hasCircular;\n\t }\n\n\t /**\n\t * Remove circular dependencies from an object and return a circularRef-free version\n\t * of the object (does not change the original obj), of null if no circular ref was found\n\t * @param {Object} obj - An object to check\n\t * @return {Object} a circular-ref free object copy if any was found, or null if no circ was found\n\t */\n\n\t }, {\n\t key: \"removeCircularReference\",\n\t value: function removeCircularReference(obj) {\n\t var hasCircular = false;\n\t var noCircRefObj = traverse_1(obj).map(function (x) {\n\t if (this.circular) {\n\t this.remove();\n\t hasCircular = true;\n\t }\n\t });\n\t return hasCircular ? noCircRefObj : null;\n\t }\n\n\t /**\n\t * Clone the object and replace the typed array attributes by regular Arrays.\n\t * @param {Object} obj - an object to alter\n\t * @return {Object} the clone if ant typed array were changed, or null if was obj didnt contain any typed array.\n\t */\n\n\t }, {\n\t key: \"replaceTypedArrayAttributesByArrays\",\n\t value: function replaceTypedArrayAttributesByArrays(obj) {\n\t var hasTypedArray = false;\n\n\t var noTypedArrClone = traverse_1(obj).map(function (x) {\n\t if (CodecUtils.isTypedArray(x)) {\n\t // here, we cannot call .length directly because traverse.map already serialized\n\t // typed arrays into regular objects\n\t var origSize = Object.keys(x).length;\n\t var untypedArray = new Array(origSize);\n\n\t for (var i = 0; i < origSize; i++) {\n\t untypedArray[i] = x[i];\n\t }\n\t this.update(untypedArray);\n\t hasTypedArray = true;\n\t }\n\t });\n\t return hasTypedArray ? noTypedArrClone : null;\n\t }\n\n\t /**\n\t * Creates a clone, does not alter the original object.\n\t * Remove circular dependencies and replace typed arrays by regular arrays.\n\t * Both will make the serialization possible and more reliable.\n\t * @param {Object} obj - the object to make serialization friendly\n\t * @return {Object} a clean clone, or null if nothing was done\n\t */\n\n\t }, {\n\t key: \"makeSerializeFriendly\",\n\t value: function makeSerializeFriendly(obj) {\n\t var newObj = obj;\n\t var noCircular = CodecUtils.removeCircularReference(newObj);\n\n\t if (noCircular) newObj = noCircular;\n\n\t var noTypedArr = CodecUtils.replaceTypedArrayAttributesByArrays(newObj);\n\n\t if (noTypedArr) newObj = noTypedArr;\n\n\t return newObj;\n\t }\n\n\t /**\n\t * Check if a string is valid or not. A string is considered as invalid if it has\n\t * unicode \"REPLACEMENT CHARACTER\" or non-printable ASCII characters.\n\t * @param {String} str - string to test\n\t * @param {Boolean} forceAll - test the whole string instead of a sample of 1000 charaters\n\t * @return {Boolean} true is the string is valid, false if invalid.\n\t */\n\n\t }, {\n\t key: \"isValidString\",\n\t value: function isValidString(str) {\n\t var forceAll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n\t var strLen = str.length;\n\t var nbSamples = forceAll ? strLen : Math.min(1000, strLen); // a sample of 1000 should be enough\n\t var flagChar = 0xFFFD;\n\t var redFlags = 0;\n\t for (var i = 0; i < nbSamples; i++) {\n\t var code = str.charCodeAt(Math.floor(Math.random() * nbSamples));\n\t if (code === flagChar || code < 32 && code != 10 && code != 13 && code != 9 || code == 127) {\n\t redFlags++;\n\t }\n\t }\n\t return !(redFlags > 0);\n\t }\n\t }]);\n\t return CodecUtils;\n\t}(); /* END of class CodecUtils */\n\n\texports.CodecUtils = CodecUtils;\n\n\tObject.defineProperty(exports, '__esModule', { value: true });\n\n})));\n//# sourceMappingURL=codecutils.umd.js.map\n"," /*\n* Author Jonathan Lurie - http://me.jonahanlurie.fr\n* License MIT\n* Link https://github.com/jonathanlurie/edfdecoder\n* Lab MCIN - http://mcin.ca/ - Montreal Neurological Institute\n*/\n\n\n/**\n* An instance of Edf is usually given as output of an EdfDecoder. It provides an\n* interface with a lot of helper function to query information that were extracted\n* from en *.edf* file, such as header information, getting a signal at a given record\n* or concatenating records of a given signal.\n*\n* Keep in mind that the number of records in an edf file can be decoded by arbitrary\n* measures, or it can be 1 second per records, etc.\n*\n*/\nclass Edf {\n constructor( header, rawSignals, physicalSignals ){\n this._header = header;\n this._physicalSignals = physicalSignals;\n this._rawSignals = rawSignals;\n }\n\n /**\n * Get the duration in second of a single record\n * @return {Number} duration\n */\n getRecordDuration(){\n return this._header.durationDataRecordsSec;\n }\n\n\n /**\n * Get the ID of the recording\n * @return {String} the ID\n */\n getRecordingID(){\n return this._header.localRecordingId;\n }\n\n\n /**\n * get the number of records per signal.\n * Note: most of the time, records from the same signal are contiguous in time.\n * @return {Number} the number of records\n */\n getNumberOfRecords(){\n return this._header.nbDataRecords;\n }\n\n\n /**\n * get the number of signals.\n * Note: a signal can have more than one record\n * @return {Number} the number of signals\n */\n getNumberOfSignals(){\n return this._header.nbSignals;\n }\n\n\n /**\n * Get the patien ID\n * @return {String} ID\n */\n getPatientID(){\n return this._header.patientId;\n }\n\n\n /**\n * Get the date and the time at which the recording has started\n * @return {Date} the date\n */\n getRecordingStartDate(){\n return this._header.recordingDate;\n }\n\n\n /**\n * Get the value of the reserved field, global (from header) or specific to a signal.\n * Notice: reserved are rarely used.\n * @param {Number} index - if not specified, get the header's reserved field. If [0, nbSignals[ get the reserved field specific for the given signal\n * @return {String} the data of the reserved field.\n */\n getReservedField( index=-1 ){\n if( index === -1 ){\n return this._header.reserved;\n }else{\n if( index >= 0 && index < this._header.signalInfo.length ){\n return this._header.signalInfo[index].reserved;\n }\n }\n\n return null;\n }\n\n\n /**\n * Get the digital maximum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalDigitalMax( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].digitalMaximum;\n }\n\n\n /**\n * Get the digital minimum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalDigitalMin( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].digitalMinimum;\n }\n\n\n /**\n * Get the physical minimum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalPhysicalMin( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].physicalMinimum;\n }\n\n\n /**\n * Get the physical maximum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalPhysicalMax( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].physicalMaximum;\n }\n\n\n /**\n * Get the label for a given signal index\n * @param {Number} index - index of the signal\n * @return {String}\n */\n getSignalLabel( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].label;\n }\n\n\n /**\n * Get the number of samples per record for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalNumberOfSamplesPerRecord( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].nbOfSamples;\n }\n\n\n /**\n * Get the unit (dimension label) used for a given signal index.\n * E.g. this can be 'uV' when the signal is an EEG\n * @param {Number} index - index of the signal\n * @return {String} the unit name\n */\n getSignalPhysicalUnit( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].physicalDimension;\n }\n\n\n /**\n * Get the unit prefiltering info for a given signal index.\n * @param {Number} index - index of the signal\n * @return {String} the prefiltering info\n */\n getSignalPrefiltering( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].prefiltering;\n }\n\n\n /**\n * Get the transducer type info for a given signal index.\n * @param {Number} index - index of the signal\n * @return {String} the transducer type info\n */\n getSignalTransducerType( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].transducerType;\n }\n\n\n /**\n * Get the sampling frequency in Hz of a given signal\n * @param {Number} index - index of the signal\n * @return {Number} frequency in Hz\n */\n getSignalSamplingFrequency( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].nbOfSamples / this._header.durationDataRecordsSec;\n }\n\n /**\n * Get the physical (scaled) signal at a given index and record\n * @param {Number} index - index of the signal\n * @param {Number} record - index of the record\n * @return {Float32Array} the physical signal in Float32\n */\n getPhysicalSignal( index, record ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n if( record<0 && record>=this._physicalSignals[index].length ){\n console.warn(\"Record index is out of range\");\n return null;\n }\n\n return this._physicalSignals[index][record];\n }\n\n\n /**\n * Get the raw (digital) signal at a given index and record\n * @param {Number} index - index of the signal\n * @param {Number} record - index of the record\n * @return {Int16Array} the physical signal in Int16\n */\n getRawSignal( index, record ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n if( record<0 && record>=this._rawSignals[index].length ){\n console.warn(\"Record index is out of range\");\n return null;\n }\n\n return this._rawSignals[index][record];\n }\n\n\n\n /**\n * Get concatenated contiguous records of a given signal, the index of the\n * first record and the number of records to concat.\n * Notice: this allocates a new buffer of an extented size.\n * @param {Number} index - index of the signal\n * @param {Number} recordStart - index of the record to start with\n * @param {Number} howMany - Number of records to concatenate\n * @return {Float32Array} the physical signal in Float32\n */\n getPhysicalSignalConcatRecords(index, recordStart=-1, howMany=-1){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n if( recordStart<0 && recordStart>=this._physicalSignals[index].length ){\n console.warn(\"The index recordStart is out of range\");\n return null;\n }\n\n if(recordStart === -1){\n recordStart = 0;\n }\n\n if(howMany === -1){\n howMany = this._physicalSignals[index].length - recordStart;\n }else{\n // we still want to check if what the user put is not out of bound\n if( recordStart + howMany > this._physicalSignals[index].length ){\n console.warn(\"The number of requested records is too large. Forcing only to available records.\");\n howMany = this._physicalSignals[index].length - recordStart; \n }\n\n }\n\n // index of the last one to consider\n var recordEnd = recordStart + howMany - 1;\n\n if( recordEnd<0 && recordEnd>=this._physicalSignals[index].length ){\n console.warn(\"Too many records to concatenate, this goes out of range.\");\n return null;\n }\n\n var totalSize = 0;\n for(var i=recordStart; i 191 && c1 < 224) {\n\t var c2 = buffUint8[pos++];\n\t out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);\n\t } else if (c1 > 239 && c1 < 365) {\n\t // Surrogate Pair\n\t var c2 = buffUint8[pos++];\n\t var c3 = buffUint8[pos++];\n\t var c4 = buffUint8[pos++];\n\t var u = ((c1 & 7) << 18 | (c2 & 63) << 12 | (c3 & 63) << 6 | c4 & 63) - 0x10000;\n\t out[c++] = String.fromCharCode(0xD800 + (u >> 10));\n\t out[c++] = String.fromCharCode(0xDC00 + (u & 1023));\n\t } else {\n\t var c2 = buffUint8[pos++];\n\t var c3 = buffUint8[pos++];\n\t var code = (c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63;\n\t if (code === 0xFFFD) {\n\t console.warn(\"Invalid string: a REPLACEMENT CHARACTER was spotted\");\n\t return null;\n\t }\n\t out[c++] = String.fromCharCode(code);\n\t }\n\t }\n\t return out.join('');\n\t }\n\t }, {\n\t key: \"unicodeToArrayBuffer\",\n\n\n\t /**\n\t * convert a unicode string into an ArrayBuffer\n\t * Note that the str is a regular string but it will be encoded with\n\t * 2 bytes per char instead of 1 ( ASCII uses 1 byte/char ).\n\t * Note: this method was kindly borrowed from Google Closure Compiler:\n\t * https://github.com/google/closure-library/blob/master/closure/goog/crypt/crypt.js\n\t * @param {String} str - string to encode\n\t * @return {ArrayBuffer} the output ArrayBuffer\n\t */\n\t value: function unicodeToArrayBuffer(str) {\n\t var out = [],\n\t p = 0;\n\t for (var i = 0; i < str.length; i++) {\n\t var c = str.charCodeAt(i);\n\t if (c < 128) {\n\t out[p++] = c;\n\t } else if (c < 2048) {\n\t out[p++] = c >> 6 | 192;\n\t out[p++] = c & 63 | 128;\n\t } else if ((c & 0xFC00) == 0xD800 && i + 1 < str.length && (str.charCodeAt(i + 1) & 0xFC00) == 0xDC00) {\n\t // Surrogate Pair\n\t c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);\n\t out[p++] = c >> 18 | 240;\n\t out[p++] = c >> 12 & 63 | 128;\n\t out[p++] = c >> 6 & 63 | 128;\n\t out[p++] = c & 63 | 128;\n\t } else {\n\t out[p++] = c >> 12 | 224;\n\t out[p++] = c >> 6 & 63 | 128;\n\t out[p++] = c & 63 | 128;\n\t }\n\t }\n\n\t // make a buffer out of the array\n\t return new Uint8Array(out).buffer;\n\t }\n\t }, {\n\t key: \"arrayBufferToString8\",\n\n\n\t /**\n\t * Convert an ArrayBuffer into a ASCII string (1 byte for each char)\n\t * @param {ArrayBuffer} buf - buffer to convert into ASCII string\n\t * @return {String} the output string\n\t */\n\t value: function arrayBufferToString8(buf) {\n\t return String.fromCharCode.apply(null, new Uint8Array(buf));\n\t }\n\n\t /**\n\t * Convert a ASCII string into an ArrayBuffer.\n\t * Note that the str is a regular string, it will be encoded with 1 byte per char\n\t * @param {String} str - string to encode\n\t * @return {ArrayBuffer}\n\t */\n\n\t }, {\n\t key: \"string8ToArrayBuffer\",\n\t value: function string8ToArrayBuffer(str) {\n\t var buf = new ArrayBuffer(str.length);\n\t var bufView = new Uint8Array(buf);\n\t for (var i = 0; i < str.length; i++) {\n\t bufView[i] = str.charCodeAt(i);\n\t }\n\t return buf;\n\t }\n\n\t /**\n\t * Write a ASCII string into a buffer\n\t * @param {String} str - a string that contains only ASCII characters\n\t * @param {ArrayBuffer} buffer - the buffer where to write the string\n\t * @param {Number} byteOffset - the offset to apply, in number of bytes\n\t */\n\n\t }, {\n\t key: \"setString8InBuffer\",\n\t value: function setString8InBuffer(str, buffer) {\n\t var byteOffset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n\n\t if (byteOffset < 0) {\n\t console.warn(\"The byte offset cannot be negative.\");\n\t return;\n\t }\n\n\t if (!buffer || !(buffer instanceof ArrayBuffer)) {\n\t console.warn(\"The buffer must be a valid ArrayBuffer.\");\n\t return;\n\t }\n\n\t if (str.length + byteOffset > buffer.byteLength) {\n\t console.warn(\"The string is too long to be writen in this buffer.\");\n\t return;\n\t }\n\n\t var bufView = new Uint8Array(buffer);\n\n\t for (var i = 0; i < str.length; i++) {\n\t bufView[i + byteOffset] = str.charCodeAt(i);\n\t }\n\t }\n\n\t /**\n\t * Extract an ASCII string from an ArrayBuffer\n\t * @param {ArrayBuffer} buffer - the buffer\n\t * @param {Number} strLength - number of chars in the string we want\n\t * @param {Number} byteOffset - the offset in number of bytes\n\t * @return {String} the string, or null in case of error\n\t */\n\n\t }, {\n\t key: \"getString8FromBuffer\",\n\t value: function getString8FromBuffer(buffer, strLength) {\n\t var byteOffset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n\n\t if (byteOffset < 0) {\n\t console.warn(\"The byte offset cannot be negative.\");\n\t return null;\n\t }\n\n\t if (!buffer || !(buffer instanceof ArrayBuffer)) {\n\t console.warn(\"The buffer must be a valid ArrayBuffer.\");\n\t return null;\n\t }\n\n\t if (strLength + byteOffset > buffer.byteLength) {\n\t console.warn(\"The string is too long to be writen in this buffer.\");\n\t return null;\n\t }\n\n\t return String.fromCharCode.apply(null, new Uint8Array(buffer, byteOffset, strLength));\n\t }\n\n\t /**\n\t * Serializes a JS object into an ArrayBuffer.\n\t * This is using a unicode JSON intermediate step.\n\t * @param {Object} obj - an object that does not have cyclic structure\n\t * @return {ArrayBuffer} the serialized output\n\t */\n\n\t }, {\n\t key: \"objectToArrayBuffer\",\n\t value: function objectToArrayBuffer(obj) {\n\t var buff = null;\n\t var objCleanClone = CodecUtils.makeSerializeFriendly(obj);\n\n\t try {\n\t var strObj = JSON.stringify(objCleanClone);\n\t buff = CodecUtils.unicodeToArrayBuffer(strObj);\n\t } catch (e) {\n\t console.warn(e);\n\t }\n\n\t return buff;\n\t }\n\n\t /**\n\t * Convert an ArrayBuffer into a JS Object. This uses an intermediate unicode JSON string.\n\t * Of course, this buffer has to come from a serialized object.\n\t * @param {ArrayBuffer} buff - the ArrayBuffer that hides some object\n\t * @return {Object} the deserialized object\n\t */\n\n\t }, {\n\t key: \"ArrayBufferToObject\",\n\t value: function ArrayBufferToObject(buff) {\n\t var obj = null;\n\n\t try {\n\t var strObj = CodecUtils.arrayBufferToUnicode(buff);\n\t obj = JSON.parse(strObj);\n\t } catch (e) {\n\t console.warn(e);\n\t }\n\n\t return obj;\n\t }\n\n\t /**\n\t * Get if wether of not the arg is a typed array\n\t * @param {Object} obj - possibly a typed array, or maybe not\n\t * @return {Boolean} true if obj is a typed array\n\t */\n\n\t }, {\n\t key: \"isTypedArray\",\n\t value: function isTypedArray(obj) {\n\t return obj instanceof Int8Array || obj instanceof Uint8Array || obj instanceof Uint8ClampedArray || obj instanceof Int16Array || obj instanceof Uint16Array || obj instanceof Int32Array || obj instanceof Uint32Array || obj instanceof Float32Array || obj instanceof Float64Array;\n\t }\n\n\t /**\n\t * Merge some ArrayBuffes in a single one\n\t * @param {Array} arrayOfBuffers - some ArrayBuffers\n\t * @return {ArrayBuffer} the larger merged buffer\n\t */\n\n\t }, {\n\t key: \"mergeBuffers\",\n\t value: function mergeBuffers(arrayOfBuffers) {\n\t var totalByteSize = 0;\n\n\t for (var i = 0; i < arrayOfBuffers.length; i++) {\n\t totalByteSize += arrayOfBuffers[i].byteLength;\n\t }\n\n\t var concatArray = new Uint8Array(totalByteSize);\n\n\t var offset = 0;\n\t for (var i = 0; i < arrayOfBuffers.length; i++) {\n\t concatArray.set(new Uint8Array(arrayOfBuffers[i]), offset);\n\t offset += arrayOfBuffers[i].byteLength;\n\t }\n\n\t return concatArray.buffer;\n\t }\n\n\t /**\n\t * In a browser, the global object is `window` while in Node, it's `GLOBAL`.\n\t * This method return the one that is relevant to the execution context.\n\t * @return {Object} the global object\n\t */\n\n\t }, {\n\t key: \"getGlobalObject\",\n\t value: function getGlobalObject() {\n\t var constructorHost = null;\n\n\t try {\n\t constructorHost = window; // in a web browser\n\t } catch (e) {\n\t try {\n\t constructorHost = GLOBAL; // in node\n\t } catch (e) {\n\t console.warn(\"You are not in a Javascript environment?? Weird.\");\n\t return null;\n\t }\n\t }\n\t return constructorHost;\n\t }\n\n\t /**\n\t * Extract a typed array from an arbitrary buffer, with an arbitrary offset\n\t * @param {ArrayBuffer} buffer - the buffer from which we extract data\n\t * @param {Number} byteOffset - offset from the begining of buffer\n\t * @param {Function} arrayType - function object, actually the constructor of the output array\n\t * @param {Number} numberOfElements - nb of elem we want to fetch from the buffer\n\t * @return {TypedArray} output of type given by arg arrayType - this is a copy, not a view\n\t */\n\n\t }, {\n\t key: \"extractTypedArray\",\n\t value: function extractTypedArray(buffer, byteOffset, arrayType, numberOfElements) {\n\t if (!buffer) {\n\t console.warn(\"Input Buffer is null.\");\n\t return null;\n\t }\n\n\t if (!(buffer instanceof ArrayBuffer)) {\n\t console.warn(\"Buffer must be of type ArrayBuffer\");\n\t return null;\n\t }\n\n\t if (numberOfElements <= 0) {\n\t console.warn(\"The number of elements to fetch must be greater than 0\");\n\t return null;\n\t }\n\n\t if (byteOffset < 0) {\n\t console.warn(\"The byte offset must be possitive or 0\");\n\t return null;\n\t }\n\n\t if (byteOffset >= buffer.byteLength) {\n\t console.warn(\"The offset cannot be larger than the size of the buffer.\");\n\t return null;\n\t }\n\n\t if (arrayType instanceof Function && !(\"BYTES_PER_ELEMENT\" in arrayType)) {\n\t console.warn(\"ArrayType must be a typed array constructor function.\");\n\t return null;\n\t }\n\n\t if (arrayType.BYTES_PER_ELEMENT * numberOfElements + byteOffset > buffer.byteLength) {\n\t console.warn(\"The requested number of elements is too large for this buffer\");\n\t return;\n\t }\n\n\t var slicedBuff = buffer.slice(byteOffset, byteOffset + numberOfElements * arrayType.BYTES_PER_ELEMENT);\n\t return new arrayType(slicedBuff);\n\t }\n\n\t /**\n\t * Get some info about the given TypedArray\n\t * @param {TypedArray} typedArray - one of the typed array\n\t * @return {Object} in form of {type: String, signed: Boolean, bytesPerElements: Number, byteLength: Number, length: Number}\n\t */\n\n\t }, {\n\t key: \"getTypedArrayInfo\",\n\t value: function getTypedArrayInfo(typedArray) {\n\t var type = null;\n\t var signed = false;\n\n\t if (typedArray instanceof Int8Array) {\n\t type = \"int\";\n\t signed = false;\n\t } else if (typedArray instanceof Uint8Array) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Uint8ClampedArray) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Int16Array) {\n\t type = \"int\";\n\t signed = false;\n\t } else if (typedArray instanceof Uint16Array) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Int32Array) {\n\t type = \"int\";\n\t signed = false;\n\t } else if (typedArray instanceof Uint32Array) {\n\t type = \"int\";\n\t signed = true;\n\t } else if (typedArray instanceof Float32Array) {\n\t type = \"float\";\n\t signed = false;\n\t } else if (typedArray instanceof Float64Array) {\n\t type = \"float\";\n\t signed = false;\n\t }\n\n\t return {\n\t type: type,\n\t signed: signed,\n\t bytesPerElements: typedArray.BYTES_PER_ELEMENT,\n\t byteLength: typedArray.byteLength,\n\t length: typedArray.length\n\t };\n\t }\n\n\t /**\n\t * Counts the number of typed array obj has as attributes\n\t * @param {Object} obj - an Object\n\t * @return {Number} the number of typed array\n\t */\n\n\t }, {\n\t key: \"howManyTypedArrayAttributes\",\n\t value: function howManyTypedArrayAttributes(obj) {\n\t var typArrCounter = 0;\n\t traverse_1(obj).forEach(function (x) {\n\t typArrCounter += CodecUtils.isTypedArray(x);\n\t });\n\t return typArrCounter;\n\t }\n\n\t /**\n\t * Check if the given object contains any circular reference.\n\t * (Circular ref are non serilizable easily, we want to spot them)\n\t * @param {Object} obj - An object to check\n\t * @return {Boolean} true if obj contains circular refm false if not\n\t */\n\n\t }, {\n\t key: \"hasCircularReference\",\n\t value: function hasCircularReference(obj) {\n\t var hasCircular = false;\n\t traverse_1(obj).forEach(function (x) {\n\t if (this.circular) {\n\t hasCircular = true;\n\t }\n\t });\n\t return hasCircular;\n\t }\n\n\t /**\n\t * Remove circular dependencies from an object and return a circularRef-free version\n\t * of the object (does not change the original obj), of null if no circular ref was found\n\t * @param {Object} obj - An object to check\n\t * @return {Object} a circular-ref free object copy if any was found, or null if no circ was found\n\t */\n\n\t }, {\n\t key: \"removeCircularReference\",\n\t value: function removeCircularReference(obj) {\n\t var hasCircular = false;\n\t var noCircRefObj = traverse_1(obj).map(function (x) {\n\t if (this.circular) {\n\t this.remove();\n\t hasCircular = true;\n\t }\n\t });\n\t return hasCircular ? noCircRefObj : null;\n\t }\n\n\t /**\n\t * Clone the object and replace the typed array attributes by regular Arrays.\n\t * @param {Object} obj - an object to alter\n\t * @return {Object} the clone if ant typed array were changed, or null if was obj didnt contain any typed array.\n\t */\n\n\t }, {\n\t key: \"replaceTypedArrayAttributesByArrays\",\n\t value: function replaceTypedArrayAttributesByArrays(obj) {\n\t var hasTypedArray = false;\n\n\t var noTypedArrClone = traverse_1(obj).map(function (x) {\n\t if (CodecUtils.isTypedArray(x)) {\n\t // here, we cannot call .length directly because traverse.map already serialized\n\t // typed arrays into regular objects\n\t var origSize = Object.keys(x).length;\n\t var untypedArray = new Array(origSize);\n\n\t for (var i = 0; i < origSize; i++) {\n\t untypedArray[i] = x[i];\n\t }\n\t this.update(untypedArray);\n\t hasTypedArray = true;\n\t }\n\t });\n\t return hasTypedArray ? noTypedArrClone : null;\n\t }\n\n\t /**\n\t * Creates a clone, does not alter the original object.\n\t * Remove circular dependencies and replace typed arrays by regular arrays.\n\t * Both will make the serialization possible and more reliable.\n\t * @param {Object} obj - the object to make serialization friendly\n\t * @return {Object} a clean clone, or null if nothing was done\n\t */\n\n\t }, {\n\t key: \"makeSerializeFriendly\",\n\t value: function makeSerializeFriendly(obj) {\n\t var newObj = obj;\n\t var noCircular = CodecUtils.removeCircularReference(newObj);\n\n\t if (noCircular) newObj = noCircular;\n\n\t var noTypedArr = CodecUtils.replaceTypedArrayAttributesByArrays(newObj);\n\n\t if (noTypedArr) newObj = noTypedArr;\n\n\t return newObj;\n\t }\n\n\t /**\n\t * Check if a string is valid or not. A string is considered as invalid if it has\n\t * unicode \"REPLACEMENT CHARACTER\" or non-printable ASCII characters.\n\t * @param {String} str - string to test\n\t * @param {Boolean} forceAll - test the whole string instead of a sample of 1000 charaters\n\t * @return {Boolean} true is the string is valid, false if invalid.\n\t */\n\n\t }, {\n\t key: \"isValidString\",\n\t value: function isValidString(str) {\n\t var forceAll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n\t var strLen = str.length;\n\t var nbSamples = forceAll ? strLen : Math.min(1000, strLen); // a sample of 1000 should be enough\n\t var flagChar = 0xFFFD;\n\t var redFlags = 0;\n\t for (var i = 0; i < nbSamples; i++) {\n\t var code = str.charCodeAt(Math.floor(Math.random() * nbSamples));\n\t if (code === flagChar || code < 32 && code != 10 && code != 13 && code != 9 || code == 127) {\n\t redFlags++;\n\t }\n\t }\n\t return !(redFlags > 0);\n\t }\n\t }]);\n\t return CodecUtils;\n\t}(); /* END of class CodecUtils */\n\n\texports.CodecUtils = CodecUtils;\n\n\tObject.defineProperty(exports, '__esModule', { value: true });\n\n})));\n//# sourceMappingURL=codecutils.umd.js.map\n"," /*\n* Author Jonathan Lurie - http://me.jonahanlurie.fr\n* License MIT\n* Link https://github.com/jonathanlurie/edfdecoder\n* Lab MCIN - http://mcin.ca/ - Montreal Neurological Institute\n*/\n\n\n/**\n* An instance of Edf is usually given as output of an EdfDecoder. It provides an\n* interface with a lot of helper function to query information that were extracted\n* from en *.edf* file, such as header information, getting a signal at a given record\n* or concatenating records of a given signal.\n*\n* Keep in mind that the number of records in an edf file can be decoded by arbitrary\n* measures, or it can be 1 second per records, etc.\n*\n*/\nclass Edf {\n constructor( header, rawSignals, physicalSignals ){\n this._header = header;\n this._physicalSignals = physicalSignals;\n this._rawSignals = rawSignals;\n }\n\n /**\n * Get the duration in second of a single record\n * @return {Number} duration\n */\n getRecordDuration(){\n return this._header.durationDataRecordsSec;\n }\n\n\n /**\n * Get the ID of the recording\n * @return {String} the ID\n */\n getRecordingID(){\n return this._header.localRecordingId;\n }\n\n\n /**\n * get the number of records per signal.\n * Note: most of the time, records from the same signal are contiguous in time.\n * @return {Number} the number of records\n */\n getNumberOfRecords(){\n return this._header.nbDataRecords;\n }\n\n\n /**\n * get the number of signals.\n * Note: a signal can have more than one record\n * @return {Number} the number of signals\n */\n getNumberOfSignals(){\n return this._header.nbSignals;\n }\n\n\n /**\n * Get the patien ID\n * @return {String} ID\n */\n getPatientID(){\n return this._header.patientId;\n }\n\n\n /**\n * Get the date and the time at which the recording has started\n * @return {Date} the date\n */\n getRecordingStartDate(){\n return this._header.recordingDate;\n }\n\n\n /**\n * Get the value of the reserved field, global (from header) or specific to a signal.\n * Notice: reserved are rarely used.\n * @param {Number} index - if not specified, get the header's reserved field. If [0, nbSignals[ get the reserved field specific for the given signal\n * @return {String} the data of the reserved field.\n */\n getReservedField( index=-1 ){\n if( index === -1 ){\n return this._header.reserved;\n }else{\n if( index >= 0 && index < this._header.signalInfo.length ){\n return this._header.signalInfo[index].reserved;\n }\n }\n\n return null;\n }\n\n\n /**\n * Get the digital maximum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalDigitalMax( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].digitalMaximum;\n }\n\n\n /**\n * Get the digital minimum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalDigitalMin( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].digitalMinimum;\n }\n\n\n /**\n * Get the physical minimum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalPhysicalMin( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].physicalMinimum;\n }\n\n\n /**\n * Get the physical maximum for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalPhysicalMax( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].physicalMaximum;\n }\n\n\n /**\n * Get the label for a given signal index\n * @param {Number} index - index of the signal\n * @return {String}\n */\n getSignalLabel( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].label;\n }\n\n\n /**\n * Get the number of samples per record for a given signal index\n * @param {Number} index - index of the signal\n * @return {Number}\n */\n getSignalNumberOfSamplesPerRecord( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].nbOfSamples;\n }\n\n\n /**\n * Get the unit (dimension label) used for a given signal index.\n * E.g. this can be 'uV' when the signal is an EEG\n * @param {Number} index - index of the signal\n * @return {String} the unit name\n */\n getSignalPhysicalUnit( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].physicalDimension;\n }\n\n\n /**\n * Get the unit prefiltering info for a given signal index.\n * @param {Number} index - index of the signal\n * @return {String} the prefiltering info\n */\n getSignalPrefiltering( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].prefiltering;\n }\n\n\n /**\n * Get the transducer type info for a given signal index.\n * @param {Number} index - index of the signal\n * @return {String} the transducer type info\n */\n getSignalTransducerType( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].transducerType;\n }\n\n\n /**\n * Get the sampling frequency in Hz of a given signal\n * @param {Number} index - index of the signal\n * @return {Number} frequency in Hz\n */\n getSignalSamplingFrequency( index ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n return this._header.signalInfo[index].nbOfSamples / this._header.durationDataRecordsSec;\n }\n\n /**\n * Get the physical (scaled) signal at a given index and record\n * @param {Number} index - index of the signal\n * @param {Number} record - index of the record\n * @return {Float32Array} the physical signal in Float32\n */\n getPhysicalSignal( index, record ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n if( record<0 && record>=this._physicalSignals[index].length ){\n console.warn(\"Record index is out of range\");\n return null;\n }\n\n return this._physicalSignals[index][record];\n }\n\n\n /**\n * Get the raw (digital) signal at a given index and record\n * @param {Number} index - index of the signal\n * @param {Number} record - index of the record\n * @return {Int16Array} the physical signal in Int16\n */\n getRawSignal( index, record ){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n if( record<0 && record>=this._rawSignals[index].length ){\n console.warn(\"Record index is out of range\");\n return null;\n }\n\n return this._rawSignals[index][record];\n }\n\n\n\n /**\n * Get concatenated contiguous records of a given signal, the index of the\n * first record and the number of records to concat.\n * Notice: this allocates a new buffer of an extented size.\n * @param {Number} index - index of the signal\n * @param {Number} recordStart - index of the record to start with\n * @param {Number} howMany - Number of records to concatenate\n * @return {Float32Array} the physical signal in Float32\n */\n getPhysicalSignalConcatRecords(index, recordStart=-1, howMany=-1){\n if( index < 0 || index >= this._header.signalInfo.length ){\n console.warn(\"Signal index is out of range\");\n return null;\n }\n\n if( recordStart<0 && recordStart>=this._physicalSignals[index].length ){\n console.warn(\"The index recordStart is out of range\");\n return null;\n }\n\n if(recordStart === -1){\n recordStart = 0;\n }\n\n if(howMany === -1){\n howMany = this._physicalSignals[index].length - recordStart;\n }else{\n // we still want to check if what the user put is not out of bound\n if( recordStart + howMany > this._physicalSignals[index].length ){\n console.warn(\"The number of requested records is too large. Forcing only to available records.\");\n howMany = this._physicalSignals[index].length - recordStart; \n }\n\n }\n\n // index of the last one to consider\n var recordEnd = recordStart + howMany - 1;\n\n if( recordEnd<0 && recordEnd>=this._physicalSignals[index].length ){\n console.warn(\"Too many records to concatenate, this goes out of range.\");\n return null;\n }\n\n var totalSize = 0;\n for(var i=recordStart; i= 85) {\n year = 1900 + Number(date[2]);\n } else {\n year = 2000 + Number(date[2]);\n }\n var time = recordingStartTime.split(\".\");\n\n header.recordingDate = new Date( year, Number(date[1] - 1), date[0], time[0], time[1], time[2], 0 );\n\n // 8 ascii : number of bytes in header record\n header.nbBytesHeaderRecord = parseInt( codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 8, offset).trim() );\n offset += 8;\n\n // 44 ascii : reserved\n header.reserved = codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 44, offset);\n offset += 44;\n\n // 8 ascii : number of data records (-1 if unknown)\n header.nbDataRecords = parseInt( codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 8, offset).trim() );\n offset += 8;\n\n // 8 ascii : duration of a data record, in seconds\n header.durationDataRecordsSec = parseInt( codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 8, offset).trim() );\n offset += 8;\n\n // 4 ascii : number of signals (ns) in data record\n header.nbSignals = parseInt( codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 4, offset).trim() );\n offset += 4;\n\n // the following fields occurs ns time in a row each\n var that = this;\n function getAllSections( sizeOfEachThing ){\n var allThings = [];\n for(var i=0; i= 85) { + year = 1900 + Number(date[2]); + } else { + year = 2000 + Number(date[2]); + } var time = recordingStartTime.split("."); - header.recordingDate = new Date( date[2], date[1], date[0], time[0], time[1], time[2], 0 ); + + header.recordingDate = new Date( year, Number(date[1] - 1), date[0], time[0], time[1], time[2], 0 ); // 8 ascii : number of bytes in header record header.nbBytesHeaderRecord = parseInt( codecutils.CodecUtils.getString8FromBuffer( this._inputBuffer , 8, offset).trim() );