| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  | // tslint:disable:file-header
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Like JSON.stringify, but without double quotes around keys, and without null instead of undefined | 
					
						
							|  |  |  |  * values | 
					
						
							|  |  |  |  * Based on https://github.com/json5/json5/blob/master/lib/json5.js
 | 
					
						
							| 
									
										
										
										
											2018-01-26 11:06:13 +01:00
										 |  |  |  * Use option "quoteKeys" to preserve quotes for keys | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-01-26 11:06:13 +01:00
										 |  |  | module.exports.stringify = function(obj, quoteKeys) { | 
					
						
							|  |  |  |   var getReplacedValueOrUndefined = function(holder, key) { | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |     var value = holder[key]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Replace the value with its toJSON value first, if possible
 | 
					
						
							|  |  |  |     if (value && value.toJSON && typeof value.toJSON === 'function') { | 
					
						
							|  |  |  |       value = value.toJSON(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-26 11:06:13 +01:00
										 |  |  |     return value; | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function isWordChar(c) { | 
					
						
							|  |  |  |     return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || | 
					
						
							|  |  |  |         c === '_' || c === '$'; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function isWordStart(c) { | 
					
						
							|  |  |  |     return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c === '_' || c === '$'; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function isWord(key) { | 
					
						
							|  |  |  |     if (typeof key !== 'string') { | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!isWordStart(key[0])) { | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     var i = 1, length = key.length; | 
					
						
							|  |  |  |     while (i < length) { | 
					
						
							|  |  |  |       if (!isWordChar(key[i])) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       i++; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // polyfills
 | 
					
						
							|  |  |  |   function isArray(obj) { | 
					
						
							|  |  |  |     if (Array.isArray) { | 
					
						
							|  |  |  |       return Array.isArray(obj); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       return Object.prototype.toString.call(obj) === '[object Array]'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |   function isDate(obj) { | 
					
						
							|  |  |  |     return Object.prototype.toString.call(obj) === '[object Date]'; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   var objStack = []; | 
					
						
							|  |  |  |   function checkForCircular(obj) { | 
					
						
							|  |  |  |     for (var i = 0; i < objStack.length; i++) { | 
					
						
							|  |  |  |       if (objStack[i] === obj) { | 
					
						
							|  |  |  |         throw new TypeError('Converting circular structure to JSON'); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Copied from Crokford's implementation of JSON
 | 
					
						
							|  |  |  |   // See
 | 
					
						
							|  |  |  |   // https://github.com/douglascrockford/JSON-js/blob/e39db4b7e6249f04a195e7dd0840e610cc9e941e/json2.js#L195
 | 
					
						
							|  |  |  |   // Begin
 | 
					
						
							|  |  |  |   var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, | 
					
						
							|  |  |  |     escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, | 
					
						
							|  |  |  |     meta = { // table of character substitutions
 | 
					
						
							|  |  |  |       '\b': '\\b', | 
					
						
							|  |  |  |       '\t': '\\t', | 
					
						
							|  |  |  |       '\n': '\\n', | 
					
						
							|  |  |  |       '\f': '\\f', | 
					
						
							|  |  |  |       '\r': '\\r', | 
					
						
							|  |  |  |       '"' : '\\"', | 
					
						
							|  |  |  |       '\\': '\\\\' | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2018-01-26 11:06:13 +01:00
										 |  |  |   function escapeString(str, keepQuotes) { | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |     // If the string contains no control characters, no quote characters, and no
 | 
					
						
							|  |  |  |     // backslash characters, then we can safely slap some quotes around it.
 | 
					
						
							|  |  |  |     // Otherwise we must also replace the offending characters with safe escape
 | 
					
						
							|  |  |  |     // sequences.
 | 
					
						
							|  |  |  |     escapable.lastIndex = 0; | 
					
						
							| 
									
										
										
										
											2018-01-26 11:06:13 +01:00
										 |  |  |     return escapable.test(str) && !keepQuotes ? '"' + str.replace(escapable, function(a) { | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |       var c = meta[a]; | 
					
						
							|  |  |  |       return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); | 
					
						
							|  |  |  |     }) + '"' : '"' + str + '"'; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // End
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-26 11:06:13 +01:00
										 |  |  |   function internalStringify(holder, key) { | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |     var buffer, res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Replace the value, if necessary
 | 
					
						
							| 
									
										
										
										
											2018-01-26 11:06:13 +01:00
										 |  |  |     var obj_part = getReplacedValueOrUndefined(holder, key); | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (obj_part && !isDate(obj_part)) { | 
					
						
							|  |  |  |       // unbox objects
 | 
					
						
							|  |  |  |       // don't unbox dates, since will turn it into number
 | 
					
						
							|  |  |  |       obj_part = obj_part.valueOf(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     switch (typeof obj_part) { | 
					
						
							|  |  |  |       case 'boolean': | 
					
						
							|  |  |  |         return obj_part.toString(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       case 'number': | 
					
						
							|  |  |  |         if (isNaN(obj_part) || !isFinite(obj_part)) { | 
					
						
							|  |  |  |           return 'null'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return obj_part.toString(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       case 'string': | 
					
						
							|  |  |  |         return escapeString(obj_part.toString()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       case 'object': | 
					
						
							|  |  |  |         if (obj_part === null) { | 
					
						
							|  |  |  |           return 'null'; | 
					
						
							|  |  |  |         } else if (isArray(obj_part)) { | 
					
						
							|  |  |  |           checkForCircular(obj_part); | 
					
						
							|  |  |  |           buffer = '['; | 
					
						
							|  |  |  |           objStack.push(obj_part); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           for (var i = 0; i < obj_part.length; i++) { | 
					
						
							| 
									
										
										
										
											2018-01-26 11:06:13 +01:00
										 |  |  |             res = internalStringify(obj_part, i); | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |             if (res === null) { | 
					
						
							|  |  |  |               buffer += 'null'; | 
					
						
							| 
									
										
										
										
											2018-04-10 13:30:51 +02:00
										 |  |  |             } /* else if (typeof res === 'undefined') {  // modified to support empty array values
 | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |               buffer += ''; | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |             }*/ | 
					
						
							|  |  |  |             else { | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |               buffer += res; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (i < obj_part.length - 1) { | 
					
						
							|  |  |  |               buffer += ','; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           objStack.pop(); | 
					
						
							|  |  |  |           buffer += ']'; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           checkForCircular(obj_part); | 
					
						
							|  |  |  |           buffer = '{'; | 
					
						
							|  |  |  |           var nonEmpty = false; | 
					
						
							|  |  |  |           objStack.push(obj_part); | 
					
						
							|  |  |  |           for (var prop in obj_part) { | 
					
						
							|  |  |  |             if (obj_part.hasOwnProperty(prop)) { | 
					
						
							| 
									
										
										
										
											2019-01-17 12:34:30 +01:00
										 |  |  |               var value = internalStringify(obj_part, prop); | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |               if (typeof value !== 'undefined' && value !== null) { | 
					
						
							|  |  |  |                 nonEmpty = true; | 
					
						
							| 
									
										
										
										
											2018-01-26 11:06:13 +01:00
										 |  |  |                 key = isWord(prop) && !quoteKeys ? prop : escapeString(prop, quoteKeys); | 
					
						
							|  |  |  |                 buffer += key + ':' + value + ','; | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           objStack.pop(); | 
					
						
							|  |  |  |           if (nonEmpty) { | 
					
						
							| 
									
										
										
										
											2018-01-26 11:06:13 +01:00
										 |  |  |             buffer = buffer.substring(0, buffer.length - 1) + '}'; | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |           } else { | 
					
						
							|  |  |  |             buffer = '{}'; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return buffer; | 
					
						
							|  |  |  |       default: | 
					
						
							|  |  |  |         // functions and undefined should be ignored
 | 
					
						
							|  |  |  |         return undefined; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // special case...when undefined is used inside of
 | 
					
						
							|  |  |  |   // a compound object/array, return null.
 | 
					
						
							|  |  |  |   // but when top-level, return undefined
 | 
					
						
							|  |  |  |   var topLevelHolder = {'': obj}; | 
					
						
							|  |  |  |   if (obj === undefined) { | 
					
						
							| 
									
										
										
										
											2019-01-17 12:34:30 +01:00
										 |  |  |     return getReplacedValueOrUndefined(topLevelHolder, ''); | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-01-26 11:06:13 +01:00
										 |  |  |   return internalStringify(topLevelHolder, ''); | 
					
						
							| 
									
										
										
										
											2018-01-18 14:04:41 +01:00
										 |  |  | }; |