-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathtoYaml.js
446 lines (396 loc) · 13.3 KB
/
toYaml.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
// toYAML - Core - Copyright Gregor Schwab <[email protected]> (MIT Licensed)
var sys = require('sys');
var defaults = {
enableLinks: true,
//default
yamlCompatible: false,
//default
usePadding: true,
extendObjects: false,
maxLevel: 100,
showHidden:false,
maxLevelMessage: function() {
return 'Max Object Depth has been reached: maxLevel ' + this.maxLevel + ' please set maxLevel to a higher level if your object is deeper than this level.'
}
}
function setDefaults(def) {
helper.extend(defaults, def);
}
function extendObjects(bool) {
if (bool) {
Object.prototype.toYaml = OOToYaml;
Array.prototype.toYaml = OOToYaml;
} else {
delete Object.prototype.toYaml;
delete Array.prototype.toYaml;
}
}
function toYaml(obj, options){
return yamlSerializer(options).toYaml(obj);
}
function preProcessLinks(obj, options){
return yamlSerializer(options).preProcessLinks(obj);
}
exports.extendObjects = extendObjects;
exports.toYaml = toYaml
exports.preProcessLinks = preProcessLinks
exports.setDefaults = setDefaults
function OOToYaml(options) {
return exports.toYaml(this, options);
}
function yamlSerializer(options) {
var o,
obja = [],
newoba = [],
Path = [],
oPath = [];
var opts = {};
var idCounter = 0;
var maxLevelError = false;
var maxLevelKeyPath = "";
helper.extend(opts, defaults, options);
function toYaml(obj) {
o = obj;
var newObj = _preProcessLinks(obj);
if (maxLevelError) {
console.log(opts.maxLevelMessage() + "\nKeypath: " + maxLevelKeyPath)
}
var header = "";
var ret = walkToYaml(newObj);
if (ret !== "") return header + ret + "\n"
else return ""
}
function walkToYaml(obj, l, before) {
if (typeof before === 'undefined') var before = "";
if (typeof l === 'undefined') var l = 0;
//if (typeof obj === 'undefined'){throw new Error('obj undefinend context: before '+before)}
var level = l;
var out = "";
if (opts.enableLinks) {
var ret = searchLink(obj);
//lookup the id of our object in the linksIn linksOut hashes
out += ret.text;
if (ret.stop === true) {
//if it's a link node, we have to stop iteration here
return out;
}
}
if (Object.prototype.toString.call(obj.obj) === '[object Array]') {
var i = 0,
len = obj.obj.length;
for (; i < len; i++) {
//for each thang in the array
out += '\n';
//draw a new line
out += drawIndent(level, 'array');
//draw the indent
out += walkToYaml(obj.obj[i], level + 1, 'array');
//draw the inner thang
}
return out;
}
if (typeof obj.obj === 'object') {
var i,
index = 0;
if (opts.usePadding) {
//preprocess the object to find the perfect indent
var maxKeyLength = 0;
for (i in obj.obj) {
if (!obj.obj.hasOwnProperty(i)) continue;
maxKeyLength = Math.max(i.length, maxKeyLength);
}
}
for (i in obj.obj) {
if (!obj.obj.hasOwnProperty(i)) return out;
if (index === 0 && !opts.yamlCompatible) {
//draw no newline on first item if we don't need to be yaml compatible
} else {
out += '\n';
//draw a new line
out += drawIndent(level, 'object');
}
out += i + ": ";
//draw the attribute name with padding
if (opts.usePadding) {
var padding = maxKeyLength - i.length;
out += drawIndent(padding);
}
out += walkToYaml(obj.obj[i], level + 1, 'object');
//draw the inner thang
index++;
}
//console.log('obj level ', level, 'out ', out);
return out;
}
//if(typeof obj === 'string') return '"'+obj+'"'
out+=obj.obj;
return out;
}
function drawIndent(level, thang) {
var indent = "";
if (typeof thang == 'undefined') {
//level variable is a padding instead
var pad = level;
for (var j = 0; j < pad; j++) {
indent += " ";
}
//identation
return indent;
}
//console.log("drawIndent ", level, thang)
if (thang === "object") {
for (var j = 0; j < level; j++) {
indent += " ";
}
//identation
} else if (thang === "array") {
for (var j = 0; j < level; j++) {
indent += " ";
}
//identation with a -
indent += "- ";
}
return indent;
}
function preProcessLinks(obj) {
o = obj;
return _preProcessLinks(obj);
}
function _preProcessLinks(obj, l, k, kp) {
var level = (typeof l === 'undefined') ? 0: l;
if (typeof k === 'undefined')
k = '',
kp = ''
if (kp !== '')
kp = kp + '.' + k
else
kp = k;
var newObj = {};
newObj.level = level,
newObj.obj = obj;
newObj.key = k
newObj.keyPath = kp;
//the maximum Depth of Iterations may not be exceeded
if (level > opts.maxLevel) {
maxLevelError = true;
maxLevelKeyPath = i + "." + maxLevelKeyPath;
newObj.obj = opts.yamlCompatible ? "" : "[MAX LEVEL REACHED]"
return newObj;
}
// Primitive types cannot have properties
switch (typeof obj) {
case 'undefined':
newObj.obj = opts.yamlCompatible ? "" : "UNDEFINED";
return newObj;
case 'string':
newObj.obj = JSON.stringify(obj).replace(/'/g, "\\'")
.replace(/\\"/g, '"')
.replace(/(^"|"$)/g, "'");
return newObj;
case 'number':
case 'boolean':
newObj.obj = obj;
return newObj;
}
// For some reason typeof null is "object", so special case here.
if (obj === null) {
newObj.obj = "null";
return newObj;
}
// Look up the keys of the object.
var visible_keys = Object.keys(obj);
var keys = opts.showHidden ? Object.getOwnPropertyNames(obj) : visible_keys;
// check for empty objects
/*if (helper.isEmpty(obj)) {
newObj.obj = '';
return newObj;
}*/
// Dates without properties can be shortcutted
if (helper.isDate(obj)) {
//&& keys.length === 0) {
newObj.obj = obj.toUTCString();
return newObj;
}
//its an object or function
//generate a local uId
var uId = uniqueId('object');
//each object gets an id also if its linked
newObj.id = uId;
//check if its a circular link first
//CHECK HERE FOR CIRCLES LEAVE THIS HERE ! OTHERWISE IT WILL PUSH PRIMITIVE OBJECTS ON THE CIRCLE PATH, YOU DON'T WANT THAT!
var idx;
if ((idx = checkCircle(newObj)) > -1) {
return newObj;
}
//check in history if its a link if yes return the obj
//history of onjects and keeps linksIn and links Out of each object
//is link
if ((idx = history(newObj)) > -1) {
return newObj;
}
// all Functions get shortcutted right now
if (typeof obj === 'function' ){//&& keys.length === 0) {
if (helper.isRegExp(obj)) {
newObj.obj = opts.yamlCompatible ? "" : '[regexp]';
newObj.type='function'
return newObj;
} else {
var name = obj.name ? ': ' + obj.name : '';
newObj.obj = opts.yamlCompatible ? "" : '[Function' + name + ']';
newObj.type='function'
return newObj;
}
}
if (Object.prototype.toString.call(obj) === '[object Array]') {
newObj.obj = [];
var i = 0,
len = obj.length;
//newObj.obj = [];
for (; i < len; i++) {
//for each thang in the array
newObj.obj.push(_preProcessLinks(obj[i], level + 1, i, kp));
//copy on without change
}
removeFromCirclePath(newObj);
return newObj;
}
if (typeof obj === 'object') {
newObj.obj = {};
var i,
index = 0;
for (i in obj) {
if (!obj.hasOwnProperty(i)) continue;
//newObj.obj[i]={};
//newObj.obj[i].key=i;
newObj.obj[i] = _preProcessLinks(obj[i], level + 1, i, kp);
//draw the inner thang
index++;
}
//pop the object from path before returning
removeFromCirclePath(newObj);
return newObj;
}
}
function searchLink(newObj) {
//id
if (newObj.ref) return {
'text': '&' + newObj.id,
'stop': false
};
//a reference
if (newObj.link) return {
'text': '*' + newObj.link,
'stop': true
};
//a link
return {
'text': "",
'stop': false
}
}
function checkCircle(newObj) {
var idx;
if ((idx = oPath.indexOf(newObj.obj)) > -1) {
var refObj = Path[idx];
//if (typeof refObj.obj !== "function") {
//for now don't support functions
refObj.ref = true;
if (typeof refObj.refObjects == "undefined") refObj.refObjects = [];
if (typeof refObj.circleObjects == "undefined") refObj.circleObjects = [];
refObj.refObjects.push(newObj);
refObj.circleObjects.push(newObj);
newObj.link = refObj.id;
newObj.obj = refObj.obj;
//}
return idx;
//if its a circle dont push on path
}
Path.push(newObj);
//"push" object to the Path
oPath.push(newObj.obj);
return - 1;
}
function removeFromCirclePath(obj) {
Path.pop();
oPath.pop();
return obj;
}
function history(newObj) {
//search for links in the history
var idx = -1;
if ((idx = obja.indexOf(newObj.obj)) > -1) {
//loop
var key = newoba[idx].id;
//hash lookup
var refObj = newoba[idx];
//hash lookup
//if (typeof refObj.obj !== "function") {
//for now don't support functions
refObj.ref = true;
if (typeof refObj.refObjects == "undefined") refObj.refObjects = [];
refObj.refObjects.push(newObj);
//linkHashOut[newObj.id]=key;//this dock is for new objects link to first object = the links
newObj.link = key;
newObj.obj = refObj.obj;
//}
return idx;
//if its a link dont push on history
}
//create a new history object
obja.push(newObj.obj);
newoba.push(newObj);
return idx;
}
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
function uniqueId(prefix) {
var id = idCounter++;
return prefix ? prefix + id : id;
}
//closure functions
return {
'toYaml': toYaml,
'preProcessLinks': preProcessLinks
}
}
//end serializer
var helper={}
helper.isArray = function isArray(ar) {
return ar instanceof Array ||
Array.isArray(ar) ||
(ar && ar !== Object.prototype && isArray(ar.__proto__));
}
helper.isRegExp = function isRegExp(re) {
var s = '' + re;
return re instanceof RegExp || // easy case
// duck-type for context-switching evalcx case
typeof(re) === 'function' &&
re.constructor.name === 'RegExp' &&
re.compile &&
re.test &&
re.exec &&
s.match(/^\/.*\/[gim]{0,3}$/);
}
helper.isDate = function isDate(d) {
if (d instanceof Date) return true;
if (typeof d !== 'object') return false;
var properties = Date.prototype && Object.getOwnPropertyNames(Date.prototype);
var proto = d.__proto__ && Object.getOwnPropertyNames(d.__proto__);
return JSON.stringify(proto) === JSON.stringify(properties);
}
helper.isString = function isString(obj) {
return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
}
// Is a given object empty?
helper.isEmpty = function isEmpty(obj) {
if (helper.isArray(obj) || helper.isString(obj)) return obj.length === 0;
for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
return true;
};
// Extend a given object with all the properties in passed-in object(s).
helper.extend = function extend(obj) {
Array.prototype.slice.call(arguments, 1).forEach(function(source) {
for (var prop in source) obj[prop] = source[prop];
});
return obj;
}