jsc 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/TODO.org ADDED
@@ -0,0 +1,12 @@
1
+ * jsc
2
+ ** colourful output in shell!
3
+ ** come far uscire un carattere sotto la lettera dove c'è l'errore
4
+ ** ann with bones + ann on ruby-talk?
5
+ ** test jsc bin file
6
+ (15:02:14) @jabber.org: eh appunto con cucumber
7
+ (15:02:23) @jabber.org: guarda le features di rake-compiler
8
+ (15:02:28) @jabber.org: sono scritte bene
9
+ (15:02:29) @jabber.org: oppure
10
+ (15:02:36) @jabber.org: quelle di ffi-swig-generator
11
+ oppure con aruba http://github.com/aslakhellesoy/aruba
12
+ ** newgem http://newgem.rubyforge.org/ o jeweler http://github.com/technicalpickles/jeweler
data/bin/#jsc# ADDED
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'getoptlong'
4
+ require 'rdoc/usage'
5
+
6
+ require File.expand_path(
7
+ File.join(File.dirname(__FILE__), %w[.. lib jsc]))
8
+
9
+ USAGE_PREAMBLE = <<-EOU
10
+
11
+ jsc, JavaScript Compiler.
12
+
13
+ This command compiles your JavaScript code throught Google Closure Compiler Service.
14
+ Look at http://gemcutter.org/gems/jsc for more info.
15
+
16
+ == Usage
17
+
18
+ jsc [options] ... ARG
19
+
20
+ --code x, -c x:
21
+ compile code x
22
+
23
+ --errors, -e:
24
+ check for errors
25
+
26
+ --warns, -w:
27
+ check for warnings
28
+
29
+ --stats, -s:
30
+ get statistics for compiled code
31
+
32
+ --all, -a:
33
+ execute every check for this code
34
+
35
+ --level value, -l value:
36
+ compile with level value
37
+ If this option is not supplied, SIMPLE_OPTIMIZATIONS will be used
38
+ (look at Google API for accepted values).
39
+
40
+ --version, -v:
41
+ show version
42
+
43
+ --help, -h:
44
+ this help
45
+
46
+ ARG: A path to a javascript file or the code that will be compiled.
47
+
48
+ If one of the following options is not specified
49
+
50
+ -e, -w, -s, -a
51
+
52
+ the code will be first compiled for errors and only if no errors are found
53
+ it will return the compiled code
54
+ EOU
55
+
56
+ opts = GetoptLong.new(
57
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
58
+ [ '--version', '-v', GetoptLong::NO_ARGUMENT ],
59
+ [ '--code', '-c', GetoptLong::OPTIONAL_ARGUMENT ],
60
+ [ '--errors', '-e', GetoptLong::NO_ARGUMENT ],
61
+ [ '--warns', '-w', GetoptLong::NO_ARGUMENT ],
62
+ [ '--stats', '-s', GetoptLong::NO_ARGUMENT ],
63
+ [ '--all', '-a', GetoptLong::NO_ARGUMENT ],
64
+ [ '--level','-l', GetoptLong::REQUIRED_ARGUMENT ]
65
+ )
66
+
67
+ file = true
68
+ output_info, level, code = ""
69
+
70
+ opts.each do |opt, arg|
71
+ case opt
72
+ when '--help'
73
+ puts USAGE_PREAMBLE
74
+ exit 0
75
+ when '--version'
76
+ puts JSCompiler::VERSION
77
+ exit 0
78
+ when '--code'
79
+ file = false
80
+ code = arg
81
+ when '--level'
82
+ if arg == ''
83
+ level = "SIMPLE_OPTIMIZATIONS"
84
+ else
85
+ level = arg
86
+ end
87
+ when '--errors'
88
+ output_info = "errors"
89
+ when '--warns'
90
+ output_info = "warnings"
91
+ when '--stats'
92
+ output_info = "statistics"
93
+ when '--all'
94
+ output_info = "all"
95
+ end
96
+ end
97
+
98
+ # -f option or CODE arg requested
99
+ unless ARGV.length > 0 or code
100
+ puts "Missing any argume
101
+
102
+ nt (try --help)"
103
+ exit 0
104
+ end
105
+
106
+ arg = code.blank? ? ARGV.shift : code
107
+
108
+ if output_info.eql?("all")
109
+ puts JSCompiler.full_compile(arg, file, level)
110
+ elsif output_info.blank?
111
+ errors_output = JSCompiler.compile(arg, file, "errors", level)
112
+ unless errors_output.eql?("No errors")
113
+ puts errors_output
114
+ else
115
+ puts JSCompiler.compile(arg, file, "", level)
116
+ end
117
+ else
118
+ puts JSCompiler.compile(arg, file, output_info, level)
119
+ end
@@ -0,0 +1,559 @@
1
+ /*
2
+ AlcaDialer_IM: Modulo di messagistica istantanea
3
+
4
+ Configurazione dei parametri di una connessione,
5
+ configurazione degli handlers,
6
+ connessione e disconnessione,
7
+ invio di dati,
8
+ amministrazione pubsub,
9
+ iscrizione e ricezione pubsub
10
+ */
11
+
12
+ // REQUIRE: jQuery e StropheJS
13
+ //
14
+ // jQuery: XML parsing
15
+ // StropheJS: XMPP
16
+
17
+ // NOTE: create a module private context
18
+ (function(){
19
+ var base_im_config = {
20
+ max_conn_retry: 0,
21
+ username: null,
22
+ password: null,
23
+ jid: null,
24
+ fulljid: null,
25
+ hostname: null,
26
+ boshurl: null,
27
+ resource: "AlcaBosh"
28
+ };
29
+
30
+ var dummy_handler = function() { };
31
+
32
+ var base_call_callbacks = {
33
+ ok: dummy_handler,
34
+ fail: dummy_handler
35
+ };
36
+
37
+ var base_global_callbacks = {
38
+ connected: dummy_handler,
39
+ connection_error: dummy_handler,
40
+ disconnecting: dummy_handler,
41
+ disconnected: dummy_handler,
42
+ receive_message: dummy_handler,
43
+ receive_pubsub_item: dummy_handler
44
+ };
45
+
46
+ var DEBUG = false;
47
+
48
+ var NS_PUBSUB = 'http://jabber.org/protocol/pubsub';
49
+
50
+ // NOTE: current configuration
51
+ var im_config = null;
52
+ // NOTE: current connection
53
+ var im_conn = null;
54
+ // NOTE: current persistent exported handlers
55
+ var im_handlers = null;
56
+
57
+ // AlcaDialer_IM constructor
58
+ var AlcaDialer_IM = mixin({}, {
59
+ init: IM_init,
60
+ connect: IM_connect,
61
+ disconnect: IM_disconnect,
62
+ send_message: IM_send_message,
63
+ ping: IM_ping,
64
+ create_user: IM_create_user,
65
+ flush: IM_flush,
66
+ pubsub: {
67
+ create_node: PUBSUB_create_node,
68
+ remove_node: PUBSUB_remove_node,
69
+ list_nodes: PUBSUB_list_nodes,
70
+ publish_item: PUBSUB_publish_item,
71
+ subscribe: PUBSUB_subscribe,
72
+ list_subscribed: PUBSUB_list_subscribed,
73
+ unsubscribe: PUBSUB_unsubscribe,
74
+ unsubscribe_all: PUBSUB_unsubscribe_all
75
+ },
76
+ get_connection: function() { return im_conn; },
77
+ jid: jid,
78
+ fulljid: fulljid,
79
+ STATUS: Strophe.Status // Export Strophe Status consts
80
+ });
81
+
82
+ // NOTE: Export AlcaDialer_IM
83
+ window.AlcaDialer_IM = AlcaDialer_IM;
84
+
85
+ // PUBLIC NETHODS IMPLEMENTATIONS
86
+ function jid(config) {
87
+ var cfg = config ? config : im_config;
88
+
89
+ return cfg.username+"@"+cfg.hostname;
90
+ }
91
+
92
+ function fulljid(config) {
93
+ var cfg = config ? config : im_config;
94
+
95
+ return jid(cfg)+'/'+cfg.resource;
96
+ }
97
+
98
+ function IM_init(config) {
99
+ // Mix configuration parameters
100
+ im_config = mixin({}, base_im_config, config);
101
+ im_config.fulljid = fulljid(im_config);
102
+ im_config.jid = jid(im_config);
103
+ im_handlers = mixin({}, base_global_callbacks, config.handlers);
104
+
105
+ return true;
106
+ }
107
+
108
+ function IM_connect() {
109
+ var fulljid = im_config.fulljid;
110
+
111
+ im_conn = new Strophe.Connection(im_config.boshurl);
112
+ // AlcaBosh.conn.rawInput = AlcaBosh._raw_input;
113
+ // AlcaBosh.conn.rawOutput = AlcaBosh._raw_output;
114
+ im_conn.connect(fulljid, im_config.password, _IM_connection_manager);
115
+ }
116
+
117
+ function IM_disconnect(reason) {
118
+ im_conn.flush();
119
+ im_conn.disconnect(reason);
120
+ im_config.conn_retry = 0;
121
+ }
122
+
123
+ function IM_create_user(callbacks) {
124
+ var cb = mixin({},base_call_callbacks,callbacks);
125
+ var xmpp_id = im_conn.getUniqueId('reg');
126
+
127
+ var xmpp = $iq(
128
+ {
129
+ "id": xmpp_id,
130
+ "type": "set",
131
+ "to": im_config.hostname
132
+ }).
133
+ c("query", {"xmlns": "jabber:iq:register"}).
134
+ c("username").t(im_config.username).
135
+ up().c("password").t(im_config.password);
136
+
137
+ _IM_sendSysIQ(xmpp, cb.ok, cb.fail);
138
+ im_conn.flush();
139
+ }
140
+
141
+ function IM_ping() {
142
+ _IM_send_data($iq({
143
+ type: 'get',
144
+ id: im_conn.getUniqueId('ping')
145
+ }).c('ping', { xmlns: 'urn:xmpp:ping' }));
146
+
147
+ im_conn.flush();
148
+ // don't remove the time handler
149
+ return true;
150
+ }
151
+
152
+ function IM_flush() {
153
+ im_conn.flush();
154
+ }
155
+
156
+ function IM_send_message(to, text) {
157
+ _IM_send_data(
158
+ $msg({ to: to, type: 'chat'}).
159
+ c('body').t(text)
160
+ );
161
+ }
162
+
163
+ function PUBSUB_list_nodes(rootnode, callbacks) {
164
+ var cb = mixin({},base_call_callbacks,callbacks);
165
+ var xmpp_id = im_conn.getUniqueId('list');
166
+ var xmpp = $iq(
167
+ {
168
+ id: xmpp_id,
169
+ type: 'get',
170
+ from: im_config.fulljid,
171
+ to: im_config.pubsub
172
+ }).
173
+ c('query',
174
+ {
175
+ xmlns: Strophe.NS.DISCO_ITEMS,
176
+ node: rootnode
177
+ });
178
+
179
+ // TODO: import from first prototype
180
+ _IM_sendIQ(xmpp, ok, fail);
181
+ function ok(data) {
182
+ var content = [];
183
+ $(data).find('item').each(function() {
184
+ content.push($(this).attr('node'));
185
+ });
186
+ cb.ok(content);
187
+ }
188
+ function fail(data) {
189
+ cb.fail(data);
190
+ }
191
+ }
192
+
193
+ /*
194
+ NOTE: xml fragment generation con jquery
195
+
196
+ var iq = $iq({ id: "12312312", type: "set"});
197
+ $(iq.tree()).append( $("<c xmlns='test'>"));
198
+ $('c', iq.tree()).append($("<value pippo='34'/>"));
199
+ iq.toString();
200
+
201
+ ====> <iq id='12312312' type='set' xmlns='jabber:client'><c xmlns='test'><value pippo='34'/></c></iq>
202
+ */
203
+
204
+ function PUBSUB_create_node(node, callbacks) {
205
+ var cb = mixin({},base_call_callbacks,callbacks);
206
+ var xmpp_id = im_conn.getUniqueId('list');
207
+ /* TODO: TOOOOOOO BIG */
208
+ var xmpp_iq = $iq(
209
+ {
210
+ id: xmpp_id,
211
+ type: 'set',
212
+ from: im_config.fulljid,
213
+ to: im_config.pubsub,
214
+ xmlns: Strophe.NS.CLIENT
215
+ }).
216
+ c('pubsub',{xmlns: NS_PUBSUB}).
217
+ c('create',{node: node});
218
+
219
+ // NOTE: Crea i fragment xml con gli helper strophe
220
+ var xmpp_configure = $build("configure", {node: node}).
221
+ c('x', {'type': 'submit', 'xmlns': 'jabber:x:data'});
222
+
223
+ var xmpp_field_formtype = $build('field', {'type': 'hidden', 'var': 'FORM_TYPE'}).
224
+ c('value').t('http://jabber.org/protocol/pubsub#node_config');
225
+
226
+ var xmpp_field_lastpub = $build('field', {'var': 'pubsub#send_last_published_item'}).
227
+ c('value').t('never');
228
+
229
+ var xmpp_field_presencedelivery = $build('field', {'var': 'pubsub#presence_based_delivery'}).
230
+ c('value').t('1');
231
+
232
+ var xmpp_field_publishmodel = $build('field', {'var': 'pubsub#publish_model'}).
233
+ c('value').t('open');
234
+
235
+ // NOTE: Assembla il pacchetto xml dai fragment mediante jQuery
236
+ $('pubsub', xmpp_iq.tree()).append(xmpp_configure.tree());
237
+ $('x', xmpp_configure.tree()).
238
+ append(xmpp_field_formtype.tree()).
239
+ append(xmpp_field_lastpub.tree()).
240
+ append(xmpp_field_presencedelivery.tree()).
241
+ append(xmpp_field_publishmodel.tree());
242
+
243
+ _IM_sendIQ(xmpp_iq, cb.ok, cb.fail);
244
+
245
+ return xmpp_id;
246
+ }
247
+
248
+ function PUBSUB_remove_node(node, callbacks) {
249
+ var cb = mixin({},base_call_callbacks,callbacks);
250
+ var xmpp_id = im_conn.getUniqueId('del');
251
+ var xmpp_iq = $iq(
252
+ {
253
+ 'type':'set',
254
+ 'from': im_config.fulljid,
255
+ 'to': im_config.pubsub,
256
+ 'id': xmpp_id
257
+ }).
258
+ c('pubsub', {'xmlns': 'http://jabber.org/protocol/pubsub#owner'}).
259
+ c('delete', {'node': node});
260
+
261
+ _IM_sendIQ(xmpp_iq, cb.ok, cb.fail);
262
+ }
263
+
264
+ function PUBSUB_publish_item(node, text, callbacks) {
265
+ var cb = mixin({},base_call_callbacks,callbacks);
266
+ var xmpp_id = im_conn.getUniqueId('pub');
267
+ var xmpp_iq = $iq(
268
+ {
269
+ 'type':'set',
270
+ 'from': im_config.fulljid,
271
+ 'to': im_config.pubsub,
272
+ 'id': xmpp_id
273
+ }).
274
+ c('pubsub', {'xmlns': 'http://jabber.org/protocol/pubsub'}).
275
+ c('publish', {'node': node }).
276
+ c('item').t(text);
277
+
278
+ _IM_sendIQ(xmpp_iq, cb.ok, cb.fail);
279
+ }
280
+
281
+
282
+ function PUBSUB_subscribe(node, callbacks) {
283
+ var cb = mixin({},base_call_callbacks,callbacks);
284
+ var xmpp_id = im_conn.getUniqueId('sub');
285
+ var bare_jid = Strophe.getBareJidFromJid(im_config.fulljid);
286
+ var xmpp_iq = $iq(
287
+ {
288
+ 'type':'set',
289
+ 'from': im_config.fulljid,
290
+ 'to': im_config.pubsub,
291
+ 'id': xmpp_id
292
+ }).
293
+ c('pubsub', {'xmlns': 'http://jabber.org/protocol/pubsub'}).
294
+ c('subscribe', {'node': node, 'jid': bare_jid});
295
+
296
+ _IM_sendIQ(xmpp_iq, parse_subscribe_ok, parse_subscribe_fail);
297
+
298
+ var result = { node: node, success: false };
299
+
300
+ function parse_subscribe_ok(data) {
301
+ result.success = true;
302
+ cb.ok(result);
303
+ }
304
+ function parse_subscribe_fail(data) {
305
+ //NOTE: result.success is already false
306
+ cb.ok(result);
307
+ }
308
+ }
309
+
310
+ function PUBSUB_unsubscribe(node, callbacks) {
311
+ var cb = mixin({},base_call_callbacks,callbacks);
312
+ var xmpp_id = im_conn.getUniqueId('unsub');
313
+ var bare_jid = Strophe.getBareJidFromJid(im_config.fulljid);
314
+ var xmpp_iq = $iq(
315
+ {
316
+ 'type':'set',
317
+ 'from': im_config.fulljid,
318
+ 'to': im_config.pubsub,
319
+ 'id': xmpp_id
320
+ }).
321
+ c('pubsub', {'xmlns': 'http://jabber.org/protocol/pubsub'}).
322
+ c('unsubscribe', {'node': node, 'jid': bare_jid});
323
+
324
+ _IM_sendIQ(xmpp_iq, parse_unsubscribe_ok, parse_unsubscribe_fail);
325
+
326
+ var result = { node: node, success: false };
327
+
328
+ function parse_unsubscribe_ok(data) {
329
+ result.success = true;
330
+ cb.ok(result);
331
+ }
332
+ function parse_unsubscribe_fail(data) {
333
+ //NOTE: result.success is already false
334
+ cb.ok(result);
335
+ }
336
+ return xmpp_id;
337
+ }
338
+
339
+ function PUBSUB_list_subscribed(callbacks) {
340
+ var cb = mixin({},base_call_callbacks,callbacks);
341
+ var xmpp_id = im_conn.getUniqueId('list');
342
+ var bare_jid = Strophe.getBareJidFromJid(im_config.fulljid);
343
+ var xmpp_iq = $iq(
344
+ {
345
+ 'type':'get',
346
+ 'from': im_config.fulljid,
347
+ 'to': im_config.pubsub,
348
+ 'id': xmpp_id
349
+ }).
350
+ c('pubsub', {'xmlns': 'http://jabber.org/protocol/pubsub'}).
351
+ c('subscriptions');
352
+
353
+ _IM_sendIQ(xmpp_iq, parse_subscriptions, cb.fail);
354
+ function parse_subscriptions(data) {
355
+ var content = [];
356
+
357
+ $(data).find('subscription').each(function () {
358
+ content.push($(this).attr('node'));
359
+ });
360
+
361
+ cb.ok(content);
362
+ }
363
+
364
+ }
365
+
366
+ // TODO: dovrebbe controllare l'effetivo esito delle desottoscrizioni richieste
367
+ function PUBSUB_unsubscribe_all(callbacks) {
368
+ var cb = mixin({},base_call_callbacks,callbacks);
369
+
370
+ PUBSUB_list_subscribed({
371
+ ok: function(data) {
372
+ var nodes = [];
373
+ var result = true;
374
+ $(data).each(function() {
375
+ PUBSUB_unsubscribe(this, {
376
+ ok: function(unsub_reply) {
377
+ nodes.push(unsub_reply);
378
+ if(nodes.length === data.length) {
379
+ if(result === true)
380
+ cb.ok(nodes);
381
+ else
382
+ cb.fail(nodes);
383
+ }
384
+ },
385
+ fail: function(unsub_reply) {
386
+ result = false;
387
+ nodes.push(unsub_reply);
388
+ if(nodes.length === data.length)
389
+ cb.fail(nodes);
390
+ }
391
+ });
392
+ im_conn.flush();
393
+ });
394
+
395
+
396
+ im_conn.flush();
397
+ }
398
+ });
399
+ }
400
+
401
+ // PRIVATE FUNCTIONS
402
+
403
+ function _IM_sendIQ(data, cb_ok, cb_fail) {
404
+ im_conn.sendIQ(data, cb_ok, cb_fail);
405
+ }
406
+
407
+ // NOTE: this helper send an IQ with attached system handlers which don't require
408
+ // an authenticated session
409
+ function _IM_sendSysIQ(elem, callback, errback, timeout) {
410
+ var timeoutHandler = null;
411
+ var that = im_conn;
412
+
413
+ if (typeof(elem.tree) === "function") {
414
+ elem = elem.tree();
415
+ }
416
+ var id = elem.getAttribute('id');
417
+
418
+ // inject id if not found
419
+ if (!id) {
420
+ id = im_conn.getUniqueId("sendIQ");
421
+ elem.setAttribute("id", id);
422
+ }
423
+
424
+ var handler = im_conn._addSysHandler(function (stanza) {
425
+
426
+ // remove timeout handler if there is one
427
+ if (timeoutHandler) {
428
+ that.deleteTimedHandler(timeoutHandler);
429
+ }
430
+
431
+ var iqtype = stanza.getAttribute('type');
432
+
433
+ if (iqtype === 'result') {
434
+ if (callback) {
435
+ callback(stanza);
436
+ }
437
+ } else if (iqtype === 'error') {
438
+ if (errback) {
439
+ errback(stanza);
440
+ }
441
+ } else {
442
+ throw {
443
+ name: "StropheError",
444
+ message: "Got bad IQ type of " + iqtype
445
+ };
446
+ }
447
+ }, null, 'iq', null, id);
448
+
449
+ // if timeout specified, setup timeout handler.
450
+ if (timeout) {
451
+ timeoutHandler = im_conn.addTimedHandler(timeout, function () {
452
+ // get rid of normal handler
453
+ that.deleteHandler(handler);
454
+
455
+ // call errback on timeout with null stanza
456
+ if (errback) {
457
+ errback(null);
458
+ }
459
+ return false;
460
+ });
461
+ }
462
+
463
+ im_conn.send(elem);
464
+
465
+ return id;
466
+ }
467
+
468
+ function _IM_connection_manager(status, reason) {
469
+ var fulljid = im_config.fulljid;
470
+ var fail_event = {
471
+ fulljid: fulljid,
472
+ status: status,
473
+ error: reason
474
+ };
475
+
476
+ switch(status) {
477
+ case Strophe.Status.ERROR:
478
+ im_handlers.connection_error(fail_event);
479
+ break;
480
+ case Strophe.Status.CONNFAIL:
481
+ im_handlers.connection_error(fail_event);
482
+ break;
483
+ case Strophe.Status.AUTHFAIL:
484
+ //im_conn.flush();
485
+ fail_event.error = "Authentication Error";
486
+ im_handlers.connection_error(fail_event);
487
+ break;
488
+ case Strophe.Status.CONNECTED:
489
+ _IM_connected();
490
+ im_handlers.connected({fulljid: fulljid});
491
+ break;
492
+ case Strophe.Status.DISCONNECTING:
493
+ im_handlers.disconnecting({
494
+ fulljid: fulljid,
495
+ reason: reason
496
+ });
497
+ break;
498
+ case Strophe.Status.DISCONNECTED:
499
+ im_handlers.disconnected({
500
+ fulljid: fulljid,
501
+ reason: reason
502
+ });
503
+ break;
504
+ }
505
+ }
506
+
507
+ function _IM_connected() {
508
+ // Install Message XMPP Stanzas receiver
509
+ im_conn.addHandler(_IM_receive_message, null, 'message',null,null,null,null);
510
+ // Send presence XMPP Stanzas
511
+ _IM_send_data($pres());
512
+ // Set periodic 5 min ping to confirm our presence
513
+ im_conn.addTimedHandler(5*60*1000, IM_ping);
514
+ }
515
+
516
+ function _IM_send_data(data) {
517
+ im_conn.send(data);
518
+ }
519
+
520
+ function _IM_receive_message(data) {
521
+ var message = $(data);
522
+ var from = message.attr('from');
523
+ var to = message.attr('to');
524
+ var type = message.attr('type');
525
+
526
+ var content = [];
527
+
528
+ if (from.match(/pubsub/i)) {
529
+ var items = $(message.find('items')[0]);
530
+ var node = items.attr('node');
531
+ message.find('item').each(function () {
532
+ content.push($(this).text());
533
+ });
534
+
535
+ if (content.length > 0)
536
+ im_handlers.receive_pubsub_item({node: node, content: content});
537
+ }
538
+ else if (type === "chat") {
539
+ content.push($(message.find('body')[0]).text());
540
+ im_handlers.receive_message({from: from, content: content});
541
+ }
542
+
543
+ return true;
544
+ }
545
+
546
+ // NOTE: simple (non-deep) mixin utility function
547
+ function mixin(target) {
548
+ var src=null;
549
+ for (var i=1; i<arguments.length; i++) {
550
+ src = arguments[i];
551
+ for (var j in src) {
552
+ target[j] = src[j];
553
+ }
554
+ }
555
+
556
+ return target;
557
+ }
558
+
559
+ })();