##// END OF EJS Templates
add File/Rename
Min RK -
Show More
@@ -1,80 +1,81 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 require([
5 5 'base/js/namespace',
6 6 'base/js/utils',
7 7 'base/js/page',
8 8 'base/js/events',
9 9 'contents',
10 10 'services/config',
11 11 'edit/js/editor',
12 12 'edit/js/menubar',
13 13 'edit/js/savewidget',
14 14 'edit/js/notificationarea',
15 15 'custom/custom',
16 16 ], function(
17 17 IPython,
18 18 utils,
19 19 page,
20 20 events,
21 21 contents,
22 22 configmod,
23 23 editmod,
24 24 menubar,
25 25 savewidget,
26 26 notificationarea
27 27 ){
28 28 page = new page.Page();
29 29
30 30 var base_url = utils.get_body_data('baseUrl');
31 31 var file_path = utils.get_body_data('filePath');
32 32 contents = new contents.Contents({base_url: base_url});
33 33 var config = new configmod.ConfigSection('edit', {base_url: base_url});
34 34 config.load();
35 35
36 36 var editor = new editmod.Editor('#texteditor-container', {
37 37 base_url: base_url,
38 38 events: events,
39 39 contents: contents,
40 40 file_path: file_path,
41 41 config: config,
42 42 });
43 43
44 44 // Make it available for debugging
45 45 IPython.editor = editor;
46 46
47 var menus = new menubar.MenuBar('#menubar', {
48 base_url: base_url,
47 var save_widget = new savewidget.SaveWidget('span#save_widget', {
49 48 editor: editor,
50 49 events: events,
51 50 });
52 51
53 var save_widget = new savewidget.SaveWidget('span#save_widget', {
52 var menus = new menubar.MenuBar('#menubar', {
53 base_url: base_url,
54 54 editor: editor,
55 55 events: events,
56 save_widget: save_widget,
56 57 });
57 58
58 59 var notification_area = new notificationarea.EditorNotificationArea(
59 60 '#notification_area', {
60 61 events: events,
61 62 });
62 63 notification_area.init_notification_widgets();
63 64
64 65 config.loaded.then(function() {
65 66 if (config.data.load_extensions) {
66 67 var nbextension_paths = Object.getOwnPropertyNames(
67 68 config.data.load_extensions);
68 69 IPython.load_extensions.apply(this, nbextension_paths);
69 70 }
70 71 });
71 72 editor.load();
72 73 page.show();
73 74
74 75 window.onbeforeunload = function () {
75 76 if (!editor.codemirror.isClean(editor.generation)) {
76 77 return "Unsaved changes will be lost. Close anyway?";
77 78 }
78 79 };
79 80
80 81 });
@@ -1,121 +1,126 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'base/js/utils',
8 8 'base/js/dialog',
9 9 'bootstrap',
10 10 ], function(IPython, $, utils, dialog, bootstrap) {
11 11 "use strict";
12 12
13 13 var MenuBar = function (selector, options) {
14 14 /**
15 15 * Constructor
16 16 *
17 17 * A MenuBar Class to generate the menubar of IPython notebook
18 18 *
19 19 * Parameters:
20 20 * selector: string
21 21 * options: dictionary
22 22 * Dictionary of keyword arguments.
23 23 * codemirror: CodeMirror instance
24 24 * contents: ContentManager instance
25 25 * events: $(Events) instance
26 26 * base_url : string
27 27 * file_path : string
28 28 */
29 29 options = options || {};
30 30 this.base_url = options.base_url || utils.get_body_data("baseUrl");
31 31 this.selector = selector;
32 32 this.editor = options.editor;
33 33 this.events = options.events;
34 this.save_widget = options.save_widget;
34 35
35 36 if (this.selector !== undefined) {
36 37 this.element = $(selector);
37 38 this.bind_events();
38 39 }
40 Object.seal(this);
39 41 };
40 42
41 43 MenuBar.prototype.bind_events = function () {
42 44 var that = this;
43 45 var editor = that.editor;
44 46
45 47 // File
46 48 this.element.find('#new-file').click(function () {
47 49 var w = window.open();
48 50 // Create a new file in the current directory
49 51 var parent = utils.url_path_split(editor.file_path)[0];
50 52 editor.contents.new_untitled(parent, {type: "file"}).then(
51 53 function (data) {
52 54 w.location = utils.url_join_encode(
53 55 that.base_url, 'edit', data.path
54 56 );
55 57 },
56 58 function(error) {
57 59 w.close();
58 60 dialog.modal({
59 61 title : 'Creating New File Failed',
60 62 body : "The error was: " + error.message,
61 63 buttons : {'OK' : {'class' : 'btn-primary'}}
62 64 });
63 65 }
64 66 );
65 67 });
66 68 this.element.find('#save-file').click(function () {
67 69 editor.save();
68 70 });
71 this.element.find('#rename-file').click(function () {
72 that.save_widget.rename();
73 });
69 74
70 75 // Edit
71 76 this.element.find('#menu-find').click(function () {
72 77 editor.codemirror.execCommand("find");
73 78 });
74 79 this.element.find('#menu-replace').click(function () {
75 80 editor.codemirror.execCommand("replace");
76 81 });
77 82 this.element.find('#menu-keymap-default').click(function () {
78 83 editor.update_codemirror_options({
79 84 vimMode: false,
80 85 keyMap: 'default'
81 86 });
82 87 });
83 88 this.element.find('#menu-keymap-sublime').click(function () {
84 89 editor.update_codemirror_options({
85 90 vimMode: false,
86 91 keyMap: 'sublime'
87 92 });
88 93 });
89 94 this.element.find('#menu-keymap-emacs').click(function () {
90 95 editor.update_codemirror_options({
91 96 vimMode: false,
92 97 keyMap: 'emacs'
93 98 });
94 99 });
95 100 this.element.find('#menu-keymap-vim').click(function () {
96 101 editor.update_codemirror_options({
97 102 vimMode: true,
98 103 keyMap: 'vim'
99 104 });
100 105 });
101 106
102 107 // View
103 108 this.element.find('#menu-line-numbers').click(function () {
104 109 var current = editor.codemirror.getOption('lineNumbers');
105 110 var value = Boolean(1-current);
106 111 editor.update_codemirror_options({lineNumbers: value});
107 112 });
108 113
109 114 this.events.on("config_changed.Editor", function () {
110 115 var lineNumbers = editor.codemirror.getOption('lineNumbers');
111 116 var text = lineNumbers ? "Hide" : "Show";
112 117 text = text + " Line Numbers";
113 118 that.element.find('#menu-line-numbers').find("a").text(text);
114 119 var keyMap = editor.codemirror.getOption('keyMap') || "default";
115 120 that.element.find(".selected-keymap").removeClass("selected-keymap");
116 121 that.element.find("#menu-keymap-" + keyMap).addClass("selected-keymap");
117 122 });
118 123 };
119 124
120 125 return {'MenuBar': MenuBar};
121 126 });
@@ -1,202 +1,202 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'base/js/utils',
8 8 'base/js/dialog',
9 9 'base/js/keyboard',
10 10 'moment',
11 11 ], function(IPython, $, utils, dialog, keyboard, moment) {
12 12 "use strict";
13 13
14 14 var SaveWidget = function (selector, options) {
15 15 this.editor = undefined;
16 16 this.selector = selector;
17 17 this.events = options.events;
18 18 this.editor = options.editor;
19 19 this._last_modified = undefined;
20 20 this.keyboard_manager = options.keyboard_manager;
21 21 if (this.selector !== undefined) {
22 22 this.element = $(selector);
23 23 this.bind_events();
24 24 }
25 25 };
26 26
27 27
28 28 SaveWidget.prototype.bind_events = function () {
29 29 var that = this;
30 30 this.element.find('span.filename').click(function () {
31 that.rename({editor: that.editor});
31 that.rename();
32 32 });
33 33 this.events.on('file_loaded.Editor', function (evt, model) {
34 34 that.update_filename(model.name);
35 35 that.update_document_title(model.name);
36 36 that.update_last_modified(model.last_modified);
37 37 });
38 38 this.events.on('file_saved.Editor', function (evt, model) {
39 39 that.update_filename(model.name);
40 40 that.update_document_title(model.name);
41 41 that.update_last_modified(model.last_modified);
42 42 });
43 43 this.events.on('file_renamed.Editor', function (evt, model) {
44 44 that.update_filename(model.name);
45 45 that.update_document_title(model.name);
46 46 that.update_address_bar(model.path);
47 47 });
48 48 this.events.on('file_save_failed.Editor', function () {
49 49 that.set_save_status('Save Failed!');
50 50 });
51 51 };
52 52
53 53
54 54 SaveWidget.prototype.rename = function (options) {
55 55 options = options || {};
56 56 var that = this;
57 57 var dialog_body = $('<div/>').append(
58 58 $("<p/>").addClass("rename-message")
59 59 .text('Enter a new filename:')
60 60 ).append(
61 61 $("<br/>")
62 62 ).append(
63 63 $('<input/>').attr('type','text').attr('size','25').addClass('form-control')
64 .val(options.editor.get_filename())
64 .val(that.editor.get_filename())
65 65 );
66 66 var d = dialog.modal({
67 67 title: "Rename File",
68 68 body: dialog_body,
69 69 buttons : {
70 70 "OK": {
71 71 class: "btn-primary",
72 72 click: function () {
73 73 var new_name = d.find('input').val();
74 74 d.find('.rename-message').text("Renaming...");
75 75 d.find('input[type="text"]').prop('disabled', true);
76 76 that.editor.rename(new_name).then(
77 77 function () {
78 78 d.modal('hide');
79 79 }, function (error) {
80 80 d.find('.rename-message').text(error.message || 'Unknown error');
81 81 d.find('input[type="text"]').prop('disabled', false).focus().select();
82 82 }
83 83 );
84 84 return false;
85 85 }
86 86 },
87 87 "Cancel": {}
88 88 },
89 89 open : function () {
90 90 // Upon ENTER, click the OK button.
91 91 d.find('input[type="text"]').keydown(function (event) {
92 92 if (event.which === keyboard.keycodes.enter) {
93 93 d.find('.btn-primary').first().click();
94 94 return false;
95 95 }
96 96 });
97 97 d.find('input[type="text"]').focus().select();
98 98 }
99 99 });
100 100 };
101 101
102 102
103 103 SaveWidget.prototype.update_filename = function (filename) {
104 104 this.element.find('span.filename').text(filename);
105 105 };
106 106
107 107 SaveWidget.prototype.update_document_title = function (filename) {
108 108 document.title = filename;
109 109 };
110 110
111 111 SaveWidget.prototype.update_address_bar = function (path) {
112 112 var state = {path : path};
113 113 window.history.replaceState(state, "", utils.url_join_encode(
114 114 this.editor.base_url,
115 115 "edit",
116 116 path)
117 117 );
118 118 };
119 119
120 120 SaveWidget.prototype.update_last_modified = function (last_modified) {
121 121 if (last_modified) {
122 122 this._last_modified = new Date(last_modified);
123 123 } else {
124 124 this._last_modified = null;
125 125 }
126 126 this._render_last_modified();
127 127 };
128 128
129 129 SaveWidget.prototype._render_last_modified = function () {
130 130 /** actually set the text in the element, from our _last_modified value
131 131
132 132 called directly, and periodically in timeouts.
133 133 */
134 134 this._schedule_render_last_modified();
135 135 var el = this.element.find('span.last_modified');
136 136 if (!this._last_modified) {
137 137 el.text('').attr('title', 'never saved');
138 138 return;
139 139 }
140 140 var chkd = moment(this._last_modified);
141 141 var long_date = chkd.format('llll');
142 142 var human_date;
143 143 var tdelta = Math.ceil(new Date() - this._last_modified);
144 144 if (tdelta < 24 * H){
145 145 // less than 24 hours old, use relative date
146 146 human_date = chkd.fromNow();
147 147 } else {
148 148 // otherwise show calendar
149 149 // otherwise update every hour and show
150 150 // <Today | yesterday|...> at hh,mm,ss
151 151 human_date = chkd.calendar();
152 152 }
153 153 el.text(human_date).attr('title', long_date);
154 154 };
155 155
156 156
157 157 var S = 1000;
158 158 var M = 60*S;
159 159 var H = 60*M;
160 160 var thresholds = {
161 161 s: 45 * S,
162 162 m: 45 * M,
163 163 h: 22 * H
164 164 };
165 165 var _timeout_from_dt = function (ms) {
166 166 /** compute a timeout to update the last-modified timeout
167 167
168 168 based on the delta in milliseconds
169 169 */
170 170 if (ms < thresholds.s) {
171 171 return 5 * S;
172 172 } else if (ms < thresholds.m) {
173 173 return M;
174 174 } else {
175 175 return 5 * M;
176 176 }
177 177 };
178 178
179 179 SaveWidget.prototype._schedule_render_last_modified = function () {
180 180 /** schedule the next update to relative date
181 181
182 182 periodically updated, so short values like 'a few seconds ago' don't get stale.
183 183 */
184 184 var that = this;
185 185 if (!this._last_modified) {
186 186 return;
187 187 }
188 188 if ((this._last_modified_timeout)) {
189 189 clearTimeout(this._last_modified_timeout);
190 190 }
191 191 var dt = Math.ceil(new Date() - this._last_modified);
192 192 if (dt < 24 * H) {
193 193 this._last_modified_timeout = setTimeout(
194 194 $.proxy(this._render_last_modified, this),
195 195 _timeout_from_dt(dt)
196 196 );
197 197 }
198 198 };
199 199
200 200 return {'SaveWidget': SaveWidget};
201 201
202 202 });
@@ -1,81 +1,82 b''
1 1 {% extends "page.html" %}
2 2
3 3 {% block title %}{{page_title}}{% endblock %}
4 4
5 5 {% block stylesheet %}
6 6 <link rel="stylesheet" href="{{ static_url('components/codemirror/lib/codemirror.css') }}">
7 7 <link rel="stylesheet" href="{{ static_url('components/codemirror/addon/dialog/dialog.css') }}">
8 8 {{super()}}
9 9 {% endblock %}
10 10
11 11 {% block params %}
12 12
13 13 data-base-url="{{base_url}}"
14 14 data-file-path="{{file_path}}"
15 15
16 16 {% endblock %}
17 17
18 18 {% block header %}
19 19
20 20 <span id="save_widget" class="nav pull-left save_widget">
21 21 <span class="filename"></span>
22 22 <span class="last_modified"></span>
23 23 </span>
24 24
25 25 {% endblock %}
26 26
27 27 {% block site %}
28 28
29 29 <div id="menubar-container" class="container">
30 30 <div id="menubar">
31 31 <div id="menus" class="navbar navbar-default" role="navigation">
32 32 <div class="container-fluid">
33 33 <button type="button" class="btn btn-default navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
34 34 <i class="fa fa-bars"></i>
35 35 <span class="navbar-text">Menu</span>
36 36 </button>
37 37 <ul class="nav navbar-nav navbar-right">
38 38 <li id="notification_area"></li>
39 39 </ul>
40 40 <div class="navbar-collapse collapse">
41 41 <ul class="nav navbar-nav">
42 42 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
43 43 <ul id="file-menu" class="dropdown-menu">
44 44 <li id="new-file"><a href="#">New</a></li>
45 45 <li id="save-file"><a href="#">Save</a></li>
46 <li id="rename-file"><a href="#">Rename</a></li>
46 47 </ul>
47 48 </li>
48 49 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
49 50 <ul id="edit-menu" class="dropdown-menu">
50 51 <li id="menu-find"><a href="#">Find</a></li>
51 52 <li id="menu-replace"><a href="#">Find &amp; Replace</a></li>
52 53 <li class="divider"></li>
53 54 <li class="dropdown-header">Key Map</li>
54 55 <li id="menu-keymap-default"><a href="#">Default<i class="fa"></i></a></li>
55 56 <li id="menu-keymap-sublime"><a href="#">Sublime Text<i class="fa"></i></a></li>
56 57 <li id="menu-keymap-vim"><a href="#">Vim<i class="fa"></i></a></li>
57 58 <li id="menu-keymap-emacs"><a href="#">emacs<i class="fa"></i></a></li>
58 59 </ul>
59 60 </li>
60 61 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
61 62 <ul id="view-menu" class="dropdown-menu">
62 63 <li id="menu-line-numbers"><a href="#">Hide Line Numbers</a></li>
63 64 </ul>
64 65 </li>
65 66 </ul>
66 67 </div>
67 68 </div>
68 69 </div>
69 70 </div>
70 71 </div>
71 72
72 73 <div id="texteditor-container" class="container"></div>
73 74
74 75 {% endblock %}
75 76
76 77 {% block script %}
77 78
78 79 {{super()}}
79 80
80 81 <script src="{{ static_url("edit/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
81 82 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now