rjr 0.12.2 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +49 -36
- data/Rakefile +2 -0
- data/bin/rjr-client +11 -9
- data/bin/rjr-server +12 -10
- data/examples/amqp.rb +29 -0
- data/examples/client.rb +32 -0
- data/examples/complete.rb +36 -0
- data/examples/local.rb +29 -0
- data/examples/server.rb +26 -0
- data/examples/tcp.rb +29 -0
- data/examples/web.rb +22 -0
- data/examples/ws.rb +29 -0
- data/lib/rjr/common.rb +7 -12
- data/lib/rjr/dispatcher.rb +171 -239
- data/lib/rjr/em_adapter.rb +33 -66
- data/lib/rjr/message.rb +43 -12
- data/lib/rjr/node.rb +197 -103
- data/lib/rjr/nodes/amqp.rb +216 -0
- data/lib/rjr/nodes/easy.rb +159 -0
- data/lib/rjr/nodes/local.rb +118 -0
- data/lib/rjr/{missing_node.rb → nodes/missing.rb} +4 -2
- data/lib/rjr/nodes/multi.rb +79 -0
- data/lib/rjr/nodes/tcp.rb +211 -0
- data/lib/rjr/nodes/web.rb +197 -0
- data/lib/rjr/nodes/ws.rb +187 -0
- data/lib/rjr/stats.rb +70 -0
- data/lib/rjr/thread_pool.rb +178 -123
- data/site/index.html +45 -0
- data/site/jquery-latest.js +9404 -0
- data/site/jrw.js +297 -0
- data/site/json.js +199 -0
- data/specs/dispatcher_spec.rb +244 -198
- data/specs/em_adapter_spec.rb +52 -80
- data/specs/message_spec.rb +223 -197
- data/specs/node_spec.rb +67 -163
- data/specs/nodes/amqp_spec.rb +82 -0
- data/specs/nodes/easy_spec.rb +13 -0
- data/specs/nodes/local_spec.rb +72 -0
- data/specs/nodes/multi_spec.rb +65 -0
- data/specs/nodes/tcp_spec.rb +75 -0
- data/specs/nodes/web_spec.rb +77 -0
- data/specs/nodes/ws_spec.rb +78 -0
- data/specs/stats_spec.rb +59 -0
- data/specs/thread_pool_spec.rb +44 -35
- metadata +40 -30
- data/lib/rjr/amqp_node.rb +0 -330
- data/lib/rjr/inspect.rb +0 -65
- data/lib/rjr/local_node.rb +0 -150
- data/lib/rjr/multi_node.rb +0 -65
- data/lib/rjr/tcp_node.rb +0 -323
- data/lib/rjr/thread_pool2.rb +0 -272
- data/lib/rjr/util.rb +0 -104
- data/lib/rjr/web_node.rb +0 -266
- data/lib/rjr/ws_node.rb +0 -289
- data/lib/rjr.rb +0 -16
- data/specs/amqp_node_spec.rb +0 -31
- data/specs/inspect_spec.rb +0 -60
- data/specs/local_node_spec.rb +0 -43
- data/specs/multi_node_spec.rb +0 -45
- data/specs/tcp_node_spec.rb +0 -33
- data/specs/util_spec.rb +0 -46
- data/specs/web_node_spec.rb +0 -32
- data/specs/ws_node_spec.rb +0 -32
- /data/lib/rjr/{tcp_node2.rb → nodes/tcp2.rb} +0 -0
- /data/lib/rjr/{udp_node.rb → nodes/udp.rb} +0 -0
data/site/jrw.js
ADDED
@@ -0,0 +1,297 @@
|
|
1
|
+
/* Json-Rpc over HTTP and Websockets
|
2
|
+
*
|
3
|
+
* Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
|
4
|
+
* Licensed under the Apache License, Version 2.0
|
5
|
+
*/
|
6
|
+
|
7
|
+
///////////////////////////////////////////////////////
|
8
|
+
// Helpers to generate a uuid
|
9
|
+
function S4() {
|
10
|
+
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
|
11
|
+
}
|
12
|
+
function guid() {
|
13
|
+
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
|
14
|
+
}
|
15
|
+
|
16
|
+
///////////////////////////////////////////////////////
|
17
|
+
// JRMessage
|
18
|
+
|
19
|
+
// Encapsulates a json-rpc message
|
20
|
+
function JRMessage(){
|
21
|
+
this.id = null;
|
22
|
+
this.rpc_method = null;
|
23
|
+
this.args = null;
|
24
|
+
|
25
|
+
this.data = null;
|
26
|
+
this.json = null;
|
27
|
+
|
28
|
+
this.error = null;
|
29
|
+
this.result = null;
|
30
|
+
|
31
|
+
this.onresponse = null;
|
32
|
+
this.success = null;
|
33
|
+
|
34
|
+
this.to_json = function(){
|
35
|
+
this.json = $.toJSON(this.data);
|
36
|
+
return this.json;
|
37
|
+
}
|
38
|
+
|
39
|
+
this.handle_response = function(res){
|
40
|
+
res.success = (res.error == undefined);
|
41
|
+
if(this.onresponse)
|
42
|
+
this.onresponse(res);
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
// Create new request message to send
|
47
|
+
JRMessage.new_request = function(rpc_method, args){
|
48
|
+
var msg = new JRMessage();
|
49
|
+
msg.id = guid();
|
50
|
+
msg.rpc_method = rpc_method;
|
51
|
+
msg.args = args;
|
52
|
+
msg.data = {jsonrpc: '2.0',
|
53
|
+
method: msg.rpc_method,
|
54
|
+
params: msg.args,
|
55
|
+
id: msg.id};
|
56
|
+
return msg;
|
57
|
+
}
|
58
|
+
|
59
|
+
// Internal helper to generate request from common args.
|
60
|
+
//
|
61
|
+
// rpc method, parameter list, and optional response callback
|
62
|
+
// will be extracted from the 'args' param in that order.
|
63
|
+
//
|
64
|
+
// node_id and headers will be set on request message before it
|
65
|
+
// is returned
|
66
|
+
JRMessage.pretty_request = function(args, node_id, headers){
|
67
|
+
// create request message
|
68
|
+
var rpc_method = args[0];
|
69
|
+
var params = [];
|
70
|
+
var cb = null;
|
71
|
+
for(a = 1; a < args.length; a++){
|
72
|
+
if(a == args.length - 1 && typeof args[a] === 'function')
|
73
|
+
cb = args[a];
|
74
|
+
else
|
75
|
+
params.push(args[a]);
|
76
|
+
}
|
77
|
+
var req = JRMessage.new_request(rpc_method, params);
|
78
|
+
if(node_id) req.data['node_id'] = node_id;
|
79
|
+
for(var header in headers)
|
80
|
+
req.data[header] = headers[header];
|
81
|
+
|
82
|
+
// register callback if last argument is a function
|
83
|
+
if(cb) req.onresponse = cb;
|
84
|
+
|
85
|
+
return req;
|
86
|
+
}
|
87
|
+
|
88
|
+
// Parse message received in json string
|
89
|
+
JRMessage.from_msg = function(dat){
|
90
|
+
var msg = new JRMessage();
|
91
|
+
msg.json = dat;
|
92
|
+
msg.data = $.evalJSON(dat);
|
93
|
+
|
94
|
+
msg.id = msg.data['id'];
|
95
|
+
msg.rpc_method = msg.data['method'];
|
96
|
+
if(msg.data['params']){
|
97
|
+
msg.params = msg.data['params'];
|
98
|
+
for(p=0;p<msg.params.length;++p){
|
99
|
+
if(JRObject.is_jrobject(msg.params[p]))
|
100
|
+
msg.params[p] = JRObject.from_json(msg.params[p]);
|
101
|
+
else if(JRObject.is_jrobject_array(msg.params[p]))
|
102
|
+
msg.params[p] = JRObject.from_json_array(msg.params[p]);
|
103
|
+
}
|
104
|
+
}
|
105
|
+
msg.error = msg.data['error'];
|
106
|
+
msg.result = msg.data['result'];
|
107
|
+
if(msg.result && JRObject.is_jrobject(msg.result))
|
108
|
+
msg.result = JRObject.from_json(msg.result);
|
109
|
+
else if(JRObject.is_jrobject_array(msg.result))
|
110
|
+
msg.result = JRObject.from_json_array(msg.result);
|
111
|
+
return msg;
|
112
|
+
}
|
113
|
+
|
114
|
+
///////////////////////////////////////////////////////
|
115
|
+
// JRObject
|
116
|
+
|
117
|
+
// Encapsulates an object w/ type
|
118
|
+
// - adaptor for the ruby 'json' library
|
119
|
+
function JRObject (type, value, ignore_properties){
|
120
|
+
this.type = type;
|
121
|
+
this.value = value;
|
122
|
+
this.ignore_properties = (typeof(ignore_properties) != 'undefined') ?
|
123
|
+
ignore_properties : ["toJSON"];
|
124
|
+
this.toJSON = function(){
|
125
|
+
var data = {};
|
126
|
+
for(p in this.value)
|
127
|
+
if($.inArray(p, this.ignore_properties) == -1)
|
128
|
+
data[p] = value[p];
|
129
|
+
return {json_class: this.type, data: data };
|
130
|
+
};
|
131
|
+
};
|
132
|
+
|
133
|
+
// Return boolean indicating if json contains encapsulated JRObject
|
134
|
+
JRObject.is_jrobject = function(json){
|
135
|
+
return json && json['json_class'] && json['data'];
|
136
|
+
};
|
137
|
+
|
138
|
+
// Return boolean indicating if json contains array of encapsulated JRObjects
|
139
|
+
JRObject.is_jrobject_array = function(json){
|
140
|
+
return json && typeof(json) == "object" && json.length > 0 && JRObject.is_jrobject(json[0]);
|
141
|
+
};
|
142
|
+
|
143
|
+
// Convert json to JRObject
|
144
|
+
JRObject.from_json = function(json){
|
145
|
+
var obj = json['data'];
|
146
|
+
obj.json_class = json['json_class'];
|
147
|
+
for(var p in obj){
|
148
|
+
if(JRObject.is_jrobject(obj[p]))
|
149
|
+
obj[p] = JRObject.from_json(obj[p]);
|
150
|
+
else if(JRObject.is_jrobject_array(obj[p])){
|
151
|
+
obj[p] = JRObject.from_json_array(obj[p]);
|
152
|
+
}
|
153
|
+
}
|
154
|
+
return obj;
|
155
|
+
};
|
156
|
+
|
157
|
+
// Convert json to array of JRObjects
|
158
|
+
JRObject.from_json_array = function(json){
|
159
|
+
var objs = [];
|
160
|
+
for(var i in json)
|
161
|
+
if(JRObject.is_jrobject(json[i]))
|
162
|
+
objs[i] = JRObject.from_json(json[i]);
|
163
|
+
return objs;
|
164
|
+
};
|
165
|
+
|
166
|
+
///////////////////////////////////////////////////////
|
167
|
+
// WSNode
|
168
|
+
|
169
|
+
// Main json-rpc client websocket interface
|
170
|
+
function WSNode (host, port){
|
171
|
+
var node = this;
|
172
|
+
this.opened = false;
|
173
|
+
this.node_id = null;
|
174
|
+
this.headers = {};
|
175
|
+
this.messages = {};
|
176
|
+
|
177
|
+
// Open socket connection
|
178
|
+
this.open = function(){
|
179
|
+
node.socket = new WebSocket("ws://" + host + ":" + port);
|
180
|
+
|
181
|
+
node.socket.onclose = function (){
|
182
|
+
if(node.onclose)
|
183
|
+
node.onclose();
|
184
|
+
};
|
185
|
+
|
186
|
+
node.socket.onmessage = function (evnt){
|
187
|
+
var msg = JRMessage.from_msg(evnt.data);
|
188
|
+
|
189
|
+
// match response w/ outstanding request
|
190
|
+
if(msg.id){
|
191
|
+
var req = node.messages[msg.id];
|
192
|
+
delete node.messages[msg.id];
|
193
|
+
req.handle_response(msg)
|
194
|
+
|
195
|
+
// if err msg, run node.onerror
|
196
|
+
if(msg.error)
|
197
|
+
if(node.onerror)
|
198
|
+
node.onerror(msg)
|
199
|
+
|
200
|
+
}else{
|
201
|
+
// relying on clients to handle notifications via message_received
|
202
|
+
// TODO add notification (and request?) handler support here
|
203
|
+
//node.invoke_method(msg.rpc_method, msg.params)
|
204
|
+
if(node.message_received)
|
205
|
+
node.message_received(msg);
|
206
|
+
|
207
|
+
}
|
208
|
+
};
|
209
|
+
|
210
|
+
node.socket.onerror = function(e){
|
211
|
+
if(node.onerror)
|
212
|
+
node.onerror(e);
|
213
|
+
}
|
214
|
+
|
215
|
+
node.socket.onopen = function (){
|
216
|
+
// send queued messages
|
217
|
+
for(var m in node.messages)
|
218
|
+
node.socket.send(node.messages[m].to_json());
|
219
|
+
|
220
|
+
node.opened = true;
|
221
|
+
|
222
|
+
// invoke client callback
|
223
|
+
if(node.onopen)
|
224
|
+
node.onopen();
|
225
|
+
};
|
226
|
+
};
|
227
|
+
|
228
|
+
// Close socket connection
|
229
|
+
this.close = function(){
|
230
|
+
this.socket.close();
|
231
|
+
};
|
232
|
+
|
233
|
+
// Invoke request on socket, may be invoked before or after socket is opened.
|
234
|
+
//
|
235
|
+
// Pass in the rpc method, arguments to invoke method with, and optional callback
|
236
|
+
// to be invoked upon received response.
|
237
|
+
this.invoke = function(){
|
238
|
+
var req = JRMessage.pretty_request(arguments, this.node_id, this.headers);
|
239
|
+
|
240
|
+
// store requests for later retrieval
|
241
|
+
this.messages[req.id] = req;
|
242
|
+
|
243
|
+
if(node.opened)
|
244
|
+
this.socket.send(req.to_json());
|
245
|
+
|
246
|
+
return req;
|
247
|
+
};
|
248
|
+
};
|
249
|
+
|
250
|
+
///////////////////////////////////////////////////////
|
251
|
+
// WebNode
|
252
|
+
|
253
|
+
// Main json-rpc www interface
|
254
|
+
function WebNode (uri){
|
255
|
+
var node = this;
|
256
|
+
this.node_id = null;
|
257
|
+
this.headers = {};
|
258
|
+
|
259
|
+
// Invoke request via http
|
260
|
+
//
|
261
|
+
// Pass in the rpc method, arguments to invoke method with, and optional callback
|
262
|
+
// to be invoked upon received response.
|
263
|
+
this.invoke = function(){
|
264
|
+
var req = JRMessage.pretty_request(arguments, this.node_id, this.headers);
|
265
|
+
|
266
|
+
$.ajax({type: 'POST',
|
267
|
+
url: uri,
|
268
|
+
data: req.to_json(),
|
269
|
+
dataType: 'text', // using text so we can parse json ourselves
|
270
|
+
|
271
|
+
success: function(data){
|
272
|
+
var msg = JRMessage.from_msg(data);
|
273
|
+
// js web client doesn't support notifications
|
274
|
+
//if(node.message_received)
|
275
|
+
//node.message_received(msg);
|
276
|
+
|
277
|
+
req.handle_response(msg)
|
278
|
+
|
279
|
+
// if err msg, run node.onerror
|
280
|
+
if(msg.error)
|
281
|
+
if(node.onerror)
|
282
|
+
node.onerror(msg);
|
283
|
+
},
|
284
|
+
|
285
|
+
error: function(jqXHR, textStatus, errorThrown){
|
286
|
+
var err = { 'error' : {'code' : jqXHR.status,
|
287
|
+
'message' : textStatus,
|
288
|
+
'class' : errorThrown } };
|
289
|
+
if(node.onerror)
|
290
|
+
node.onerror(err);
|
291
|
+
|
292
|
+
req.handle_response(err)
|
293
|
+
}});
|
294
|
+
|
295
|
+
return req;
|
296
|
+
};
|
297
|
+
};
|
data/site/json.js
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
/**
|
2
|
+
* jQuery JSON plugin 2.4.0
|
3
|
+
*
|
4
|
+
* @author Brantley Harris, 2009-2011
|
5
|
+
* @author Timo Tijhof, 2011-2012
|
6
|
+
* @source This plugin is heavily influenced by MochiKit's serializeJSON, which is
|
7
|
+
* copyrighted 2005 by Bob Ippolito.
|
8
|
+
* @source Brantley Harris wrote this plugin. It is based somewhat on the JSON.org
|
9
|
+
* website's http://www.json.org/json2.js, which proclaims:
|
10
|
+
* "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
|
11
|
+
* I uphold.
|
12
|
+
* @license MIT License <http://www.opensource.org/licenses/mit-license.php>
|
13
|
+
*/
|
14
|
+
(function ($) {
|
15
|
+
'use strict';
|
16
|
+
|
17
|
+
var escape = /["\\\x00-\x1f\x7f-\x9f]/g,
|
18
|
+
meta = {
|
19
|
+
'\b': '\\b',
|
20
|
+
'\t': '\\t',
|
21
|
+
'\n': '\\n',
|
22
|
+
'\f': '\\f',
|
23
|
+
'\r': '\\r',
|
24
|
+
'"' : '\\"',
|
25
|
+
'\\': '\\\\'
|
26
|
+
},
|
27
|
+
hasOwn = Object.prototype.hasOwnProperty;
|
28
|
+
|
29
|
+
/**
|
30
|
+
* jQuery.toJSON
|
31
|
+
* Converts the given argument into a JSON representation.
|
32
|
+
*
|
33
|
+
* @param o {Mixed} The json-serializable *thing* to be converted
|
34
|
+
*
|
35
|
+
* If an object has a toJSON prototype, that will be used to get the representation.
|
36
|
+
* Non-integer/string keys are skipped in the object, as are keys that point to a
|
37
|
+
* function.
|
38
|
+
*
|
39
|
+
*/
|
40
|
+
$.toJSON = typeof JSON === 'object' && JSON.stringify ? JSON.stringify : function (o) {
|
41
|
+
if (o === null) {
|
42
|
+
return 'null';
|
43
|
+
}
|
44
|
+
|
45
|
+
var pairs, k, name, val,
|
46
|
+
type = $.type(o);
|
47
|
+
|
48
|
+
if (type === 'undefined') {
|
49
|
+
return undefined;
|
50
|
+
}
|
51
|
+
|
52
|
+
// Also covers instantiated Number and Boolean objects,
|
53
|
+
// which are typeof 'object' but thanks to $.type, we
|
54
|
+
// catch them here. I don't know whether it is right
|
55
|
+
// or wrong that instantiated primitives are not
|
56
|
+
// exported to JSON as an {"object":..}.
|
57
|
+
// We choose this path because that's what the browsers did.
|
58
|
+
if (type === 'number' || type === 'boolean') {
|
59
|
+
return String(o);
|
60
|
+
}
|
61
|
+
if (type === 'string') {
|
62
|
+
return $.quoteString(o);
|
63
|
+
}
|
64
|
+
if (typeof o.toJSON === 'function') {
|
65
|
+
return $.toJSON(o.toJSON());
|
66
|
+
}
|
67
|
+
if (type === 'date') {
|
68
|
+
var month = o.getUTCMonth() + 1,
|
69
|
+
day = o.getUTCDate(),
|
70
|
+
year = o.getUTCFullYear(),
|
71
|
+
hours = o.getUTCHours(),
|
72
|
+
minutes = o.getUTCMinutes(),
|
73
|
+
seconds = o.getUTCSeconds(),
|
74
|
+
milli = o.getUTCMilliseconds();
|
75
|
+
|
76
|
+
if (month < 10) {
|
77
|
+
month = '0' + month;
|
78
|
+
}
|
79
|
+
if (day < 10) {
|
80
|
+
day = '0' + day;
|
81
|
+
}
|
82
|
+
if (hours < 10) {
|
83
|
+
hours = '0' + hours;
|
84
|
+
}
|
85
|
+
if (minutes < 10) {
|
86
|
+
minutes = '0' + minutes;
|
87
|
+
}
|
88
|
+
if (seconds < 10) {
|
89
|
+
seconds = '0' + seconds;
|
90
|
+
}
|
91
|
+
if (milli < 100) {
|
92
|
+
milli = '0' + milli;
|
93
|
+
}
|
94
|
+
if (milli < 10) {
|
95
|
+
milli = '0' + milli;
|
96
|
+
}
|
97
|
+
return '"' + year + '-' + month + '-' + day + 'T' +
|
98
|
+
hours + ':' + minutes + ':' + seconds +
|
99
|
+
'.' + milli + 'Z"';
|
100
|
+
}
|
101
|
+
|
102
|
+
pairs = [];
|
103
|
+
|
104
|
+
if ($.isArray(o)) {
|
105
|
+
for (k = 0; k < o.length; k++) {
|
106
|
+
pairs.push($.toJSON(o[k]) || 'null');
|
107
|
+
}
|
108
|
+
return '[' + pairs.join(',') + ']';
|
109
|
+
}
|
110
|
+
|
111
|
+
// Any other object (plain object, RegExp, ..)
|
112
|
+
// Need to do typeof instead of $.type, because we also
|
113
|
+
// want to catch non-plain objects.
|
114
|
+
if (typeof o === 'object') {
|
115
|
+
for (k in o) {
|
116
|
+
// Only include own properties,
|
117
|
+
// Filter out inherited prototypes
|
118
|
+
if (hasOwn.call(o, k)) {
|
119
|
+
// Keys must be numerical or string. Skip others
|
120
|
+
type = typeof k;
|
121
|
+
if (type === 'number') {
|
122
|
+
name = '"' + k + '"';
|
123
|
+
} else if (type === 'string') {
|
124
|
+
name = $.quoteString(k);
|
125
|
+
} else {
|
126
|
+
continue;
|
127
|
+
}
|
128
|
+
type = typeof o[k];
|
129
|
+
|
130
|
+
// Invalid values like these return undefined
|
131
|
+
// from toJSON, however those object members
|
132
|
+
// shouldn't be included in the JSON string at all.
|
133
|
+
if (type !== 'function' && type !== 'undefined') {
|
134
|
+
val = $.toJSON(o[k]);
|
135
|
+
pairs.push(name + ':' + val);
|
136
|
+
}
|
137
|
+
}
|
138
|
+
}
|
139
|
+
return '{' + pairs.join(',') + '}';
|
140
|
+
}
|
141
|
+
};
|
142
|
+
|
143
|
+
/**
|
144
|
+
* jQuery.evalJSON
|
145
|
+
* Evaluates a given json string.
|
146
|
+
*
|
147
|
+
* @param str {String}
|
148
|
+
*/
|
149
|
+
$.evalJSON = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
|
150
|
+
/*jshint evil: true */
|
151
|
+
return eval('(' + str + ')');
|
152
|
+
};
|
153
|
+
|
154
|
+
/**
|
155
|
+
* jQuery.secureEvalJSON
|
156
|
+
* Evals JSON in a way that is *more* secure.
|
157
|
+
*
|
158
|
+
* @param str {String}
|
159
|
+
*/
|
160
|
+
$.secureEvalJSON = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
|
161
|
+
var filtered =
|
162
|
+
str
|
163
|
+
.replace(/\\["\\\/bfnrtu]/g, '@')
|
164
|
+
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
|
165
|
+
.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
|
166
|
+
|
167
|
+
if (/^[\],:{}\s]*$/.test(filtered)) {
|
168
|
+
/*jshint evil: true */
|
169
|
+
return eval('(' + str + ')');
|
170
|
+
}
|
171
|
+
throw new SyntaxError('Error parsing JSON, source is not valid.');
|
172
|
+
};
|
173
|
+
|
174
|
+
/**
|
175
|
+
* jQuery.quoteString
|
176
|
+
* Returns a string-repr of a string, escaping quotes intelligently.
|
177
|
+
* Mostly a support function for toJSON.
|
178
|
+
* Examples:
|
179
|
+
* >>> jQuery.quoteString('apple')
|
180
|
+
* "apple"
|
181
|
+
*
|
182
|
+
* >>> jQuery.quoteString('"Where are we going?", she asked.')
|
183
|
+
* "\"Where are we going?\", she asked."
|
184
|
+
*/
|
185
|
+
$.quoteString = function (str) {
|
186
|
+
if (str.match(escape)) {
|
187
|
+
return '"' + str.replace(escape, function (a) {
|
188
|
+
var c = meta[a];
|
189
|
+
if (typeof c === 'string') {
|
190
|
+
return c;
|
191
|
+
}
|
192
|
+
c = a.charCodeAt();
|
193
|
+
return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
|
194
|
+
}) + '"';
|
195
|
+
}
|
196
|
+
return '"' + str + '"';
|
197
|
+
};
|
198
|
+
|
199
|
+
}(jQuery));
|