Show More
@@ -284,16 +284,16 b' define([' | |||||
284 | }); |
|
284 | }); | |
285 | }; |
|
285 | }; | |
286 |
|
286 | |||
287 | /** |
|
|||
288 | * POST /api/kernels/[:kernel_id]/restart |
|
|||
289 | * |
|
|||
290 | * Restart the kernel. |
|
|||
291 | * |
|
|||
292 | * @function interrupt |
|
|||
293 | * @param {function} [success] - function executed on ajax success |
|
|||
294 | * @param {function} [error] - functon executed on ajax error |
|
|||
295 | */ |
|
|||
296 | Kernel.prototype.restart = function (success, error) { |
|
287 | Kernel.prototype.restart = function (success, error) { | |
|
288 | /** | |||
|
289 | * POST /api/kernels/[:kernel_id]/restart | |||
|
290 | * | |||
|
291 | * Restart the kernel. | |||
|
292 | * | |||
|
293 | * @function interrupt | |||
|
294 | * @param {function} [success] - function executed on ajax success | |||
|
295 | * @param {function} [error] - functon executed on ajax error | |||
|
296 | */ | |||
297 | this.events.trigger('kernel_restarting.Kernel', {kernel: this}); |
|
297 | this.events.trigger('kernel_restarting.Kernel', {kernel: this}); | |
298 | this.stop_channels(); |
|
298 | this.stop_channels(); | |
299 |
|
299 | |||
@@ -325,14 +325,14 b' define([' | |||||
325 | }); |
|
325 | }); | |
326 | }; |
|
326 | }; | |
327 |
|
327 | |||
328 | /** |
|
|||
329 | * Reconnect to a disconnected kernel. This is not actually a |
|
|||
330 | * standard HTTP request, but useful function nonetheless for |
|
|||
331 | * reconnecting to the kernel if the connection is somehow lost. |
|
|||
332 | * |
|
|||
333 | * @function reconnect |
|
|||
334 | */ |
|
|||
335 | Kernel.prototype.reconnect = function () { |
|
328 | Kernel.prototype.reconnect = function () { | |
|
329 | /** | |||
|
330 | * Reconnect to a disconnected kernel. This is not actually a | |||
|
331 | * standard HTTP request, but useful function nonetheless for | |||
|
332 | * reconnecting to the kernel if the connection is somehow lost. | |||
|
333 | * | |||
|
334 | * @function reconnect | |||
|
335 | */ | |||
336 | if (this.is_connected()) { |
|
336 | if (this.is_connected()) { | |
337 | return; |
|
337 | return; | |
338 | } |
|
338 | } | |
@@ -344,15 +344,15 b' define([' | |||||
344 | this.start_channels(); |
|
344 | this.start_channels(); | |
345 | }; |
|
345 | }; | |
346 |
|
346 | |||
347 | /** |
|
|||
348 | * Handle a successful AJAX request by updating the kernel id and |
|
|||
349 | * name from the response, and then optionally calling a provided |
|
|||
350 | * callback. |
|
|||
351 | * |
|
|||
352 | * @function _on_success |
|
|||
353 | * @param {function} success - callback |
|
|||
354 | */ |
|
|||
355 | Kernel.prototype._on_success = function (success) { |
|
347 | Kernel.prototype._on_success = function (success) { | |
|
348 | /** | |||
|
349 | * Handle a successful AJAX request by updating the kernel id and | |||
|
350 | * name from the response, and then optionally calling a provided | |||
|
351 | * callback. | |||
|
352 | * | |||
|
353 | * @function _on_success | |||
|
354 | * @param {function} success - callback | |||
|
355 | */ | |||
356 | var that = this; |
|
356 | var that = this; | |
357 | return function (data, status, xhr) { |
|
357 | return function (data, status, xhr) { | |
358 | if (data) { |
|
358 | if (data) { | |
@@ -366,14 +366,14 b' define([' | |||||
366 | }; |
|
366 | }; | |
367 | }; |
|
367 | }; | |
368 |
|
368 | |||
369 | /** |
|
|||
370 | * Handle a failed AJAX request by logging the error message, and |
|
|||
371 | * then optionally calling a provided callback. |
|
|||
372 | * |
|
|||
373 | * @function _on_error |
|
|||
374 | * @param {function} error - callback |
|
|||
375 | */ |
|
|||
376 | Kernel.prototype._on_error = function (error) { |
|
369 | Kernel.prototype._on_error = function (error) { | |
|
370 | /** | |||
|
371 | * Handle a failed AJAX request by logging the error message, and | |||
|
372 | * then optionally calling a provided callback. | |||
|
373 | * | |||
|
374 | * @function _on_error | |||
|
375 | * @param {function} error - callback | |||
|
376 | */ | |||
377 | return function (xhr, status, err) { |
|
377 | return function (xhr, status, err) { | |
378 | utils.log_ajax_error(xhr, status, err); |
|
378 | utils.log_ajax_error(xhr, status, err); | |
379 | if (error) { |
|
379 | if (error) { | |
@@ -382,27 +382,27 b' define([' | |||||
382 | }; |
|
382 | }; | |
383 | }; |
|
383 | }; | |
384 |
|
384 | |||
385 | /** |
|
|||
386 | * Perform necessary tasks once the kernel has been started, |
|
|||
387 | * including actually connecting to the kernel. |
|
|||
388 | * |
|
|||
389 | * @function _kernel_created |
|
|||
390 | * @param {Object} data - information about the kernel including id |
|
|||
391 | */ |
|
|||
392 | Kernel.prototype._kernel_created = function (data) { |
|
385 | Kernel.prototype._kernel_created = function (data) { | |
|
386 | /** | |||
|
387 | * Perform necessary tasks once the kernel has been started, | |||
|
388 | * including actually connecting to the kernel. | |||
|
389 | * | |||
|
390 | * @function _kernel_created | |||
|
391 | * @param {Object} data - information about the kernel including id | |||
|
392 | */ | |||
393 | this.id = data.id; |
|
393 | this.id = data.id; | |
394 | this.kernel_url = utils.url_join_encode(this.kernel_service_url, this.id); |
|
394 | this.kernel_url = utils.url_join_encode(this.kernel_service_url, this.id); | |
395 | this.start_channels(); |
|
395 | this.start_channels(); | |
396 | }; |
|
396 | }; | |
397 |
|
397 | |||
398 | /** |
|
|||
399 | * Perform necessary tasks once the connection to the kernel has |
|
|||
400 | * been established. This includes requesting information about |
|
|||
401 | * the kernel. |
|
|||
402 | * |
|
|||
403 | * @function _kernel_connected |
|
|||
404 | */ |
|
|||
405 | Kernel.prototype._kernel_connected = function () { |
|
398 | Kernel.prototype._kernel_connected = function () { | |
|
399 | /** | |||
|
400 | * Perform necessary tasks once the connection to the kernel has | |||
|
401 | * been established. This includes requesting information about | |||
|
402 | * the kernel. | |||
|
403 | * | |||
|
404 | * @function _kernel_connected | |||
|
405 | */ | |||
406 | this.events.trigger('kernel_connected.Kernel', {kernel: this}); |
|
406 | this.events.trigger('kernel_connected.Kernel', {kernel: this}); | |
407 | this.events.trigger('kernel_starting.Kernel', {kernel: this}); |
|
407 | this.events.trigger('kernel_starting.Kernel', {kernel: this}); | |
408 | // get kernel info so we know what state the kernel is in |
|
408 | // get kernel info so we know what state the kernel is in | |
@@ -413,24 +413,24 b' define([' | |||||
413 | }); |
|
413 | }); | |
414 | }; |
|
414 | }; | |
415 |
|
415 | |||
416 | /** |
|
|||
417 | * Perform necessary tasks after the kernel has died. This closing |
|
|||
418 | * communication channels to the kernel if they are still somehow |
|
|||
419 | * open. |
|
|||
420 | * |
|
|||
421 | * @function _kernel_dead |
|
|||
422 | */ |
|
|||
423 | Kernel.prototype._kernel_dead = function () { |
|
416 | Kernel.prototype._kernel_dead = function () { | |
|
417 | /** | |||
|
418 | * Perform necessary tasks after the kernel has died. This closing | |||
|
419 | * communication channels to the kernel if they are still somehow | |||
|
420 | * open. | |||
|
421 | * | |||
|
422 | * @function _kernel_dead | |||
|
423 | */ | |||
424 | this.stop_channels(); |
|
424 | this.stop_channels(); | |
425 | }; |
|
425 | }; | |
426 |
|
426 | |||
427 | /** |
|
|||
428 | * Start the `shell`and `iopub` channels. |
|
|||
429 | * Will stop and restart them if they already exist. |
|
|||
430 | * |
|
|||
431 | * @function start_channels |
|
|||
432 | */ |
|
|||
433 | Kernel.prototype.start_channels = function () { |
|
427 | Kernel.prototype.start_channels = function () { | |
|
428 | /** | |||
|
429 | * Start the `shell`and `iopub` channels. | |||
|
430 | * Will stop and restart them if they already exist. | |||
|
431 | * | |||
|
432 | * @function start_channels | |||
|
433 | */ | |||
434 | var that = this; |
|
434 | var that = this; | |
435 | this.stop_channels(); |
|
435 | this.stop_channels(); | |
436 | var ws_host_url = this.ws_url + this.kernel_url; |
|
436 | var ws_host_url = this.ws_url + this.kernel_url; | |
@@ -504,29 +504,29 b' define([' | |||||
504 | this.channels.stdin.onmessage = $.proxy(this._handle_input_request, this); |
|
504 | this.channels.stdin.onmessage = $.proxy(this._handle_input_request, this); | |
505 | }; |
|
505 | }; | |
506 |
|
506 | |||
507 | /** |
|
|||
508 | * Handle a websocket entering the open state, |
|
|||
509 | * signaling that the kernel is connected when all channels are open. |
|
|||
510 | * |
|
|||
511 | * @function _ws_opened |
|
|||
512 | */ |
|
|||
513 | Kernel.prototype._ws_opened = function (evt) { |
|
507 | Kernel.prototype._ws_opened = function (evt) { | |
|
508 | /** | |||
|
509 | * Handle a websocket entering the open state, | |||
|
510 | * signaling that the kernel is connected when all channels are open. | |||
|
511 | * | |||
|
512 | * @function _ws_opened | |||
|
513 | */ | |||
514 | if (this.is_connected()) { |
|
514 | if (this.is_connected()) { | |
515 | // all events ready, trigger started event. |
|
515 | // all events ready, trigger started event. | |
516 | this._kernel_connected(); |
|
516 | this._kernel_connected(); | |
517 | } |
|
517 | } | |
518 | }; |
|
518 | }; | |
519 |
|
519 | |||
520 | /** |
|
|||
521 | * Handle a websocket entering the closed state. This closes the |
|
|||
522 | * other communication channels if they are open. If the websocket |
|
|||
523 | * was not closed due to an error, try to reconnect to the kernel. |
|
|||
524 | * |
|
|||
525 | * @function _ws_closed |
|
|||
526 | * @param {string} ws_url - the websocket url |
|
|||
527 | * @param {bool} error - whether the connection was closed due to an error |
|
|||
528 | */ |
|
|||
529 | Kernel.prototype._ws_closed = function(ws_url, error) { |
|
520 | Kernel.prototype._ws_closed = function(ws_url, error) { | |
|
521 | /** | |||
|
522 | * Handle a websocket entering the closed state. This closes the | |||
|
523 | * other communication channels if they are open. If the websocket | |||
|
524 | * was not closed due to an error, try to reconnect to the kernel. | |||
|
525 | * | |||
|
526 | * @function _ws_closed | |||
|
527 | * @param {string} ws_url - the websocket url | |||
|
528 | * @param {bool} error - whether the connection was closed due to an error | |||
|
529 | */ | |||
530 | this.stop_channels(); |
|
530 | this.stop_channels(); | |
531 |
|
531 | |||
532 | this.events.trigger('kernel_disconnected.Kernel', {kernel: this}); |
|
532 | this.events.trigger('kernel_disconnected.Kernel', {kernel: this}); | |
@@ -553,13 +553,13 b' define([' | |||||
553 | } |
|
553 | } | |
554 | }; |
|
554 | }; | |
555 |
|
555 | |||
556 | /** |
|
|||
557 | * Close the websocket channels. After successful close, the value |
|
|||
558 | * in `this.channels[channel_name]` will be null. |
|
|||
559 | * |
|
|||
560 | * @function stop_channels |
|
|||
561 | */ |
|
|||
562 | Kernel.prototype.stop_channels = function () { |
|
556 | Kernel.prototype.stop_channels = function () { | |
|
557 | /** | |||
|
558 | * Close the websocket channels. After successful close, the value | |||
|
559 | * in `this.channels[channel_name]` will be null. | |||
|
560 | * | |||
|
561 | * @function stop_channels | |||
|
562 | */ | |||
563 | var that = this; |
|
563 | var that = this; | |
564 | var close = function (c) { |
|
564 | var close = function (c) { | |
565 | return function () { |
|
565 | return function () { | |
@@ -580,15 +580,15 b' define([' | |||||
580 | } |
|
580 | } | |
581 | }; |
|
581 | }; | |
582 |
|
582 | |||
583 | /** |
|
|||
584 | * Check whether there is a connection to the kernel. This |
|
|||
585 | * function only returns true if all channel objects have been |
|
|||
586 | * created and have a state of WebSocket.OPEN. |
|
|||
587 | * |
|
|||
588 | * @function is_connected |
|
|||
589 | * @returns {bool} - whether there is a connection |
|
|||
590 | */ |
|
|||
591 | Kernel.prototype.is_connected = function () { |
|
583 | Kernel.prototype.is_connected = function () { | |
|
584 | /** | |||
|
585 | * Check whether there is a connection to the kernel. This | |||
|
586 | * function only returns true if all channel objects have been | |||
|
587 | * created and have a state of WebSocket.OPEN. | |||
|
588 | * | |||
|
589 | * @function is_connected | |||
|
590 | * @returns {bool} - whether there is a connection | |||
|
591 | */ | |||
592 | for (var c in this.channels) { |
|
592 | for (var c in this.channels) { | |
593 | // if any channel is not ready, then we're not connected |
|
593 | // if any channel is not ready, then we're not connected | |
594 | if (this.channels[c] === null) { |
|
594 | if (this.channels[c] === null) { | |
@@ -601,15 +601,15 b' define([' | |||||
601 | return true; |
|
601 | return true; | |
602 | }; |
|
602 | }; | |
603 |
|
603 | |||
604 | /** |
|
|||
605 | * Check whether the connection to the kernel has been completely |
|
|||
606 | * severed. This function only returns true if all channel objects |
|
|||
607 | * are null. |
|
|||
608 | * |
|
|||
609 | * @function is_fully_disconnected |
|
|||
610 | * @returns {bool} - whether the kernel is fully disconnected |
|
|||
611 | */ |
|
|||
612 | Kernel.prototype.is_fully_disconnected = function () { |
|
604 | Kernel.prototype.is_fully_disconnected = function () { | |
|
605 | /** | |||
|
606 | * Check whether the connection to the kernel has been completely | |||
|
607 | * severed. This function only returns true if all channel objects | |||
|
608 | * are null. | |||
|
609 | * | |||
|
610 | * @function is_fully_disconnected | |||
|
611 | * @returns {bool} - whether the kernel is fully disconnected | |||
|
612 | */ | |||
613 | for (var c in this.channels) { |
|
613 | for (var c in this.channels) { | |
614 | if (this.channels[c] === null) { |
|
614 | if (this.channels[c] === null) { | |
615 | return true; |
|
615 | return true; | |
@@ -618,12 +618,12 b' define([' | |||||
618 | return false; |
|
618 | return false; | |
619 | }; |
|
619 | }; | |
620 |
|
620 | |||
621 | /** |
|
|||
622 | * Send a message on the Kernel's shell channel |
|
|||
623 | * |
|
|||
624 | * @function send_shell_message |
|
|||
625 | */ |
|
|||
626 | Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata, buffers) { |
|
621 | Kernel.prototype.send_shell_message = function (msg_type, content, callbacks, metadata, buffers) { | |
|
622 | /** | |||
|
623 | * Send a message on the Kernel's shell channel | |||
|
624 | * | |||
|
625 | * @function send_shell_message | |||
|
626 | */ | |||
627 | if (!this.is_connected()) { |
|
627 | if (!this.is_connected()) { | |
628 | throw new Error("kernel is not connected"); |
|
628 | throw new Error("kernel is not connected"); | |
629 | } |
|
629 | } | |
@@ -633,17 +633,17 b' define([' | |||||
633 | return msg.header.msg_id; |
|
633 | return msg.header.msg_id; | |
634 | }; |
|
634 | }; | |
635 |
|
635 | |||
636 | /** |
|
|||
637 | * Get kernel info |
|
|||
638 | * |
|
|||
639 | * @function kernel_info |
|
|||
640 | * @param callback {function} |
|
|||
641 | * |
|
|||
642 | * When calling this method, pass a callback function that expects one argument. |
|
|||
643 | * The callback will be passed the complete `kernel_info_reply` message documented |
|
|||
644 | * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info) |
|
|||
645 | */ |
|
|||
646 | Kernel.prototype.kernel_info = function (callback) { |
|
636 | Kernel.prototype.kernel_info = function (callback) { | |
|
637 | /** | |||
|
638 | * Get kernel info | |||
|
639 | * | |||
|
640 | * @function kernel_info | |||
|
641 | * @param callback {function} | |||
|
642 | * | |||
|
643 | * When calling this method, pass a callback function that expects one argument. | |||
|
644 | * The callback will be passed the complete `kernel_info_reply` message documented | |||
|
645 | * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#kernel-info) | |||
|
646 | */ | |||
647 | var callbacks; |
|
647 | var callbacks; | |
648 | if (callback) { |
|
648 | if (callback) { | |
649 | callbacks = { shell : { reply : callback } }; |
|
649 | callbacks = { shell : { reply : callback } }; | |
@@ -651,19 +651,19 b' define([' | |||||
651 | return this.send_shell_message("kernel_info_request", {}, callbacks); |
|
651 | return this.send_shell_message("kernel_info_request", {}, callbacks); | |
652 | }; |
|
652 | }; | |
653 |
|
653 | |||
654 | /** |
|
|||
655 | * Get info on an object |
|
|||
656 | * |
|
|||
657 | * When calling this method, pass a callback function that expects one argument. |
|
|||
658 | * The callback will be passed the complete `inspect_reply` message documented |
|
|||
659 | * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information) |
|
|||
660 | * |
|
|||
661 | * @function inspect |
|
|||
662 | * @param code {string} |
|
|||
663 | * @param cursor_pos {integer} |
|
|||
664 | * @param callback {function} |
|
|||
665 | */ |
|
|||
666 | Kernel.prototype.inspect = function (code, cursor_pos, callback) { |
|
654 | Kernel.prototype.inspect = function (code, cursor_pos, callback) { | |
|
655 | /** | |||
|
656 | * Get info on an object | |||
|
657 | * | |||
|
658 | * When calling this method, pass a callback function that expects one argument. | |||
|
659 | * The callback will be passed the complete `inspect_reply` message documented | |||
|
660 | * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information) | |||
|
661 | * | |||
|
662 | * @function inspect | |||
|
663 | * @param code {string} | |||
|
664 | * @param cursor_pos {integer} | |||
|
665 | * @param callback {function} | |||
|
666 | */ | |||
667 | var callbacks; |
|
667 | var callbacks; | |
668 | if (callback) { |
|
668 | if (callback) { | |
669 | callbacks = { shell : { reply : callback } }; |
|
669 | callbacks = { shell : { reply : callback } }; | |
@@ -677,56 +677,56 b' define([' | |||||
677 | return this.send_shell_message("inspect_request", content, callbacks); |
|
677 | return this.send_shell_message("inspect_request", content, callbacks); | |
678 | }; |
|
678 | }; | |
679 |
|
679 | |||
680 | /** |
|
|||
681 | * Execute given code into kernel, and pass result to callback. |
|
|||
682 | * |
|
|||
683 | * @async |
|
|||
684 | * @function execute |
|
|||
685 | * @param {string} code |
|
|||
686 | * @param [callbacks] {Object} With the following keys (all optional) |
|
|||
687 | * @param callbacks.shell.reply {function} |
|
|||
688 | * @param callbacks.shell.payload.[payload_name] {function} |
|
|||
689 | * @param callbacks.iopub.output {function} |
|
|||
690 | * @param callbacks.iopub.clear_output {function} |
|
|||
691 | * @param callbacks.input {function} |
|
|||
692 | * @param {object} [options] |
|
|||
693 | * @param [options.silent=false] {Boolean} |
|
|||
694 | * @param [options.user_expressions=empty_dict] {Dict} |
|
|||
695 | * @param [options.allow_stdin=false] {Boolean} true|false |
|
|||
696 | * |
|
|||
697 | * @example |
|
|||
698 | * |
|
|||
699 | * The options object should contain the options for the execute |
|
|||
700 | * call. Its default values are: |
|
|||
701 | * |
|
|||
702 | * options = { |
|
|||
703 | * silent : true, |
|
|||
704 | * user_expressions : {}, |
|
|||
705 | * allow_stdin : false |
|
|||
706 | * } |
|
|||
707 | * |
|
|||
708 | * When calling this method pass a callbacks structure of the |
|
|||
709 | * form: |
|
|||
710 | * |
|
|||
711 | * callbacks = { |
|
|||
712 | * shell : { |
|
|||
713 | * reply : execute_reply_callback, |
|
|||
714 | * payload : { |
|
|||
715 | * set_next_input : set_next_input_callback, |
|
|||
716 | * } |
|
|||
717 | * }, |
|
|||
718 | * iopub : { |
|
|||
719 | * output : output_callback, |
|
|||
720 | * clear_output : clear_output_callback, |
|
|||
721 | * }, |
|
|||
722 | * input : raw_input_callback |
|
|||
723 | * } |
|
|||
724 | * |
|
|||
725 | * Each callback will be passed the entire message as a single |
|
|||
726 | * arugment. Payload handlers will be passed the corresponding |
|
|||
727 | * payload and the execute_reply message. |
|
|||
728 | */ |
|
|||
729 | Kernel.prototype.execute = function (code, callbacks, options) { |
|
680 | Kernel.prototype.execute = function (code, callbacks, options) { | |
|
681 | /** | |||
|
682 | * Execute given code into kernel, and pass result to callback. | |||
|
683 | * | |||
|
684 | * @async | |||
|
685 | * @function execute | |||
|
686 | * @param {string} code | |||
|
687 | * @param [callbacks] {Object} With the following keys (all optional) | |||
|
688 | * @param callbacks.shell.reply {function} | |||
|
689 | * @param callbacks.shell.payload.[payload_name] {function} | |||
|
690 | * @param callbacks.iopub.output {function} | |||
|
691 | * @param callbacks.iopub.clear_output {function} | |||
|
692 | * @param callbacks.input {function} | |||
|
693 | * @param {object} [options] | |||
|
694 | * @param [options.silent=false] {Boolean} | |||
|
695 | * @param [options.user_expressions=empty_dict] {Dict} | |||
|
696 | * @param [options.allow_stdin=false] {Boolean} true|false | |||
|
697 | * | |||
|
698 | * @example | |||
|
699 | * | |||
|
700 | * The options object should contain the options for the execute | |||
|
701 | * call. Its default values are: | |||
|
702 | * | |||
|
703 | * options = { | |||
|
704 | * silent : true, | |||
|
705 | * user_expressions : {}, | |||
|
706 | * allow_stdin : false | |||
|
707 | * } | |||
|
708 | * | |||
|
709 | * When calling this method pass a callbacks structure of the | |||
|
710 | * form: | |||
|
711 | * | |||
|
712 | * callbacks = { | |||
|
713 | * shell : { | |||
|
714 | * reply : execute_reply_callback, | |||
|
715 | * payload : { | |||
|
716 | * set_next_input : set_next_input_callback, | |||
|
717 | * } | |||
|
718 | * }, | |||
|
719 | * iopub : { | |||
|
720 | * output : output_callback, | |||
|
721 | * clear_output : clear_output_callback, | |||
|
722 | * }, | |||
|
723 | * input : raw_input_callback | |||
|
724 | * } | |||
|
725 | * | |||
|
726 | * Each callback will be passed the entire message as a single | |||
|
727 | * arugment. Payload handlers will be passed the corresponding | |||
|
728 | * payload and the execute_reply message. | |||
|
729 | */ | |||
730 | var content = { |
|
730 | var content = { | |
731 | code : code, |
|
731 | code : code, | |
732 | silent : true, |
|
732 | silent : true, |
General Comments 0
You need to be logged in to leave comments.
Login now