1 | {{main_id='editor_main'}} |
---|
2 | {{extend 'layout.html'}} |
---|
3 | {{ |
---|
4 | dirs=[{'name':'models', 'reg':'.*\.py$'}, |
---|
5 | {'name':'controllers', 'reg':'.*\.py$'}, |
---|
6 | {'name':'views', 'reg':'[\w/\-]+(\.\w+)+$'}, |
---|
7 | {'name':'modules', 'reg':'.*\.py$'}, |
---|
8 | {'name':'static', 'reg': '[^\.#].*'}, |
---|
9 | {'name':'private', 'reg':'.*\.py$'}] |
---|
10 | |
---|
11 | def file_create_form(location, anchor=None, helptext=""): |
---|
12 | form=FORM( |
---|
13 | LABEL(T("create file with filename:")), |
---|
14 | SELECT(_name='dir', _style='width:100px;', |
---|
15 | *[OPTION(dir['name'], _value=dir['name']) for dir in dirs]), |
---|
16 | XML(' '),LABEL('/', _style='display:inline-block;'),XML(' '), |
---|
17 | INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY(),_class=''), |
---|
18 | TAG['SMALL'](helptext,_class="help-block"), |
---|
19 | INPUT(_type='submit', name=T('filename'), _value=T('Create'), _style='display:block', _id='btn_file_create'), |
---|
20 | INPUT(_type="hidden",_name="editor"), |
---|
21 | INPUT(_type="hidden",_name="location",_value=location), |
---|
22 | INPUT(_type="hidden",_name="sender",_value=URL('design',args=app)), |
---|
23 | INPUT(_type="hidden",_name="token",_value=session.token), |
---|
24 | #INPUT(_type="hidden",_name="id",_value=anchor), |
---|
25 | _action=URL('create_file'), |
---|
26 | _id='file_create_form', |
---|
27 | _class="generatedbyw2p well well-small") |
---|
28 | return form |
---|
29 | |
---|
30 | }} |
---|
31 | |
---|
32 | <!-- begin "edit" block --> |
---|
33 | {{ |
---|
34 | def shortcut(combo, description): |
---|
35 | return XML('<li class="span5"><span class="teletype-text">%s</span><span>%s</span></li>' % (combo, description)) |
---|
36 | def listfiles(app, dir, regexp='.*\.py$'): |
---|
37 | files = sorted( |
---|
38 | listdir(apath('%(app)s/%(dir)s/' % {'app':app, 'dir':dir}, r=request), regexp)) |
---|
39 | files = [x.replace(os.path.sep, '/') for x in files if not x.endswith('.bak')] |
---|
40 | return files |
---|
41 | |
---|
42 | def editfile(path,file,vars={}): |
---|
43 | args=(path,file) if 'app' in vars else (app,path,file) |
---|
44 | url = URL('edit', args=args, vars=vars) |
---|
45 | return A(file, _class='editor_filelink', _href=url, _style='word-wrap: nowrap;') |
---|
46 | }} |
---|
47 | {{cm=URL('static', 'codemirror')}} |
---|
48 | {{js_url=URL('static', 'js')}} |
---|
49 | {{css_url=URL('static', 'css')}} |
---|
50 | <link rel="stylesheet" href="{{=cm}}/lib/codemirror.css"> |
---|
51 | <link rel="stylesheet" href="{{='%s/theme/%s.css' % (cm, editor_settings['theme'])}}"> |
---|
52 | <script src="{{=cm}}/lib/codemirror.js"></script> |
---|
53 | <script src="{{=cm}}/addon/edit/matchbrackets.js"></script> |
---|
54 | <script src="{{=cm}}/addon/edit/closetag.js"></script> |
---|
55 | {{if editor_settings['editor'] != 'default':}}<script src="{{='%s/keymap/%s.js' % (cm, editor_settings['editor'])}}"></script>{{pass}} |
---|
56 | <script src="{{=cm}}/mode/python/python.js"></script> |
---|
57 | <script src="{{=cm}}/mode/xml/xml.js"></script> |
---|
58 | <script src="{{=cm}}/mode/css/css.js"></script> |
---|
59 | <script src="{{=cm}}/mode/javascript/javascript.js"></script> |
---|
60 | <script src="{{=cm}}/mode/htmlmixed/htmlmixed.js"></script> |
---|
61 | <script src="{{=cm}}/addon/hint/show-hint.js"></script> |
---|
62 | <script src="{{=cm}}/addon/hint/python-hint.js"></script> |
---|
63 | <link rel="stylesheet" href="{{=cm}}/addon/hint/show-hint.css"> |
---|
64 | <script src="{{=cm}}/addon/search/search.js"></script> |
---|
65 | <script src="{{=cm}}/addon/search/searchcursor.js"></script> |
---|
66 | <script src="{{=cm}}/addon/dialog/dialog.js"></script> |
---|
67 | <script src="{{=cm}}/addon/edit/trailingspace.js"></script> |
---|
68 | <link rel="stylesheet" href="{{=cm}}/addon/dialog/dialog.css"> |
---|
69 | <script src="{{=cm}}/addon/selection/active-line.js"></script> |
---|
70 | <script src="{{=cm}}/addon/display/fullscreen.js"></script> |
---|
71 | <link rel="stylesheet" href="{{=cm}}/addon/display/fullscreen.css"> |
---|
72 | <script src="{{=cm}}/addon/fold/foldcode.js"></script> |
---|
73 | <script src="{{=cm}}/addon/fold/foldgutter.js"></script> |
---|
74 | <script src="{{=cm}}/addon/fold/brace-fold.js"></script> |
---|
75 | <script src="{{=cm}}/addon/fold/xml-fold.js"></script> |
---|
76 | <script src="{{=cm}}/addon/fold/comment-fold.js"></script> |
---|
77 | <script src="{{=cm}}/addon/fold/indent-fold.js"></script> |
---|
78 | <script src="{{=cm}}/addon/comment/comment.js"></script> |
---|
79 | <link rel="stylesheet" href="{{=cm}}/addon/fold/foldgutter.css"> |
---|
80 | <script src="{{=cm}}/emmet.min.js"></script> |
---|
81 | <script src="{{=js_url}}/ajax_editor.js"></script> |
---|
82 | <link rel="stylesheet" href="{{=css_url}}/typeahead.js-bootstrap.css"> |
---|
83 | <link rel="stylesheet" href="{{=css_url}}/web2py-codemirror.css"> |
---|
84 | <script type="text/javascript"> |
---|
85 | var current_font_incr = 0; // Default font-size, 0 means isn't set |
---|
86 | $(document).on('shown click', 'a[data-toggle="tab"]', function (e, lineno) { |
---|
87 | var tab_id = $(this).attr('href'); |
---|
88 | var editor = $(tab_id + " textarea").data('editor'); |
---|
89 | if (editor) { |
---|
90 | editor_height = $(window).height() - $(tab_id + " .well-small").offset().top - $(tab_id + " .well-small").outerHeight(true) - $('.navbar-fixed-bottom').outerHeight() - 60; |
---|
91 | editor.setSize('100%', editor_height); |
---|
92 | editor.refresh(); |
---|
93 | if (lineno !== undefined) { |
---|
94 | editor.setCursor(lineno); |
---|
95 | editor.centerOnCursor(); |
---|
96 | } |
---|
97 | } |
---|
98 | var n_li = $('#filesTab li').length; |
---|
99 | $.each($('#filesTab li'), function(index, element) { |
---|
100 | $(element).css('max-width', 100/n_li + '%' ); |
---|
101 | }); |
---|
102 | //$(function(){$('.CodeMirror-scroll').css("height","auto").css("overflow-x","auto");}); |
---|
103 | }); |
---|
104 | |
---|
105 | // Close the selected tab |
---|
106 | $(document).on('click', '#filesTab button[class="close"]', function (e) { |
---|
107 | var tab_body = $($(this).parent().attr("href")); // it should be a div |
---|
108 | var tab_header = $(this).parent().parent(); // it should be a li |
---|
109 | var saved = $(tab_body.find('textarea').data('editor')).data('saved'); |
---|
110 | var close = true; |
---|
111 | if (saved === false) { |
---|
112 | close = confirm("You are closing an unsaved file") |
---|
113 | } |
---|
114 | if (close) { |
---|
115 | if (tab_header.hasClass('active') === true) { //Set active an other tab |
---|
116 | var $prev = $(tab_header).prev(); |
---|
117 | if ($prev.length) { |
---|
118 | $prev.children('a[data-toggle="tab"]').tab('show'); // select previous tab |
---|
119 | } else { |
---|
120 | $(tab_header).next().children('a[data-toggle="tab"]').tab('show'); // select next tab |
---|
121 | } |
---|
122 | } |
---|
123 | tab_header.remove(); //remove li of tab |
---|
124 | tab_body.remove(); //remove tab content (div) |
---|
125 | } |
---|
126 | }); |
---|
127 | |
---|
128 | // Revert current file |
---|
129 | $(document).on('click', '#revert', function (e) { |
---|
130 | e.preventDefault(); |
---|
131 | load_file($(this).attr("href")); |
---|
132 | }); |
---|
133 | // Restore current file |
---|
134 | $(document).on('click', '#restore', function (e) { |
---|
135 | e.preventDefault(); |
---|
136 | load_file($(this).attr("href")); |
---|
137 | }); |
---|
138 | |
---|
139 | // open the selected file |
---|
140 | $(document).on('click', 'a.editor_filelink, a#editor_settingslink', function (e) { |
---|
141 | e.preventDefault(); |
---|
142 | var url = $(this).attr("href"); |
---|
143 | var lineno = $(this).data("lineno"); |
---|
144 | load_file(url, lineno); |
---|
145 | }); |
---|
146 | |
---|
147 | /* This method updates all editors already instantiated with the selected preferences*/ |
---|
148 | function update_editor(preferences) { |
---|
149 | var href = "{{="%s/theme/" % cm}}" + preferences.theme + ".css"; |
---|
150 | var link = $("<link>"); |
---|
151 | link.attr({ |
---|
152 | type: 'text/css', |
---|
153 | rel: 'stylesheet', |
---|
154 | href: href |
---|
155 | }); |
---|
156 | $("head").append( link ); |
---|
157 | if ( preferences.editor != 'default'){ |
---|
158 | var src = "{{='%s/keymap/' % cm}}" + preferences.editor + ".js"; |
---|
159 | //var src = "/admin/static/codemirror/keymap/" + editor_name + ".js"; |
---|
160 | var script = $("<script>"); |
---|
161 | script.attr({ |
---|
162 | src: src |
---|
163 | }); |
---|
164 | $("head").append( script ); |
---|
165 | } |
---|
166 | |
---|
167 | $('textarea[name="data"]') .each(function(id, ta) { |
---|
168 | editor = $(ta).data('editor'); |
---|
169 | editor.setOption("theme", preferences.theme); |
---|
170 | editor.setOption("keyMap", preferences.editor); |
---|
171 | editor.setOption("autoCloseTags", (preferences.closetag === 'true')); |
---|
172 | editor.setOption("foldGutter", (preferences.codefolding === 'true')); |
---|
173 | editor.setOption("indentUnit", parseInt(preferences.tabwidth)); |
---|
174 | editor.setOption("tabSize", parseInt(preferences.tabwidth)); |
---|
175 | editor.setOption("indentWithTabs", (preferences.indentwithtabs === 'true')); |
---|
176 | gutters = editor.getOption("gutters"); |
---|
177 | if ( preferences.linenumbers === 'true' && $.inArray('CodeMirror-linenumbers', gutters) === -1 ) { |
---|
178 | gutters.splice(0, 0, 'CodeMirror-linenumbers'); |
---|
179 | editor.setOption("gutter", gutters); |
---|
180 | } |
---|
181 | editor.setOption("lineNumbers", (preferences.linenumbers === 'true')); |
---|
182 | editor.setOption("styleActiveLine", (preferences.highlightline === 'true')); |
---|
183 | }); |
---|
184 | } |
---|
185 | |
---|
186 | // incr/decr editor font-size |
---|
187 | $(document).on('click', 'a.font_button', function (e) { |
---|
188 | e.preventDefault(); |
---|
189 | var id = $(this).attr('id'); |
---|
190 | var new_font_incr; |
---|
191 | switch (id) { |
---|
192 | case 'incr': new_incr = 2; break; |
---|
193 | case 'decr': new_incr = -2; break; |
---|
194 | case 'default': new_incr = 0; |
---|
195 | } |
---|
196 | $('textarea[name="data"]').each(function(id, ta) { |
---|
197 | editor = $(ta).data('editor'); |
---|
198 | set_font(editor, new_incr); |
---|
199 | }); |
---|
200 | current_font_incr = (new_incr !== 0) ? current_font_incr + new_incr : 0; |
---|
201 | }); |
---|
202 | </script> |
---|
203 | |
---|
204 | {{block sectionclass}}edit{{end}} |
---|
205 | |
---|
206 | <div class='row-fluid'> |
---|
207 | <div class="right controls btn-toolbar pull-right"> |
---|
208 | |
---|
209 | {{=LOAD('default', 'editor_sessions', ajax=True, _class='btn-group')}} |
---|
210 | |
---|
211 | <div class="btn-group"> |
---|
212 | <a class="button btn" onclick="$('#files').toggle(); return false" href="#">Files toggle</a> |
---|
213 | </div> |
---|
214 | <div class="btn-group"> |
---|
215 | <a id="decr" class="button btn font_button">-</a> |
---|
216 | <a id="default" class="button btn font_button" >A</a> |
---|
217 | <a id="incr" class="button btn font_button">+</a> |
---|
218 | </div> |
---|
219 | <div class="btn-group"> |
---|
220 | {{=button(URL('design',args=request.vars.app if request.vars.app else request.args[0], anchor=request.vars.id), T('back'))}} |
---|
221 | <a class="button btn" href="http://web2py.readthedocs.org/en/latest/" target="_blank"><span>{{=T('docs')}}</span></a> |
---|
222 | <a class="button btn" title="{{=T('change editor settings')}}" id="editor_settingslink" href="{{=URL('default', 'edit', args=request.args, vars={'settings':True})}}"><i class="icon-cog"></i></a> |
---|
223 | </div> |
---|
224 | </div> |
---|
225 | </div> |
---|
226 | <div id="editor_area" class="row-fluid"> |
---|
227 | <div id="files"> |
---|
228 | <div class="files-toggle"><span class="arrow"></span></div> |
---|
229 | <div class="files-menu"> |
---|
230 | <ul class="nav nav-list" rel="pagebookmark" id="filelist"> |
---|
231 | <li><input type="text" placeholder="{{=T('Rapid Search')}}" class="input-block-level typeahead-tw search-query"></li> |
---|
232 | {{auto_complete_list=[]}} |
---|
233 | <br /> |
---|
234 | <div> |
---|
235 | <button onclick="jQuery('#form').slideToggle()" class="btn btn-mini">{{=T('Create')}}</button> |
---|
236 | <br /> |
---|
237 | <div id="form"> |
---|
238 | <div> |
---|
239 | {{=file_create_form('%s/' % app, '')}} |
---|
240 | </div> |
---|
241 | </div> |
---|
242 | <script>jQuery('#form').slideToggle()</script> |
---|
243 | </div> |
---|
244 | <br /> |
---|
245 | <div id="files_menu"> |
---|
246 | {{=LOAD('default', 'files_menu', vars={'app':app}, ajax=True)}} |
---|
247 | </div> |
---|
248 | </ul> |
---|
249 | </div> |
---|
250 | </div> |
---|
251 | <div class="span12" id="edit_placeholder"> |
---|
252 | <ul class="nav nav-tabs " id="filesTab"> |
---|
253 | </ul> |
---|
254 | <div id="myTabContent" class="tab-content"> |
---|
255 | </div> |
---|
256 | </div> |
---|
257 | <section id="windows_divs" class="tab-content "> |
---|
258 | <div id="window_todo" class="tab-pane container-fluid"> |
---|
259 | {{=LOAD('default', 'todolist.load', vars={'app':app}, ajax=True, timeout=60000, times="infinity")}} |
---|
260 | </div> |
---|
261 | <div id="window_shortcuts" class="tab-pane container-fluid"> |
---|
262 | {{include 'default/editor_shortcuts.html'}} |
---|
263 | </div> |
---|
264 | <div id="window_dbhooks" class="tab-pane container-fluid"> |
---|
265 | <h4>Tables hooks</h4> |
---|
266 | <div>{{=LOAD(url="/%s/appadmin/hooks" % app, ajax=True, timeout=60000, times="infinity")}}</div> |
---|
267 | </div> |
---|
268 | </section> |
---|
269 | </div> |
---|
270 | {{block footer}} |
---|
271 | <div id="" class="navbar navbar-inverse navbar-fixed-bottom"> |
---|
272 | <div class="navbar-inner"> |
---|
273 | <ul id="windows_hooks" class="nav"> |
---|
274 | <li class=""><a href="#window_todo">TODO</a></li> |
---|
275 | <li class=""><a href="#window_shortcuts">Shortcuts</a></li> |
---|
276 | <li class=""><a href="#window_dbhooks">Hooks</a></li> |
---|
277 | </ul> |
---|
278 | </div> |
---|
279 | </div> |
---|
280 | |
---|
281 | <script> |
---|
282 | $(document).on('click', '#window_todo li a', function (e) { |
---|
283 | $(this).find('i').toggleClass('icon-chevron-right'); |
---|
284 | $(this).find('i').toggleClass('icon-chevron-down'); |
---|
285 | }); |
---|
286 | $('#windows_hooks li a').click(function (e) { |
---|
287 | e.preventDefault(); |
---|
288 | if ( $(this).parent('li').hasClass('active') ) { |
---|
289 | $(this).parent('li').removeClass('active'); |
---|
290 | $($(this).attr('href')).removeClass('active'); |
---|
291 | } else { |
---|
292 | $(this).tab('show'); |
---|
293 | } |
---|
294 | }); |
---|
295 | </script> |
---|
296 | {{end}} |
---|
297 | <script> |
---|
298 | $(document).ready(function() { |
---|
299 | var filesMenu = $('#files'); |
---|
300 | var ow = filesMenu.outerWidth(); |
---|
301 | filesMenu.width(ow); |
---|
302 | $('#files').css('left', '-'+ow+'px'); |
---|
303 | $.web2py.trap_form('url', 'form'); |
---|
304 | $('#form form').addClass('no_trap'); // Let to reuse the same form |
---|
305 | {{if len(request.args) > 1:}} |
---|
306 | load_file('{{=URL(f='edit', args=request.args, vars=request.get_vars)}}', {{=request.vars.lineno or 1}}); |
---|
307 | {{pass}} |
---|
308 | }); |
---|
309 | </script> |
---|
310 | <!-- Typeahead scripts here so the page load faster --> |
---|
311 | <script src="{{=URL('static', 'js/typeahead.min.js')}}"></script> |
---|
312 | <script src="{{=URL('static', 'js/hogan-2.0.0.js')}}"></script> |
---|
313 | <script> |
---|
314 | $('.typeahead-tw').typeahead({ |
---|
315 | name: 'files', |
---|
316 | local:{{from gluon.serializers import json}}{{=XML(json(auto_complete_list))}}, |
---|
317 | template: template_js, |
---|
318 | engine: Hogan, |
---|
319 | }); |
---|
320 | $(document).on("typeahead:selected", '.typeahead', function(e, datum) { |
---|
321 | load_file(datum.link); |
---|
322 | $(this).val(''); |
---|
323 | }); |
---|
324 | /* handlers to manage editor sessions |
---|
325 | */ |
---|
326 | $(document).on('click', '#save_session', function(e) { |
---|
327 | e.preventDefault(); |
---|
328 | var session_name=prompt("Session name","{{=app}}"); |
---|
329 | |
---|
330 | if (session_name!==null) { |
---|
331 | files = $("[data-path]").map(function(){return $(this).data("path");}).get(); |
---|
332 | data = JSON.stringify({ files: files, session_name:session_name}); |
---|
333 | $.ajaxSetup({contentType: "application/json"}); |
---|
334 | $.web2py.ajax_page("POST", "{{=URL('default', 'editor_sessions')}}", data, 'manage_sessions'); |
---|
335 | } |
---|
336 | }); |
---|
337 | $(document).on('click', '#saved_sessions a[data-files]', function(e) { |
---|
338 | e.preventDefault(); |
---|
339 | files = $(this).data('files'); |
---|
340 | array_files = files.split(','); |
---|
341 | $.each(array_files, function(index, value) { |
---|
342 | url = "{{=URL('default','edit')}}/" + value; |
---|
343 | load_file(url); |
---|
344 | }); |
---|
345 | }); |
---|
346 | |
---|
347 | </script> |
---|
348 | <!-- end "edit" block --> |
---|