##// END OF EJS Templates
Merge pull request #6866 from takluyver/nb-texteditor...
Min RK -
r19078:231dfe88 merge
parent child Browse files
Show More
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,29 b''
1 #encoding: utf-8
2 """Tornado handlers for the terminal emulator."""
3
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
6
7 from tornado import web
8 from ..base.handlers import IPythonHandler, path_regex
9 from ..utils import url_escape
10
11 class EditorHandler(IPythonHandler):
12 """Render the text editor interface."""
13 @web.authenticated
14 def get(self, path):
15 path = path.strip('/')
16 if not self.contents_manager.file_exists(path):
17 raise web.HTTPError(404, u'File does not exist: %s' % path)
18
19 basename = path.rsplit('/', 1)[-1]
20 self.write(self.render_template('edit.html',
21 file_path=url_escape(path),
22 basename=basename,
23 page_title=basename + " (editing)",
24 )
25 )
26
27 default_handlers = [
28 (r"/edit%s" % path_regex, EditorHandler),
29 ] No newline at end of file
@@ -0,0 +1,83 b''
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 define([
5 'jquery',
6 'base/js/notificationwidget',
7 ], function($, notificationwidget) {
8 "use strict";
9
10 // store reference to the NotificationWidget class
11 var NotificationWidget = notificationwidget.NotificationWidget;
12
13 /**
14 * Construct the NotificationArea object. Options are:
15 * events: $(Events) instance
16 * save_widget: SaveWidget instance
17 * notebook: Notebook instance
18 * keyboard_manager: KeyboardManager instance
19 *
20 * @constructor
21 * @param {string} selector - a jQuery selector string for the
22 * notification area element
23 * @param {Object} [options] - a dictionary of keyword arguments.
24 */
25 var NotificationArea = function (selector, options) {
26 this.selector = selector;
27 this.events = options.events;
28 if (this.selector !== undefined) {
29 this.element = $(selector);
30 }
31 this.widget_dict = {};
32 };
33
34 /**
35 * Get a widget by name, creating it if it doesn't exist.
36 *
37 * @method widget
38 * @param {string} name - the widget name
39 */
40 NotificationArea.prototype.widget = function (name) {
41 if (this.widget_dict[name] === undefined) {
42 return this.new_notification_widget(name);
43 }
44 return this.get_widget(name);
45 };
46
47 /**
48 * Get a widget by name, throwing an error if it doesn't exist.
49 *
50 * @method get_widget
51 * @param {string} name - the widget name
52 */
53 NotificationArea.prototype.get_widget = function (name) {
54 if(this.widget_dict[name] === undefined) {
55 throw('no widgets with this name');
56 }
57 return this.widget_dict[name];
58 };
59
60 /**
61 * Create a new notification widget with the given name. The
62 * widget must not already exist.
63 *
64 * @method new_notification_widget
65 * @param {string} name - the widget name
66 */
67 NotificationArea.prototype.new_notification_widget = function (name) {
68 if (this.widget_dict[name] !== undefined) {
69 throw('widget with that name already exists!');
70 }
71
72 // create the element for the notification widget and add it
73 // to the notification aread element
74 var div = $('<div/>').attr('id', 'notification_' + name);
75 $(this.selector).append(div);
76
77 // create the widget object and return it
78 this.widget_dict[name] = new NotificationWidget('#notification_' + name);
79 return this.widget_dict[name];
80 };
81
82 return {'NotificationArea': NotificationArea};
83 });
@@ -0,0 +1,74 b''
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 define([
5 'jquery',
6 'base/js/utils',
7 'codemirror/lib/codemirror',
8 'codemirror/mode/meta',
9 'codemirror/addon/search/search'
10 ],
11 function($,
12 utils,
13 CodeMirror
14 ) {
15 var Editor = function(selector, options) {
16 this.selector = selector;
17 this.contents = options.contents;
18 this.events = options.events;
19 this.base_url = options.base_url;
20 this.file_path = options.file_path;
21
22 this.codemirror = CodeMirror($(this.selector)[0]);
23
24 // It appears we have to set commands on the CodeMirror class, not the
25 // instance. I'd like to be wrong, but since there should only be one CM
26 // instance on the page, this is good enough for now.
27 CodeMirror.commands.save = $.proxy(this.save, this);
28
29 this.save_enabled = false;
30 };
31
32 Editor.prototype.load = function() {
33 var that = this;
34 var cm = this.codemirror;
35 this.contents.get(this.file_path, {type: 'file', format: 'text'})
36 .then(function(model) {
37 cm.setValue(model.content);
38
39 // Find and load the highlighting mode
40 var modeinfo = CodeMirror.findModeByMIME(model.mimetype);
41 if (modeinfo) {
42 utils.requireCodeMirrorMode(modeinfo.mode, function() {
43 cm.setOption('mode', modeinfo.mode);
44 });
45 }
46 that.save_enabled = true;
47 },
48 function(error) {
49 cm.setValue("Error! " + error.message +
50 "\nSaving disabled.");
51 that.save_enabled = false;
52 }
53 );
54 };
55
56 Editor.prototype.save = function() {
57 if (!this.save_enabled) {
58 console.log("Not saving, save disabled");
59 return;
60 }
61 var model = {
62 path: this.file_path,
63 type: 'file',
64 format: 'text',
65 content: this.codemirror.getValue(),
66 };
67 var that = this;
68 this.contents.save(this.file_path, model).then(function() {
69 that.events.trigger("save_succeeded.TextEditor");
70 });
71 };
72
73 return {Editor: Editor};
74 });
@@ -0,0 +1,53 b''
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 require([
5 'base/js/namespace',
6 'base/js/utils',
7 'base/js/page',
8 'base/js/events',
9 'contents',
10 'edit/js/editor',
11 'edit/js/menubar',
12 'edit/js/notificationarea',
13 'custom/custom',
14 ], function(
15 IPython,
16 utils,
17 page,
18 events,
19 contents,
20 editor,
21 menubar,
22 notificationarea
23 ){
24 page = new page.Page();
25
26 var base_url = utils.get_body_data('baseUrl');
27 var file_path = utils.get_body_data('filePath');
28 contents = new contents.Contents({base_url: base_url});
29
30 var editor = new editor.Editor('#texteditor-container', {
31 base_url: base_url,
32 events: events,
33 contents: contents,
34 file_path: file_path,
35 });
36
37 // Make it available for debugging
38 IPython.editor = editor;
39
40 var menus = new menubar.MenuBar('#menubar', {
41 base_url: base_url,
42 editor: editor,
43 });
44
45 var notification_area = new notificationarea.EditorNotificationArea(
46 '#notification_area', {
47 events: events,
48 });
49 notification_area.init_notification_widgets();
50
51 editor.load();
52 page.show();
53 });
@@ -0,0 +1,46 b''
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 define([
5 'base/js/namespace',
6 'jquery',
7 'base/js/utils',
8 'bootstrap',
9 ], function(IPython, $, utils, bootstrap) {
10 "use strict";
11
12 var MenuBar = function (selector, options) {
13 // Constructor
14 //
15 // A MenuBar Class to generate the menubar of IPython notebook
16 //
17 // Parameters:
18 // selector: string
19 // options: dictionary
20 // Dictionary of keyword arguments.
21 // codemirror: CodeMirror instance
22 // contents: ContentManager instance
23 // events: $(Events) instance
24 // base_url : string
25 // file_path : string
26 options = options || {};
27 this.base_url = options.base_url || utils.get_body_data("baseUrl");
28 this.selector = selector;
29 this.editor = options.editor;
30
31 if (this.selector !== undefined) {
32 this.element = $(selector);
33 this.bind_events();
34 }
35 };
36
37 MenuBar.prototype.bind_events = function () {
38 // File
39 var that = this;
40 this.element.find('#save_file').click(function () {
41 that.editor.save();
42 });
43 };
44
45 return {'MenuBar': MenuBar};
46 });
@@ -0,0 +1,29 b''
1 define([
2 'base/js/notificationarea'
3 ], function(notificationarea) {
4 "use strict";
5 var NotificationArea = notificationarea.NotificationArea;
6
7 var EditorNotificationArea = function(selector, options) {
8 NotificationArea.apply(this, [selector, options]);
9 }
10
11 EditorNotificationArea.prototype = Object.create(NotificationArea.prototype);
12
13 /**
14 * Initialize the default set of notification widgets.
15 *
16 * @method init_notification_widgets
17 */
18 EditorNotificationArea.prototype.init_notification_widgets = function () {
19 var that = this;
20 var enw = this.new_notification_widget('editor');
21
22 this.events.on("save_succeeded.TextEditor", function() {
23 enw.set_message("File saved", 2000);
24 });
25 };
26
27
28 return {EditorNotificationArea: EditorNotificationArea};
29 });
@@ -0,0 +1,72 b''
1 {% extends "page.html" %}
2
3 {% block title %}{{page_title}}{% endblock %}
4
5 {% block stylesheet %}
6 <link rel="stylesheet" href="{{ static_url('components/codemirror/lib/codemirror.css') }}">
7 <link rel="stylesheet" href="{{ static_url('components/codemirror/addon/dialog/dialog.css') }}">
8 <style>
9 #texteditor-container {
10 border-bottom: 1px solid #ccc;
11 }
12
13 #filename {
14 font-size: 16pt;
15 display: table;
16 padding: 0px 5px;
17 }
18 </style>
19
20 {{super()}}
21 {% endblock %}
22
23 {% block params %}
24
25 data-base-url="{{base_url}}"
26 data-file-path="{{file_path}}"
27
28 {% endblock %}
29
30 {% block header %}
31
32 <span id="filename">{{ basename }}</span>
33
34 {% endblock %}
35
36 {% block site %}
37
38 <div id="menubar-container" class="container">
39 <div id="menubar">
40 <div id="menus" class="navbar navbar-default" role="navigation">
41 <div class="container-fluid">
42 <button type="button" class="btn btn-default navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
43 <i class="fa fa-bars"></i>
44 <span class="navbar-text">Menu</span>
45 </button>
46 <ul class="nav navbar-nav navbar-right">
47 <li id="notification_area"></li>
48 </ul>
49 <div class="navbar-collapse collapse">
50 <ul class="nav navbar-nav">
51 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
52 <ul id="file_menu" class="dropdown-menu">
53 <li id="save_file"><a href="#">Save</a></li>
54 </ul>
55 </li>
56 </ul>
57 </div>
58 </div>
59 </div>
60 </div>
61 </div>
62
63 <div id="texteditor-container" class="container"></div>
64
65 {% endblock %}
66
67 {% block script %}
68
69 {{super()}}
70
71 <script src="{{ static_url("edit/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
72 {% endblock %}
@@ -199,6 +199,7 b' class NotebookWebApplication(web.Application):'
199 handlers.extend(load_handlers('notebook.handlers'))
199 handlers.extend(load_handlers('notebook.handlers'))
200 handlers.extend(load_handlers('nbconvert.handlers'))
200 handlers.extend(load_handlers('nbconvert.handlers'))
201 handlers.extend(load_handlers('kernelspecs.handlers'))
201 handlers.extend(load_handlers('kernelspecs.handlers'))
202 handlers.extend(load_handlers('edit.handlers'))
202 handlers.extend(load_handlers('services.config.handlers'))
203 handlers.extend(load_handlers('services.config.handlers'))
203 handlers.extend(load_handlers('services.kernels.handlers'))
204 handlers.extend(load_handlers('services.kernels.handlers'))
204 handlers.extend(load_handlers('services.contents.handlers'))
205 handlers.extend(load_handlers('services.contents.handlers'))
@@ -9,6 +9,7 b' import io'
9 import os
9 import os
10 import shutil
10 import shutil
11 from contextlib import contextmanager
11 from contextlib import contextmanager
12 import mimetypes
12
13
13 from tornado import web
14 from tornado import web
14
15
@@ -204,6 +205,7 b' class FileContentsManager(ContentsManager):'
204 model['created'] = created
205 model['created'] = created
205 model['content'] = None
206 model['content'] = None
206 model['format'] = None
207 model['format'] = None
208 model['mimetype'] = None
207 try:
209 try:
208 model['writable'] = os.access(os_path, os.W_OK)
210 model['writable'] = os.access(os_path, os.W_OK)
209 except OSError:
211 except OSError:
@@ -264,8 +266,11 b' class FileContentsManager(ContentsManager):'
264 """
266 """
265 model = self._base_model(path)
267 model = self._base_model(path)
266 model['type'] = 'file'
268 model['type'] = 'file'
269
270 os_path = self._get_os_path(path)
271 model['mimetype'] = mimetypes.guess_type(os_path)[0] or 'text/plain'
272
267 if content:
273 if content:
268 os_path = self._get_os_path(path)
269 if not os.path.isfile(os_path):
274 if not os.path.isfile(os_path):
270 # could be FIFO
275 # could be FIFO
271 raise web.HTTPError(400, "Cannot get content of non-file %s" % os_path)
276 raise web.HTTPError(400, "Cannot get content of non-file %s" % os_path)
1 NO CONTENT: file renamed from IPython/html/static/notebook/js/notificationwidget.js to IPython/html/static/base/js/notificationwidget.js
NO CONTENT: file renamed from IPython/html/static/notebook/js/notificationwidget.js to IPython/html/static/base/js/notificationwidget.js
@@ -105,7 +105,7 b' require(['
105 save_widget: save_widget,
105 save_widget: save_widget,
106 quick_help: quick_help},
106 quick_help: quick_help},
107 common_options));
107 common_options));
108 var notification_area = new notificationarea.NotificationArea(
108 var notification_area = new notificationarea.NotebookNotificationArea(
109 '#notification_area', {
109 '#notification_area', {
110 events: events,
110 events: events,
111 save_widget: save_widget,
111 save_widget: save_widget,
@@ -1,97 +1,29 b''
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
3
4 define([
1 define([
5 'base/js/namespace',
2 'base/js/namespace',
6 'jquery',
3 'jquery',
7 'base/js/utils',
4 'base/js/utils',
8 'base/js/dialog',
5 'base/js/dialog',
9 'notebook/js/notificationwidget',
6 'base/js/notificationarea',
10 'moment'
7 'moment'
11 ], function(IPython, $, utils, dialog, notificationwidget, moment) {
8 ], function(IPython, $, utils, dialog, notificationarea, moment) {
12 "use strict";
9 "use strict";
13
10 var NotificationArea = notificationarea.NotificationArea;
14 // store reference to the NotificationWidget class
11
15 var NotificationWidget = notificationwidget.NotificationWidget;
12 var NotebookNotificationArea = function(selector, options) {
16
13 NotificationArea.apply(this, [selector, options]);
17 /**
18 * Construct the NotificationArea object. Options are:
19 * events: $(Events) instance
20 * save_widget: SaveWidget instance
21 * notebook: Notebook instance
22 * keyboard_manager: KeyboardManager instance
23 *
24 * @constructor
25 * @param {string} selector - a jQuery selector string for the
26 * notification area element
27 * @param {Object} [options] - a dictionary of keyword arguments.
28 */
29 var NotificationArea = function (selector, options) {
30 this.selector = selector;
31 this.events = options.events;
32 this.save_widget = options.save_widget;
14 this.save_widget = options.save_widget;
33 this.notebook = options.notebook;
15 this.notebook = options.notebook;
34 this.keyboard_manager = options.keyboard_manager;
16 this.keyboard_manager = options.keyboard_manager;
35 if (this.selector !== undefined) {
17 }
36 this.element = $(selector);
18
37 }
19 NotebookNotificationArea.prototype = Object.create(NotificationArea.prototype);
38 this.widget_dict = {};
20
39 };
40
41 /**
42 * Get a widget by name, creating it if it doesn't exist.
43 *
44 * @method widget
45 * @param {string} name - the widget name
46 */
47 NotificationArea.prototype.widget = function (name) {
48 if (this.widget_dict[name] === undefined) {
49 return this.new_notification_widget(name);
50 }
51 return this.get_widget(name);
52 };
53
54 /**
55 * Get a widget by name, throwing an error if it doesn't exist.
56 *
57 * @method get_widget
58 * @param {string} name - the widget name
59 */
60 NotificationArea.prototype.get_widget = function (name) {
61 if(this.widget_dict[name] === undefined) {
62 throw('no widgets with this name');
63 }
64 return this.widget_dict[name];
65 };
66
67 /**
68 * Create a new notification widget with the given name. The
69 * widget must not already exist.
70 *
71 * @method new_notification_widget
72 * @param {string} name - the widget name
73 */
74 NotificationArea.prototype.new_notification_widget = function (name) {
75 if (this.widget_dict[name] !== undefined) {
76 throw('widget with that name already exists!');
77 }
78
79 // create the element for the notification widget and add it
80 // to the notification aread element
81 var div = $('<div/>').attr('id', 'notification_' + name);
82 $(this.selector).append(div);
83
84 // create the widget object and return it
85 this.widget_dict[name] = new NotificationWidget('#notification_' + name);
86 return this.widget_dict[name];
87 };
88
89 /**
21 /**
90 * Initialize the default set of notification widgets.
22 * Initialize the default set of notification widgets.
91 *
23 *
92 * @method init_notification_widgets
24 * @method init_notification_widgets
93 */
25 */
94 NotificationArea.prototype.init_notification_widgets = function () {
26 NotebookNotificationArea.prototype.init_notification_widgets = function () {
95 this.init_kernel_notification_widget();
27 this.init_kernel_notification_widget();
96 this.init_notebook_notification_widget();
28 this.init_notebook_notification_widget();
97 };
29 };
@@ -101,7 +33,7 b' define(['
101 *
33 *
102 * @method init_kernel_notification_widget
34 * @method init_kernel_notification_widget
103 */
35 */
104 NotificationArea.prototype.init_kernel_notification_widget = function () {
36 NotebookNotificationArea.prototype.init_kernel_notification_widget = function () {
105 var that = this;
37 var that = this;
106 var knw = this.new_notification_widget('kernel');
38 var knw = this.new_notification_widget('kernel');
107 var $kernel_ind_icon = $("#kernel_indicator_icon");
39 var $kernel_ind_icon = $("#kernel_indicator_icon");
@@ -324,7 +256,7 b' define(['
324 *
256 *
325 * @method init_notebook_notification_widget
257 * @method init_notebook_notification_widget
326 */
258 */
327 NotificationArea.prototype.init_notebook_notification_widget = function () {
259 NotebookNotificationArea.prototype.init_notebook_notification_widget = function () {
328 var nnw = this.new_notification_widget('notebook');
260 var nnw = this.new_notification_widget('notebook');
329
261
330 // Notebook events
262 // Notebook events
@@ -381,7 +313,8 b' define(['
381 });
313 });
382 };
314 };
383
315
384 IPython.NotificationArea = NotificationArea;
316 // Backwards compatibility.
385
317 IPython.NotificationArea = NotebookNotificationArea;
386 return {'NotificationArea': NotificationArea};
318
319 return {'NotebookNotificationArea': NotebookNotificationArea};
387 });
320 });
General Comments 0
You need to be logged in to leave comments. Login now