Show More
@@ -38,9 +38,9 define([ | |||||
38 | * @param {object|undefined} [options] |
|
38 | * @param {object|undefined} [options] | |
39 | * @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters |
|
39 | * @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters | |
40 | */ |
|
40 | */ | |
41 | var Cell = function (options) { |
|
41 | var Cell = function (options, keyboard_manager, events) { | |
42 |
this.keyboard_manager = |
|
42 | this.keyboard_manager = keyboard_manager; | |
43 |
this.events = |
|
43 | this.events = events; | |
44 | options = this.mergeopt(Cell, options); |
|
44 | options = this.mergeopt(Cell, options); | |
45 | // superclass default overwrite our default |
|
45 | // superclass default overwrite our default | |
46 |
|
46 |
@@ -54,13 +54,13 define([ | |||||
54 | * @param {object|undefined} [options] |
|
54 | * @param {object|undefined} [options] | |
55 | * @param [options.cm_config] {object} config to pass to CodeMirror |
|
55 | * @param [options.cm_config] {object} config to pass to CodeMirror | |
56 | */ |
|
56 | */ | |
57 | var CodeCell = function (kernel, options) { |
|
57 | var CodeCell = function (kernel, options, events, config, keyboard_manager, notebook) { | |
58 | this.kernel = kernel || null; |
|
58 | this.kernel = kernel || null; | |
59 |
this.notebook = |
|
59 | this.notebook = notebook; | |
60 | this.collapsed = false; |
|
60 | this.collapsed = false; | |
61 |
this.tooltip = new tooltip.Tooltip( |
|
61 | this.tooltip = new tooltip.Tooltip(events); | |
62 |
this.events = |
|
62 | this.events = events; | |
63 |
this.config = |
|
63 | this.config = config; | |
64 |
|
64 | |||
65 | // create all attributed in constructor function |
|
65 | // create all attributed in constructor function | |
66 | // even if null for V8 VM optimisation |
|
66 | // even if null for V8 VM optimisation | |
@@ -77,7 +77,7 define([ | |||||
77 |
|
77 | |||
78 | options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options}); |
|
78 | options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options}); | |
79 |
|
79 | |||
80 |
Cell.apply(this,[options]); |
|
80 | Cell.apply(this,[options, keyboard_manager, events]); | |
81 |
|
81 | |||
82 | // Attributes we want to override in this subclass. |
|
82 | // Attributes we want to override in this subclass. | |
83 | this.cell_type = "code"; |
|
83 | this.cell_type = "code"; |
@@ -1,6 +1,7 | |||||
1 | // Copyright (c) IPython Development Team. |
|
1 | // Copyright (c) IPython Development Team. | |
2 | // Distributed under the terms of the Modified BSD License. |
|
2 | // Distributed under the terms of the Modified BSD License. | |
3 |
|
3 | |||
|
4 | var ipython = ipython || {}; | |||
4 | require([ |
|
5 | require([ | |
5 | 'base/js/namespace', |
|
6 | 'base/js/namespace', | |
6 | 'jquery', |
|
7 | 'jquery', | |
@@ -48,25 +49,18 require([ | |||||
48 | }; |
|
49 | }; | |
49 |
|
50 | |||
50 | var user_config = $.extend({}, config.default_config); |
|
51 | var user_config = $.extend({}, config.default_config); | |
51 | options.user_config = user_config; |
|
|||
52 | var page = new page.Page(); |
|
52 | var page = new page.Page(); | |
53 | var layout_manager = new layoutmanager.LayoutManager(); |
|
53 | var layout_manager = new layoutmanager.LayoutManager(); | |
54 | options.layout_manager = layout_manager; |
|
|||
55 | var events = $([new events.Events()]); |
|
54 | var events = $([new events.Events()]); | |
56 | options.events = events; |
|
|||
57 | var pager = new pager.Pager('div#pager', 'div#pager_splitter', layout_manager, events); |
|
55 | var pager = new pager.Pager('div#pager', 'div#pager_splitter', layout_manager, events); | |
58 | var keyboard_manager = new keyboardmanager.KeyboardManager(pager, events); |
|
56 | var keyboard_manager = new keyboardmanager.KeyboardManager(pager, events); | |
59 | options.keyboard_manager = keyboard_manager; |
|
|||
60 | var save_widget = new savewidget.SaveWidget('span#save_widget', events); |
|
57 | var save_widget = new savewidget.SaveWidget('span#save_widget', events); | |
61 | options.save_widget = save_widget; |
|
58 | var notebook = new notebook.Notebook('div#notebook', options, events, keyboard_manager, save_widget, user_config); | |
62 | var notebook = new notebook.Notebook('div#notebook', options); |
|
|||
63 | options.notebook = notebook; |
|
|||
64 | var login_widget = new loginwidget.LoginWidget('span#login_widget', options); |
|
59 | var login_widget = new loginwidget.LoginWidget('span#login_widget', options); | |
65 |
var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', |
|
60 | var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', layout_manager, notebook, events); | |
66 |
var quick_help = new quickhelp.QuickHelp(undefined, |
|
61 | var quick_help = new quickhelp.QuickHelp(undefined, keyboard_manager, events); | |
67 | options.quick_help = quick_help; |
|
62 | var menubar = new menubar.MenuBar('#menubar', options, notebook, layout_manager, events, save_widget, quick_help); | |
68 | var menubar = new menubar.MenuBar('#menubar', options); |
|
63 | var notification_area = new notificationarea.NotificationArea('#notification_area', events, save_widget, notebook); | |
69 | var notification_area = new notificationarea.NotificationArea('#notification_area', options); |
|
|||
70 | notification_area.init_notification_widgets(); |
|
64 | notification_area.init_notification_widgets(); | |
71 |
|
65 | |||
72 | $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+ |
|
66 | $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+ | |
@@ -99,17 +93,17 require([ | |||||
99 | events.trigger('app_initialized.NotebookApp'); |
|
93 | events.trigger('app_initialized.NotebookApp'); | |
100 | notebook.load_notebook(options.notebook_name, options.notebook_path); |
|
94 | notebook.load_notebook(options.notebook_name, options.notebook_path); | |
101 |
|
95 | |||
102 |
|
|
96 | ipython.page = page; | |
103 |
|
|
97 | ipython.layout_manager = layout_manager; | |
104 |
|
|
98 | ipython.notebook = notebook; | |
105 |
|
|
99 | ipython.pager = pager; | |
106 |
|
|
100 | ipython.quick_help = quick_help; | |
107 |
|
|
101 | ipython.login_widget = login_widget; | |
108 |
|
|
102 | ipython.menubar = menubar; | |
109 |
|
|
103 | ipython.toolbar = toolbar; | |
110 |
|
|
104 | ipython.notification_area = notification_area; | |
111 |
|
|
105 | ipython.events = events; | |
112 |
|
|
106 | ipython.keyboard_manager = keyboard_manager; | |
113 |
|
|
107 | ipython.save_widget = save_widget; | |
114 |
|
|
108 | ipython.config = user_config; | |
115 | }); |
|
109 | }); |
@@ -9,10 +9,10 define([ | |||||
9 | ], function(IPython, $, toolbar, celltoolbar) { |
|
9 | ], function(IPython, $, toolbar, celltoolbar) { | |
10 | "use strict"; |
|
10 | "use strict"; | |
11 |
|
11 | |||
12 |
var MainToolBar = function (selector, |
|
12 | var MainToolBar = function (selector, layout_manager, notebook, events) { | |
13 | toolbar.ToolBar.apply(this, arguments); |
|
13 | toolbar.ToolBar.apply(this, arguments); | |
14 |
this.events = |
|
14 | this.events = events; | |
15 |
this.notebook = |
|
15 | this.notebook = notebook; | |
16 | this.construct(); |
|
16 | this.construct(); | |
17 | this.add_celltype_list(); |
|
17 | this.add_celltype_list(); | |
18 | this.add_celltoolbar_list(); |
|
18 | this.add_celltoolbar_list(); |
@@ -24,18 +24,18 define([ | |||||
24 | * $('body').data('baseUrl'); |
|
24 | * $('body').data('baseUrl'); | |
25 | * does not support change for now is set through this option |
|
25 | * does not support change for now is set through this option | |
26 | */ |
|
26 | */ | |
27 | var MenuBar = function (selector, options) { |
|
27 | var MenuBar = function (selector, options, notebook, layout_manager, events, save_widget, quick_help) { | |
28 | options = options || {}; |
|
28 | options = options || {}; | |
29 | this.base_url = options.base_url || utils.get_body_data("baseUrl"); |
|
29 | this.base_url = options.base_url || utils.get_body_data("baseUrl"); | |
30 | this.selector = selector; |
|
30 | this.selector = selector; | |
31 |
this.notebook = |
|
31 | this.notebook = notebook; | |
32 |
this.layout_manager = |
|
32 | this.layout_manager = layout_manager; | |
33 |
this.events = |
|
33 | this.events = events; | |
34 |
this.save_widget = |
|
34 | this.save_widget = save_widget; | |
35 |
this.quick_help = |
|
35 | this.quick_help = quick_help; | |
36 |
|
36 | |||
37 | try { |
|
37 | try { | |
38 |
this.tour = new tour.Tour( |
|
38 | this.tour = new tour.Tour(notebook, events); | |
39 | } catch (e) { |
|
39 | } catch (e) { | |
40 | this.tour = undefined; |
|
40 | this.tour = undefined; | |
41 | console.log("Failed to instantiate Notebook Tour", e); |
|
41 | console.log("Failed to instantiate Notebook Tour", e); |
@@ -38,15 +38,15 define([ | |||||
38 | * @param {Object} [options] A config object |
|
38 | * @param {Object} [options] A config object | |
39 | * @param {Object} [events] An events object |
|
39 | * @param {Object} [events] An events object | |
40 | */ |
|
40 | */ | |
41 | var Notebook = function (selector, options) { |
|
41 | var Notebook = function (selector, options, events, keyboard_manager, save_widget, config) { | |
42 |
this.config = |
|
42 | this.config = config; | |
43 |
this.events = |
|
43 | this.events = events; | |
44 |
this.keyboard_manager = |
|
44 | this.keyboard_manager = keyboard_manager; | |
45 | // TODO: This code smells (and the other `= this` line a couple lines down) |
|
45 | // TODO: This code smells (and the other `= this` line a couple lines down) | |
46 | // We need a better way to deal with circular instance references. |
|
46 | // We need a better way to deal with circular instance references. | |
47 |
|
|
47 | keyboard_manager.notebook = this; | |
48 |
this.save_widget = |
|
48 | this.save_widget = save_widget; | |
49 |
|
|
49 | save_widget.notebook = this; | |
50 |
|
50 | |||
51 | mathjaxutils.init(); |
|
51 | mathjaxutils.init(); | |
52 |
|
52 | |||
@@ -807,14 +807,14 define([ | |||||
807 |
|
807 | |||
808 | if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) { |
|
808 | if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) { | |
809 | if (type === 'code') { |
|
809 | if (type === 'code') { | |
810 |
cell = new codecell.CodeCell(this.kernel, this.options); |
|
810 | cell = new codecell.CodeCell(this.kernel, this.options, this.events, this.config, this.keyboard_manager, this); | |
811 | cell.set_input_prompt(); |
|
811 | cell.set_input_prompt(); | |
812 | } else if (type === 'markdown') { |
|
812 | } else if (type === 'markdown') { | |
813 |
cell = new cells.MarkdownCell(this.options); |
|
813 | cell = new cells.MarkdownCell(this.options, this.events, this.config, this.keyboard_manager, this); | |
814 | } else if (type === 'raw') { |
|
814 | } else if (type === 'raw') { | |
815 |
cell = new cells.RawCell(this.options); |
|
815 | cell = new cells.RawCell(this.options, this.events, this.config, this.keyboard_manager, this); | |
816 | } else if (type === 'heading') { |
|
816 | } else if (type === 'heading') { | |
817 |
cell = new cells.HeadingCell(this.options); |
|
817 | cell = new cells.HeadingCell(this.options, this.events, this.config, this.keyboard_manager, this); | |
818 | } |
|
818 | } | |
819 |
|
819 | |||
820 | if(this._insert_element_at_index(cell.element,index)) { |
|
820 | if(this._insert_element_at_index(cell.element,index)) { |
@@ -10,11 +10,11 define([ | |||||
10 | ], function(IPython, $, utils, dialog, notificationwidget) { |
|
10 | ], function(IPython, $, utils, dialog, notificationwidget) { | |
11 | "use strict"; |
|
11 | "use strict"; | |
12 |
|
12 | |||
13 |
var NotificationArea = function (selector, |
|
13 | var NotificationArea = function (selector, events, save_widget, notebook) { | |
14 | this.selector = selector; |
|
14 | this.selector = selector; | |
15 |
this.events = |
|
15 | this.events = events; | |
16 |
this.save_widget = |
|
16 | this.save_widget = save_widget; | |
17 |
this.notebook = |
|
17 | this.notebook = notebook; | |
18 | if (this.selector !== undefined) { |
|
18 | if (this.selector !== undefined) { | |
19 | this.element = $(selector); |
|
19 | this.element = $(selector); | |
20 | } |
|
20 | } |
@@ -10,11 +10,10 define([ | |||||
10 | "use strict"; |
|
10 | "use strict"; | |
11 | var platform = utils.platform; |
|
11 | var platform = utils.platform; | |
12 |
|
12 | |||
13 |
var QuickHelp = function (selector, |
|
13 | var QuickHelp = function (selector, keyboard_manager, events) { | |
14 |
this.keyboard_manager = |
|
14 | this.keyboard_manager = keyboard_manager; | |
15 | // TODO: Remove circular reference. |
|
15 | keyboard_manager.quick_help = this; | |
16 | options.keyboard_manager.quick_help = this; |
|
16 | this.events = events; | |
17 | this.events = options.events; |
|
|||
18 | }; |
|
17 | }; | |
19 |
|
18 | |||
20 | var cmd_ctrl = 'Ctrl-'; |
|
19 | var cmd_ctrl = 'Ctrl-'; |
@@ -24,14 +24,14 define([ | |||||
24 | * @param [options.cm_config] {object} config to pass to CodeMirror, will extend/overwrite default config |
|
24 | * @param [options.cm_config] {object} config to pass to CodeMirror, will extend/overwrite default config | |
25 | * @param [options.placeholder] {string} default string to use when souce in empty for rendering (only use in some TextCell subclass) |
|
25 | * @param [options.placeholder] {string} default string to use when souce in empty for rendering (only use in some TextCell subclass) | |
26 | */ |
|
26 | */ | |
27 | var TextCell = function (options) { |
|
27 | var TextCell = function (options, events, config, keyboard_manager, notebook) { | |
28 | // in all TextCell/Cell subclasses |
|
28 | // in all TextCell/Cell subclasses | |
29 | // do not assign most of members here, just pass it down |
|
29 | // do not assign most of members here, just pass it down | |
30 | // in the options dict potentially overwriting what you wish. |
|
30 | // in the options dict potentially overwriting what you wish. | |
31 | // they will be assigned in the base class. |
|
31 | // they will be assigned in the base class. | |
32 |
this.notebook = |
|
32 | this.notebook = notebook; | |
33 |
this.events = |
|
33 | this.events = events; | |
34 |
this.config = |
|
34 | this.config = config; | |
35 |
|
35 | |||
36 | // we cannot put this as a class key as it has handle to "this". |
|
36 | // we cannot put this as a class key as it has handle to "this". | |
37 | var cm_overwrite_options = { |
|
37 | var cm_overwrite_options = { | |
@@ -43,7 +43,7 define([ | |||||
43 | this.cell_type = this.cell_type || 'text'; |
|
43 | this.cell_type = this.cell_type || 'text'; | |
44 | mathjaxutils = mathjaxutils; |
|
44 | mathjaxutils = mathjaxutils; | |
45 |
|
45 | |||
46 | Cell.apply(this, [options]); |
|
46 | Cell.apply(this, [options, keyboard_manager, events]); | |
47 |
|
47 | |||
48 | this.rendered = false; |
|
48 | this.rendered = false; | |
49 | }; |
|
49 | }; | |
@@ -218,11 +218,11 define([ | |||||
218 | * @constructor MarkdownCell |
|
218 | * @constructor MarkdownCell | |
219 | * @extends IPython.HTMLCell |
|
219 | * @extends IPython.HTMLCell | |
220 | */ |
|
220 | */ | |
221 | var MarkdownCell = function (options) { |
|
221 | var MarkdownCell = function (options, events, config, keyboard_manager) { | |
222 | options = this.mergeopt(MarkdownCell, options); |
|
222 | options = this.mergeopt(MarkdownCell, options); | |
223 |
|
223 | |||
224 | this.cell_type = 'markdown'; |
|
224 | this.cell_type = 'markdown'; | |
225 | TextCell.apply(this, [options]); |
|
225 | TextCell.apply(this, [options, events, config, keyboard_manager]); | |
226 | }; |
|
226 | }; | |
227 |
|
227 | |||
228 | MarkdownCell.options_default = { |
|
228 | MarkdownCell.options_default = { | |
@@ -268,10 +268,10 define([ | |||||
268 | * @constructor RawCell |
|
268 | * @constructor RawCell | |
269 | * @extends TextCell |
|
269 | * @extends TextCell | |
270 | */ |
|
270 | */ | |
271 | var RawCell = function (options) { |
|
271 | var RawCell = function (options, events, config, keyboard_manager) { | |
272 |
|
272 | |||
273 | options = this.mergeopt(RawCell,options); |
|
273 | options = this.mergeopt(RawCell,options); | |
274 | TextCell.apply(this, [options]); |
|
274 | TextCell.apply(this, [options, events, config, keyboard_manager]); | |
275 | this.cell_type = 'raw'; |
|
275 | this.cell_type = 'raw'; | |
276 | // RawCell should always hide its rendered div |
|
276 | // RawCell should always hide its rendered div | |
277 | this.element.find('div.text_cell_render').hide(); |
|
277 | this.element.find('div.text_cell_render').hide(); | |
@@ -327,12 +327,12 define([ | |||||
327 | * @constructor HeadingCell |
|
327 | * @constructor HeadingCell | |
328 | * @extends TextCell |
|
328 | * @extends TextCell | |
329 | */ |
|
329 | */ | |
330 | var HeadingCell = function (options) { |
|
330 | var HeadingCell = function (options, events, config, keyboard_manager) { | |
331 | options = this.mergeopt(HeadingCell, options); |
|
331 | options = this.mergeopt(HeadingCell, options); | |
332 |
|
332 | |||
333 | this.level = 1; |
|
333 | this.level = 1; | |
334 | this.cell_type = 'heading'; |
|
334 | this.cell_type = 'heading'; | |
335 | TextCell.apply(this, [options]); |
|
335 | TextCell.apply(this, [options, events, config, keyboard_manager]); | |
336 |
|
336 | |||
337 | /** |
|
337 | /** | |
338 | * heading level of the cell, use getter and setter to access |
|
338 | * heading level of the cell, use getter and setter to access |
@@ -8,8 +8,8 define([ | |||||
8 | ], function(IPython, $, notebooklist) { |
|
8 | ], function(IPython, $, notebooklist) { | |
9 | "use strict"; |
|
9 | "use strict"; | |
10 |
|
10 | |||
11 | var KernelList = function (selector, options) { |
|
11 | var KernelList = function (selector, options, session_list) { | |
12 |
notebooklist.NotebookList.call(this, selector, options, 'running', |
|
12 | notebooklist.NotebookList.call(this, selector, options, 'running', session_list); | |
13 | }; |
|
13 | }; | |
14 |
|
14 | |||
15 | KernelList.prototype = Object.create(notebooklist.NotebookList.prototype); |
|
15 | KernelList.prototype = Object.create(notebooklist.NotebookList.prototype); |
@@ -1,6 +1,7 | |||||
1 | // Copyright (c) IPython Development Team. |
|
1 | // Copyright (c) IPython Development Team. | |
2 | // Distributed under the terms of the Modified BSD License. |
|
2 | // Distributed under the terms of the Modified BSD License. | |
3 |
|
3 | |||
|
4 | var ipython = ipython || {}; | |||
4 | require([ |
|
5 | require([ | |
5 | 'base/js/namespace', |
|
6 | 'base/js/namespace', | |
6 | 'jquery', |
|
7 | 'jquery', | |
@@ -28,19 +29,16 require([ | |||||
28 |
|
29 | |||
29 | page = new page.Page(); |
|
30 | page = new page.Page(); | |
30 |
|
31 | |||
31 |
var opt |
|
32 | var opts = { | |
32 | base_url: utils.get_body_data("baseUrl"), |
|
33 | base_url: utils.get_body_data("baseUrl"), | |
33 | notebook_path: utils.get_body_data("notebookPath"), |
|
34 | notebook_path: utils.get_body_data("notebookPath"), | |
34 | }; |
|
35 | }; | |
35 |
|
||||
36 | events = $([new events.Events()]); |
|
36 | events = $([new events.Events()]); | |
37 | options.events = events; |
|
37 | session_list = new sesssionlist.SesssionList(opts, events); | |
38 | session_list = new sesssionlist.SesssionList(options); |
|
38 | notebook_list = new notebooklist.NotebookList('#notebook_list', opts, undefined, session_list); | |
39 | options.session_list = session_list; |
|
39 | cluster_list = new clusterlist.ClusterList('#cluster_list', opts); | |
40 |
|
|
40 | kernel_list = new kernellist.KernelList('#running_list', opts, session_list); | |
41 | cluster_list = new clusterlist.ClusterList('#cluster_list', options); |
|
41 | login_widget = new loginwidget.LoginWidget('#login_widget', opts); | |
42 | kernel_list = new kernellist.KernelList('#running_list', options); |
|
|||
43 | login_widget = new loginwidget.LoginWidget('#login_widget', options); |
|
|||
44 |
|
42 | |||
45 | $('#new_notebook').button().click(function (e) { |
|
43 | $('#new_notebook').button().click(function (e) { | |
46 | notebook_list.new_notebook(); |
|
44 | notebook_list.new_notebook(); | |
@@ -106,11 +104,11 require([ | |||||
106 | } |
|
104 | } | |
107 |
|
105 | |||
108 | // For backwards compatability. |
|
106 | // For backwards compatability. | |
109 |
|
|
107 | ipython.page = page; | |
110 |
|
|
108 | ipython.notebook_list = notebook_list; | |
111 |
|
|
109 | ipython.cluster_list = cluster_list; | |
112 |
|
|
110 | ipython.session_list = session_list; | |
113 |
|
|
111 | ipython.kernel_list = kernel_list; | |
114 |
|
|
112 | ipython.login_widget = login_widget; | |
115 |
|
|
113 | ipython.events = events; | |
116 | }); |
|
114 | }); |
@@ -9,9 +9,9 define([ | |||||
9 | ], function(IPython, $, utils, dialog) { |
|
9 | ], function(IPython, $, utils, dialog) { | |
10 | "use strict"; |
|
10 | "use strict"; | |
11 |
|
11 | |||
12 | var NotebookList = function (selector, options, element_name) { |
|
12 | var NotebookList = function (selector, options, element_name, session_list) { | |
13 | var that = this; |
|
13 | var that = this; | |
14 |
this.session_list = |
|
14 | this.session_list = session_list; | |
15 | // allow code re-use by just changing element_name in kernellist.js |
|
15 | // allow code re-use by just changing element_name in kernellist.js | |
16 | this.element_name = element_name || 'notebook'; |
|
16 | this.element_name = element_name || 'notebook'; | |
17 | this.selector = selector; |
|
17 | this.selector = selector; |
@@ -8,8 +8,8 define([ | |||||
8 | ], function(IPython, $, utils) { |
|
8 | ], function(IPython, $, utils) { | |
9 | "use strict"; |
|
9 | "use strict"; | |
10 |
|
10 | |||
11 | var SesssionList = function (options) { |
|
11 | var SesssionList = function (options, events) { | |
12 |
this.events = |
|
12 | this.events = events; | |
13 | this.sessions = {}; |
|
13 | this.sessions = {}; | |
14 | this.base_url = options.base_url || utils.get_body_data("baseUrl"); |
|
14 | this.base_url = options.base_url || utils.get_body_data("baseUrl"); | |
15 | }; |
|
15 | }; |
General Comments 0
You need to be logged in to leave comments.
Login now