-
Notifications
You must be signed in to change notification settings - Fork 0
/
JFON.js
171 lines (132 loc) · 4.52 KB
/
JFON.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
// //\\// Beaver Tool Belt. BTB. Light weight JavaScript framework.
// See jfon.readme below.
// At a glance, this line { f : function () {}, ar : some-array, wrap : x } ...
// converts to
// {
// f: { wrap : { fun : "function () ..." },
// ar : { wrap : { arr : some-array-"naked" }, prop1 : ..., prop2 : ... } // where prop1, ... are properties of some-array
// wrap : { esc : x },
// ....
//
// Hence, only these combinations of "wrap" property are possible in final result:
// wrap : { fun ...;
// wrap : { arr ...;
// wrap : { esc ...;
//
//
// CAVEATS : When serializing a funciton, be sure it does not have a "live" closure.
// JFON is not serializing closures, so closure cannot be transported via JFON.
//
// Discussion: http://stackoverflow.com/questions/16767431/parsing-functions-stored-as-strings-in-object-literal-json-syntax-and-differetia
// http://perfectionkills.com/global-eval-what-are-the-options/
// where to post this code: http://stackoverflow.com/questions/11556131/parsing-a-json-like-string-that-contains-functions?lq=1
//
( function ( window ) {
//. sets namespace
var btb = window.btb$ = window.btb$ || {};
//. sets namespace for JFON
var jfon = btb.jfon = btb.jfon || {};
jfon.readme = "JavaScript Full Object Notation.\n\n" +
"Version: 0.0.1. October 2, 2013.\n" +
"Copyright: (c) 2013 Konstantin Kirillov. License: MIT.\n" +
"Adds: \"extra properties\" to JSON: storing functions and full array properties.\n" +
"Method: reserves property name \"wrap\" to wrap extra properties.\n" +
"Objects: without \"extra properties\" converted to original JSON.\n" +
"Format: is self-explanatory from test-samples.\n" +
"More: comments are in JFON.js\n\n";
// TODM Bad test. Use "Array protot" instead.
var arrayDetector = function ( obj ) { return ( typeof obj === 'object' ) && ( !!obj.length || obj.length === 0 ); };
var hasOwn = Object.prototype.hasOwnProperty;
var detectIntIndex = /^\-?[0-9]+$/;
var undefined = ( function ( undefined ) { return undefined; } ) ();
// //\\ Prepares object to be JSONed
// It skips elements with typeof element = 'undefined'.
var toJFON = jfon.toJFON = function ( paper, recdepth, level )
{
level = level || 0;
var typ = typeof paper;
//. returns "plain" objects
if( paper === null || ( typ !== 'object' && typ !== 'function' ) ) return paper;
//. exterminates properties below restricted depth; TODM slow?
if( ( recdepth || recdepth === 0 ) && level > recdepth ) return undefined;
var wall = null;
var wrap = null;
if( typ === 'function' )
{
wrap = wrap || {};
wrap.fun = paper.toString();
//. as of this version, ignores properties of function()
return { wrap : wrap };
}
if( hasOwn.call( paper, 'wrap' ) )
{
wrap = wrap || {};
wrap.esc = toJFON( paper.wrap, recdepth, level + 1 );
}
var arr = arrayDetector( paper ) && [];
for( var pp in paper )
{
if( hasOwn.call( paper, pp ) )
{
var theValue = toJFON( paper[ pp ], recdepth, level + 1 );
if( typeof theValue === 'undefined' ) continue;
if( arr && detectIntIndex.test( pp + '' ) )
{
arr[ pp ] = theValue;
}else if( pp !== 'wrap' ) {
wall = wall || {};
wall[ pp ] = theValue;
}
}
}
if( arr && !wall && !wrap ) wall = arr;
wall = wall || {};
if( arr || wrap )
{
wrap = wrap || {};
wall.wrap = wrap;
if( arr) wall.wrap.arr = arr;
}
return wall;
};
/// Makes final step in recreating original object from "wrapper"
var fromJFON = btb.fromJFON = function ( paper )
{
var typ = typeof paper;
if( paper === null || ( typ !== 'object' && typ !== 'function' ) ) return paper;
var wall = {};
var wrap = paper.wrap;
if( wrap )
{
if( wrap.fun )
{
wall = eval( '(' + wrap.fun + ')' );
}else if( wrap.arr ) {
wall = fromJFON( wrap.arr );
}
//. restores wrap property and keeps wrap alive
if( wrap.esc ) wall.wrap = fromJFON( wrap.esc );
}else if( arrayDetector( paper ) ) {
wall = [];
}
/// transfers array and object properties in one loop
for( var pp in paper )
{
if( hasOwn.call( paper, pp ) && pp !== 'wrap' )
{
wall[ pp ] = fromJFON( paper[ pp ] );
}
}
return wall;
};
// //\\ API methods
jfon.encode = function ( obj )
{
return JSON.stringify( toJFON( obj ), null, '\t');
};
jfon.decode = function ( obj )
{
return fromJFON( JSON.parse( obj ) );
};
// \\// API methods
}) ( window );