merging waldir-patch-1
[KiwiIRC.git] / client / assets / src / models / application.js
1 _kiwi.model.Application = function () {
2 // Set to a reference to this object within initialize()
3 var that = null;
4
5
6 var model = function () {
7 /** _kiwi.view.Application */
8 this.view = null;
9
10 /** _kiwi.view.StatusMessage */
11 this.message = null;
12
13 /* Address for the kiwi server */
14 this.kiwi_server = null;
15
16 this.initialize = function (options) {
17 that = this;
18
19 if (options[0].container) {
20 this.set('container', options[0].container);
21 }
22
23 // The base url to the kiwi server
24 this.set('base_path', options[0].base_path ? options[0].base_path : '/kiwi');
25
26 // Any options sent down from the server
27 this.server_settings = options[0].server_settings || {};
28
29 // Best guess at where the kiwi server is
30 this.detectKiwiServer();
31
32 // Takes instances of model_network
33 this.connections = new _kiwi.model.NetworkPanelList();
34 };
35
36
37 this.start = function () {
38 // Only debug if set in the querystring
39 if (!getQueryVariable('debug')) {
40 manageDebug(false);
41 } else {
42 //manageDebug(true);
43 }
44
45 // Set the gateway up
46 _kiwi.gateway = new _kiwi.model.Gateway();
47 this.bindGatewayCommands(_kiwi.gateway);
48
49 this.initializeClient();
50 this.initializeGlobals();
51
52 this.view.barsHide(true);
53
54 this.showIntialConenctionDialog();
55 };
56
57
58 this.detectKiwiServer = function () {
59 // If running from file, default to localhost:7777 by default
60 if (window.location.protocol === 'file:') {
61 this.kiwi_server = 'http://localhost:7778';
62 } else {
63 // Assume the kiwi server is on the same server
64 this.kiwi_server = window.location.protocol + '//' + window.location.host;
65 }
66 };
67
68
69 this.showIntialConenctionDialog = function() {
70 var connection_dialog = new _kiwi.model.NewConnection();
71 this.populateDefaultServerSettings(connection_dialog);
72
73 connection_dialog.view.$el.addClass('initial');
74 this.view.$el.find('.panel_container:first').append(connection_dialog.view.$el);
75
76 var $info = $($('#tmpl_new_connection_info').html().trim());
77
78 if ($info.html()) {
79 connection_dialog.view.infoBoxSet($info);
80 connection_dialog.view.infoBoxShow();
81 }
82
83 // TODO: Shouldn't really be here but it's not working in the view.. :/
84 // Hack for firefox browers: Focus is not given on this event loop iteration
85 setTimeout(function(){
86 connection_dialog.view.$el.find('.nick').select();
87 }, 0);
88
89 // Once connected, close this dialog and remove its own event
90 var fn = function() {
91 connection_dialog.view.$el.slideUp(function() {
92 connection_dialog.view.dispose();
93 connection_dialog = null;
94
95 _kiwi.gateway.off('onconnect', fn);
96 });
97
98 };
99 _kiwi.gateway.on('onconnect', fn);
100 };
101
102
103 this.initializeClient = function () {
104 this.view = new _kiwi.view.Application({model: this, el: this.get('container')});
105
106 // Applets panel list
107 this.applet_panels = new _kiwi.model.PanelList();
108 this.applet_panels.view.$el.addClass('panellist applets');
109 this.view.$el.find('.tabs').append(this.applet_panels.view.$el);
110
111 /**
112 * Set the UI components up
113 */
114 this.controlbox = new _kiwi.view.ControlBox({el: $('#kiwi .controlbox')[0]});
115 this.bindControllboxCommands(this.controlbox);
116
117 this.topicbar = new _kiwi.view.TopicBar({el: this.view.$el.find('.topic')[0]});
118
119 new _kiwi.view.AppToolbar({el: _kiwi.app.view.$el.find('.toolbar .app_tools')[0]});
120
121 this.message = new _kiwi.view.StatusMessage({el: this.view.$el.find('.status_message')[0]});
122
123 this.resize_handle = new _kiwi.view.ResizeHandler({el: this.view.$el.find('.memberlists_resize_handle')[0]});
124
125 // Rejigg the UI sizes
126 this.view.doLayout();
127 };
128
129
130 this.initializeGlobals = function () {
131 _kiwi.global.connections = this.connections;
132
133 _kiwi.global.panels = this.panels;
134 _kiwi.global.panels.applets = this.applet_panels;
135
136 _kiwi.global.components.Applet = _kiwi.model.Applet;
137 _kiwi.global.components.Panel =_kiwi.model.Panel;
138 };
139
140
141 this.populateDefaultServerSettings = function (new_connection_dialog) {
142 var parts;
143 var defaults = {
144 nick: '',
145 server: '',
146 port: 6667,
147 ssl: false,
148 channel: '#chat',
149 channel_key: ''
150 };
151 var uricheck;
152
153
154 /**
155 * Get any settings set by the server
156 * These settings may be changed in the server selection dialog or via URL parameters
157 */
158 if (this.server_settings.client) {
159 if (this.server_settings.client.nick)
160 defaults.nick = this.server_settings.client.nick;
161
162 if (this.server_settings.client.server)
163 defaults.server = this.server_settings.client.server;
164
165 if (this.server_settings.client.port)
166 defaults.port = this.server_settings.client.port;
167
168 if (this.server_settings.client.ssl)
169 defaults.ssl = this.server_settings.client.ssl;
170
171 if (this.server_settings.client.channel)
172 defaults.channel = this.server_settings.client.channel;
173 }
174
175
176
177 /**
178 * Get any settings passed in the URL
179 * These settings may be changed in the server selection dialog
180 */
181
182 // Any query parameters first
183 if (getQueryVariable('nick'))
184 defaults.nick = getQueryVariable('nick');
185
186 if (window.location.hash)
187 defaults.channel = window.location.hash;
188
189
190 // Process the URL part by part, extracting as we go
191 parts = window.location.pathname.toString().replace(this.get('base_path'), '').split('/');
192
193 if (parts.length > 0) {
194 parts.shift();
195
196 if (parts.length > 0 && parts[0]) {
197 // Check to see if we're dealing with an irc: uri, or whether we need to extract the server/channel info from the HTTP URL path.
198 uricheck = parts[0].substr(0, 7).toLowerCase();
199 if ((uricheck === 'ircs%3a') || (uricheck.substr(0,6) === 'irc%3a')) {
200 parts[0] = decodeURIComponent(parts[0]);
201 // irc[s]://<host>[:<port>]/[<channel>[?<password>]]
202 uricheck = /^irc(s)?:(?:\/\/?)?([^:\/]+)(?::([0-9]+))?(?:(?:\/)([^\?]*)(?:(?:\?)(.*))?)?$/.exec(parts[0]);
203 /*
204 uricheck[1] = ssl (optional)
205 uricheck[2] = host
206 uricheck[3] = port (optional)
207 uricheck[4] = channel (optional)
208 uricheck[5] = channel key (optional, channel must also be set)
209 */
210 if (uricheck) {
211 if (typeof uricheck[1] !== 'undefined') {
212 defaults.ssl = true;
213 if (defaults.port === 6667) {
214 defaults.port = 6697;
215 }
216 }
217 defaults.server = uricheck[2];
218 if (typeof uricheck[3] !== 'undefined') {
219 defaults.port = uricheck[3];
220 }
221 if (typeof uricheck[4] !== 'undefined') {
222 defaults.channel = '#' + uricheck[4];
223 if (typeof uricheck[5] !== 'undefined') {
224 defaults.channel_key = uricheck[5];
225 }
226 }
227 }
228 parts = [];
229 } else {
230 // Extract the port+ssl if we find one
231 if (parts[0].search(/:/) > 0) {
232 defaults.port = parts[0].substring(parts[0].search(/:/) + 1);
233 defaults.server = parts[0].substring(0, parts[0].search(/:/));
234 if (defaults.port[0] === '+') {
235 defaults.port = parseInt(defaults.port.substring(1), 10);
236 defaults.ssl = true;
237 } else {
238 defaults.ssl = false;
239 }
240
241 } else {
242 defaults.server = parts[0];
243 }
244
245 parts.shift();
246 }
247 }
248
249 if (parts.length > 0 && parts[0]) {
250 defaults.channel = '#' + parts[0];
251 parts.shift();
252 }
253 }
254
255 // If any settings have been given by the server.. override any auto detected settings
256 /**
257 * Get any server restrictions as set in the server config
258 * These settings can not be changed in the server selection dialog
259 */
260 if (this.server_settings && this.server_settings.connection) {
261 if (this.server_settings.connection.server) {
262 defaults.server = this.server_settings.connection.server;
263 }
264
265 if (this.server_settings.connection.port) {
266 defaults.port = this.server_settings.connection.port;
267 }
268
269 if (this.server_settings.connection.ssl) {
270 defaults.ssl = this.server_settings.connection.ssl;
271 }
272
273 if (this.server_settings.connection.channel) {
274 defaults.channel = this.server_settings.connection.channel;
275 }
276
277 if (this.server_settings.connection.nick) {
278 defaults.nick = this.server_settings.connection.nick;
279 }
280 }
281
282 // Set any random numbers if needed
283 defaults.nick = defaults.nick.replace('?', Math.floor(Math.random() * 100000).toString());
284
285 // Populate the server select box with defaults
286 new_connection_dialog.view.populateFields(defaults);
287 };
288
289
290 this.panels = (function() {
291 var fn = function(panel_type) {
292 var panels;
293
294 // Default panel type
295 panel_type = panel_type || 'connections';
296
297 switch (panel_type) {
298 case 'connections':
299 panels = this.connections.panels();
300 break;
301 case 'applets':
302 panels = this.applet_panels.models;
303 break;
304 }
305
306 // Active panels / server
307 panels.active = this.connections.active_panel;
308 panels.server = this.connections.active_connection ?
309 this.connections.active_connection.panels.server :
310 null;
311
312 return panels;
313 };
314
315 _.extend(fn, Backbone.Events);
316
317 return fn;
318 })();
319
320
321 this.bindGatewayCommands = function (gw) {
322 gw.on('onconnect', function (event) {
323 that.view.barsShow();
324 });
325
326
327 /**
328 * Handle the reconnections to the kiwi server
329 */
330 (function () {
331 var gw_stat = 0;
332
333 gw.on('disconnect', function (event) {
334 var msg = 'You have been disconnected. Attempting to reconnect for you..';
335 that.message.text(msg, {timeout: 10000});
336
337 that.view.$el.removeClass('connected');
338
339 // Mention the disconnection on every channel
340 _kiwi.app.connections.forEach(function(connection) {
341 connection.panels.server.addMsg('', msg, 'action quit');
342
343 connection.panels.forEach(function(panel) {
344 if (!panel.isChannel())
345 return;
346
347 panel.addMsg('', msg, 'action quit');
348 });
349 });
350
351 gw_stat = 1;
352 });
353
354
355 gw.on('reconnecting', function (event) {
356 var msg = 'You have been disconnected. Attempting to reconnect again in ' + (event.delay/1000) + ' seconds..';
357
358 // Only need to mention the repeating re-connection messages on server panels
359 _kiwi.app.connections.forEach(function(connection) {
360 connection.panels.server.addMsg('', msg, 'action quit');
361 });
362 });
363
364
365 gw.on('onconnect', function (event) {
366 that.view.$el.addClass('connected');
367 if (gw_stat !== 1) return;
368
369 var msg = 'It\'s OK, you\'re connected again :)';
370 that.message.text(msg, {timeout: 5000});
371
372 // Mention the disconnection on every channel
373 _kiwi.app.connections.forEach(function(connection) {
374 connection.panels.server.addMsg('', msg, 'action join');
375
376 connection.panels.forEach(function(panel) {
377 if (!panel.isChannel())
378 return;
379
380 panel.addMsg('', msg, 'action join');
381 });
382 });
383
384 gw_stat = 0;
385 });
386 })();
387
388 };
389
390
391
392 /**
393 * Bind to certain commands that may be typed into the control box
394 */
395 this.bindControllboxCommands = function (controlbox) {
396 // Default aliases
397 $.extend(controlbox.preprocessor.aliases, {
398 // General aliases
399 '/p': '/part $1+',
400 '/me': '/action $1+',
401 '/j': '/join $1+',
402 '/q': '/query $1+',
403
404 // Op related aliases
405 '/op': '/quote mode $channel +o $1+',
406 '/deop': '/quote mode $channel -o $1+',
407 '/hop': '/quote mode $channel +h $1+',
408 '/dehop': '/quote mode $channel -h $1+',
409 '/voice': '/quote mode $channel +v $1+',
410 '/devoice': '/quote mode $channel -v $1+',
411 '/k': '/kick $channel $1+',
412
413 // Misc aliases
414 '/slap': '/me slaps $1 around a bit with a large trout'
415 });
416
417 controlbox.on('unknown_command', unknownCommand);
418
419 controlbox.on('command', allCommands);
420 controlbox.on('command:msg', msgCommand);
421
422 controlbox.on('command:action', actionCommand);
423
424 controlbox.on('command:join', joinCommand);
425
426 controlbox.on('command:part', partCommand);
427
428 controlbox.on('command:nick', function (ev) {
429 _kiwi.gateway.changeNick(null, ev.params[0]);
430 });
431
432 controlbox.on('command:query', queryCommand);
433
434 controlbox.on('command:topic', topicCommand);
435
436 controlbox.on('command:notice', noticeCommand);
437
438 controlbox.on('command:quote', quoteCommand);
439
440 controlbox.on('command:kick', kickCommand);
441
442 controlbox.on('command:clear', clearCommand);
443
444 controlbox.on('command:ctcp', ctcpCommand);
445
446 controlbox.on('command:server', serverCommand);
447
448
449 controlbox.on('command:css', function (ev) {
450 var queryString = '?reload=' + new Date().getTime();
451 $('link[rel="stylesheet"]').each(function () {
452 this.href = this.href.replace(/\?.*|$/, queryString);
453 });
454 });
455
456 controlbox.on('command:js', function (ev) {
457 if (!ev.params[0]) return;
458 $script(ev.params[0] + '?' + (new Date().getTime()));
459 });
460
461
462 controlbox.on('command:set', function (ev) {
463 if (!ev.params[0]) return;
464
465 var setting = ev.params[0],
466 value;
467
468 // Do we have a second param to set a value?
469 if (ev.params[1]) {
470 ev.params.shift();
471
472 value = ev.params.join(' ');
473
474 // If we're setting a true boolean value..
475 if (value === 'true')
476 value = true;
477
478 // If we're setting a false boolean value..
479 if (value === 'false')
480 value = false;
481
482 // If we're setting a number..
483 if (parseInt(value, 10).toString() === value)
484 value = parseInt(value, 10);
485
486 _kiwi.global.settings.set(setting, value);
487 }
488
489 // Read the value to the user
490 _kiwi.app.panels().active.addMsg('', setting + ' = ' + _kiwi.global.settings.get(setting).toString());
491 });
492
493
494 controlbox.on('command:save', function (ev) {
495 _kiwi.global.settings.save();
496 _kiwi.app.panels().active.addMsg('', 'Settings have been saved');
497 });
498
499
500 controlbox.on('command:alias', function (ev) {
501 var name, rule;
502
503 // No parameters passed so list them
504 if (!ev.params[1]) {
505 $.each(controlbox.preprocessor.aliases, function (name, rule) {
506 _kiwi.app.panels().server.addMsg(' ', name + ' => ' + rule);
507 });
508 return;
509 }
510
511 // Deleting an alias?
512 if (ev.params[0] === 'del' || ev.params[0] === 'delete') {
513 name = ev.params[1];
514 if (name[0] !== '/') name = '/' + name;
515 delete controlbox.preprocessor.aliases[name];
516 return;
517 }
518
519 // Add the alias
520 name = ev.params[0];
521 ev.params.shift();
522 rule = ev.params.join(' ');
523
524 // Make sure the name starts with a slash
525 if (name[0] !== '/') name = '/' + name;
526
527 // Now actually add the alias
528 controlbox.preprocessor.aliases[name] = rule;
529 });
530
531
532 controlbox.on('command:ignore', function (ev) {
533 var list = _kiwi.gateway.get('ignore_list');
534
535 // No parameters passed so list them
536 if (!ev.params[0]) {
537 if (list.length > 0) {
538 _kiwi.app.panels().active.addMsg(' ', 'Ignored nicks:');
539 $.each(list, function (idx, ignored_pattern) {
540 _kiwi.app.panels().active.addMsg(' ', ignored_pattern);
541 });
542 } else {
543 _kiwi.app.panels().active.addMsg(' ', 'Not ignoring anybody');
544 }
545 return;
546 }
547
548 // We have a parameter, so add it
549 list.push(ev.params[0]);
550 _kiwi.gateway.set('ignore_list', list);
551 _kiwi.app.panels().active.addMsg(' ', 'Ignoring ' + ev.params[0]);
552 });
553
554
555 controlbox.on('command:unignore', function (ev) {
556 var list = _kiwi.gateway.get('ignore_list');
557
558 if (!ev.params[0]) {
559 _kiwi.app.panels().active.addMsg(' ', 'Specifiy which nick you wish to stop ignoring');
560 return;
561 }
562
563 list = _.reject(list, function(pattern) {
564 return pattern === ev.params[0];
565 });
566
567 _kiwi.gateway.set('ignore_list', list);
568
569 _kiwi.app.panels().active.addMsg(' ', 'Stopped ignoring ' + ev.params[0]);
570 });
571
572
573 controlbox.on('command:applet', appletCommand);
574 controlbox.on('command:settings', settingsCommand);
575 controlbox.on('command:script', scriptCommand);
576 };
577
578 // A fallback action. Send a raw command to the server
579 function unknownCommand (ev) {
580 var raw_cmd = ev.command + ' ' + ev.params.join(' ');
581 console.log('RAW: ' + raw_cmd);
582 _kiwi.gateway.raw(null, raw_cmd);
583 }
584
585 function allCommands (ev) {}
586
587 function joinCommand (ev) {
588 var panels, channel_names;
589
590 channel_names = ev.params.join(' ').split(',');
591 panels = that.connections.active_connection.createAndJoinChannels(channel_names);
592
593 // Show the last channel if we have one
594 if (panels)
595 panels[panels.length - 1].view.show();
596 }
597
598 function queryCommand (ev) {
599 var destination, panel;
600
601 destination = ev.params[0];
602
603 // Check if we have the panel already. If not, create it
604 panel = that.connections.active_connection.panels.getByName(destination);
605 if (!panel) {
606 panel = new _kiwi.model.Query({name: destination});
607 that.connections.active_connection.panels.add(panel);
608 }
609
610 if (panel) panel.view.show();
611
612 }
613
614 function msgCommand (ev) {
615 var destination = ev.params[0],
616 panel = that.connections.active_connection.panels.getByName(destination) || that.panels().server;
617
618 ev.params.shift();
619
620 panel.addMsg(_kiwi.app.connections.active_connection.get('nick'), ev.params.join(' '));
621 _kiwi.gateway.privmsg(null, destination, ev.params.join(' '));
622 }
623
624 function actionCommand (ev) {
625 if (_kiwi.app.panels().active.isServer()) {
626 return;
627 }
628
629 var panel = _kiwi.app.panels().active;
630 panel.addMsg('', '* ' + _kiwi.app.connections.active_connection.get('nick') + ' ' + ev.params.join(' '), 'action');
631 _kiwi.gateway.action(null, panel.get('name'), ev.params.join(' '));
632 }
633
634 function partCommand (ev) {
635 if (ev.params.length === 0) {
636 _kiwi.gateway.part(null, _kiwi.app.panels().active.get('name'));
637 } else {
638 _.each(ev.params, function (channel) {
639 _kiwi.gateway.part(null, channel);
640 });
641 }
642 }
643
644 function topicCommand (ev) {
645 var channel_name;
646
647 if (ev.params.length === 0) return;
648
649 if (that.isChannelName(ev.params[0])) {
650 channel_name = ev.params[0];
651 ev.params.shift();
652 } else {
653 channel_name = _kiwi.app.panels().active.get('name');
654 }
655
656 _kiwi.gateway.topic(null, channel_name, ev.params.join(' '));
657 }
658
659 function noticeCommand (ev) {
660 var destination;
661
662 // Make sure we have a destination and some sort of message
663 if (ev.params.length <= 1) return;
664
665 destination = ev.params[0];
666 ev.params.shift();
667
668 _kiwi.gateway.notice(null, destination, ev.params.join(' '));
669 }
670
671 function quoteCommand (ev) {
672 var raw = ev.params.join(' ');
673 _kiwi.gateway.raw(null, raw);
674 }
675
676 function kickCommand (ev) {
677 var nick, panel = _kiwi.app.panels().active;
678
679 if (!panel.isChannel()) return;
680
681 // Make sure we have a nick
682 if (ev.params.length === 0) return;
683
684 nick = ev.params[0];
685 ev.params.shift();
686
687 _kiwi.gateway.kick(null, panel.get('name'), nick, ev.params.join(' '));
688 }
689
690 function clearCommand (ev) {
691 // Can't clear a server or applet panel
692 if (_kiwi.app.panels().active.isServer() || _kiwi.app.panels().active.isApplet()) {
693 return;
694 }
695
696 if (_kiwi.app.panels().active.clearMessages) {
697 _kiwi.app.panels().active.clearMessages();
698 }
699 }
700
701 function ctcpCommand(ev) {
702 var target, type;
703
704 // Make sure we have a target and a ctcp type (eg. version, time)
705 if (ev.params.length < 2) return;
706
707 target = ev.params[0];
708 ev.params.shift();
709
710 type = ev.params[0];
711 ev.params.shift();
712
713 _kiwi.gateway.ctcp(null, true, type, target, ev.params.join(' '));
714 }
715
716 function settingsCommand (ev) {
717 var settings = _kiwi.model.Applet.loadOnce('kiwi_settings');
718 settings.view.show();
719 }
720
721 function scriptCommand (ev) {
722 var editor = _kiwi.model.Applet.loadOnce('kiwi_script_editor');
723 editor.view.show();
724 }
725
726 function appletCommand (ev) {
727 if (!ev.params[0]) return;
728
729 var panel = new _kiwi.model.Applet();
730
731 if (ev.params[1]) {
732 // Url and name given
733 panel.load(ev.params[0], ev.params[1]);
734 } else {
735 // Load a pre-loaded applet
736 if (_kiwi.applets[ev.params[0]]) {
737 panel.load(new _kiwi.applets[ev.params[0]]());
738 } else {
739 _kiwi.app.panels().server.addMsg('', 'Applet "' + ev.params[0] + '" does not exist');
740 return;
741 }
742 }
743
744 _kiwi.app.connections.active_connection.panels.add(panel);
745 panel.view.show();
746 }
747
748
749 function serverCommand (ev) {
750 var server, port, ssl, password, nick,
751 tmp;
752
753 // If no server address given, show the new connection dialog
754 if (!ev.params[0]) {
755 tmp = new _kiwi.view.MenuBox('New Connection');
756 tmp.addItem('new_connection', new _kiwi.model.NewConnection().view.$el);
757 tmp.show();
758
759 // Center screen the dialog
760 tmp.$el.offset({
761 top: (that.view.$el.height() / 2) - (tmp.$el.height() / 2),
762 left: (that.view.$el.width() / 2) - (tmp.$el.width() / 2)
763 });
764
765 return;
766 }
767
768 // Port given in 'host:port' format and no specific port given after a space
769 if (ev.params[0].indexOf(':') > 0) {
770 tmp = ev.params[0].split(':');
771 server = tmp[0];
772 port = tmp[1];
773
774 password = ev.params[1] || undefined;
775
776 } else {
777 // Server + port given as 'host port'
778 server = ev.params[0];
779 port = ev.params[1] || 6667;
780
781 password = ev.params[2] || undefined;
782 }
783
784 // + in the port means SSL
785 if (port.toString()[0] === '+') {
786 ssl = true;
787 port = parseInt(port.substring(1), 10);
788 } else {
789 ssl = false;
790 }
791
792 // Default port if one wasn't found
793 port = port || 6667;
794
795 // Use the same nick as we currently have
796 nick = _kiwi.app.connections.active_connection.get('nick');
797
798 _kiwi.app.panels().active.addMsg('', 'Connecting to ' + server + ':' + port.toString() + '..');
799
800 _kiwi.gateway.newConnection({
801 nick: nick,
802 host: server,
803 port: port,
804 ssl: ssl,
805 password: password
806 }, function(err, new_connection) {
807 if (err)
808 _kiwi.app.panels().active.addMsg('', 'Error connecting to ' + server + ':' + port.toString() + ' (' + err.toString() + ')');
809 });
810 }
811
812
813
814
815
816 this.isChannelName = function (channel_name) {
817 var channel_prefix = _kiwi.gateway.get('channel_prefix');
818
819 if (!channel_name || !channel_name.length) return false;
820 return (channel_prefix.indexOf(channel_name[0]) > -1);
821 };
822
823
824 };
825
826
827 model = Backbone.Model.extend(new model());
828
829 return new model(arguments);
830 };