[IMP] web: add ace editor for xml and python edition
* display all lines and don't display the scroll bar into ace editor * lazy read ace lib and add missing lib file * use not minified version of ace lib * Move ace lib from website to web * Added ace widget for webclient * XML mode for view text box * Python mode for server action text box * Removed web_ace_editor.xml, instead added template in base.xml itself * Added widget=ace_editor in compute field of ir.model.fields
This commit is contained in:
@@ -0,0 +1 @@
|
||||
define("ace/mode/python_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="and|as|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while|with|yield",t="True|False|None|NotImplemented|Ellipsis|__debug__",n="abs|divmod|input|open|staticmethod|all|enumerate|int|ord|str|any|eval|isinstance|pow|sum|basestring|execfile|issubclass|print|super|binfile|iter|property|tuple|bool|filter|len|range|type|bytearray|float|list|raw_input|unichr|callable|format|locals|reduce|unicode|chr|frozenset|long|reload|vars|classmethod|getattr|map|repr|xrange|cmp|globals|max|reversed|zip|compile|hasattr|memoryview|round|__import__|complex|hash|min|set|apply|delattr|help|next|setattr|buffer|dict|hex|object|slice|coerce|dir|id|oct|sorted|intern",r=this.createKeywordMapper({"invalid.deprecated":"debugger","support.function":n,"constant.language":t,keyword:e},"identifier"),i="(?:r|u|ur|R|U|UR|Ur|uR)?",s="(?:(?:[1-9]\\d*)|(?:0))",o="(?:0[oO]?[0-7]+)",u="(?:0[xX][\\dA-Fa-f]+)",a="(?:0[bB][01]+)",f="(?:"+s+"|"+o+"|"+u+"|"+a+")",l="(?:[eE][+-]?\\d+)",c="(?:\\.\\d+)",h="(?:\\d+)",p="(?:(?:"+h+"?"+c+")|(?:"+h+"\\.))",d="(?:(?:"+p+"|"+h+")"+l+")",v="(?:"+d+"|"+p+")",m="\\\\(x[0-9A-Fa-f]{2}|[0-7]{3}|[\\\\abfnrtv'\"]|U[0-9A-Fa-f]{8}|u[0-9A-Fa-f]{4})";this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"string",regex:i+'"{3}',next:"qqstring3"},{token:"string",regex:i+'"(?=.)',next:"qqstring"},{token:"string",regex:i+"'{3}",next:"qstring3"},{token:"string",regex:i+"'(?=.)",next:"qstring"},{token:"constant.numeric",regex:"(?:"+v+"|\\d+)[jJ]\\b"},{token:"constant.numeric",regex:v},{token:"constant.numeric",regex:f+"[lL]\\b"},{token:"constant.numeric",regex:f+"\\b"},{token:r,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|%|<<|>>|&|\\||\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\[\\(\\{]"},{token:"paren.rparen",regex:"[\\]\\)\\}]"},{token:"text",regex:"\\s+"}],qqstring3:[{token:"constant.language.escape",regex:m},{token:"string",regex:'"{3}',next:"start"},{defaultToken:"string"}],qstring3:[{token:"constant.language.escape",regex:m},{token:"string",regex:"'{3}",next:"start"},{defaultToken:"string"}],qqstring:[{token:"constant.language.escape",regex:m},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"start"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:m},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"start"},{defaultToken:"string"}]}};r.inherits(s,i),t.PythonHighlightRules=s}),define("ace/mode/folding/pythonic",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=t.FoldMode=function(e){this.foldingStartMarker=new RegExp("([\\[{])(?:\\s*)$|("+e+")(?:\\s*)(?:#.*)?$")};r.inherits(s,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=e.getLine(n),i=r.match(this.foldingStartMarker);if(i)return i[1]?this.openingBracketBlock(e,i[1],n,i.index):i[2]?this.indentationBlock(e,n,i.index+i[2].length):this.indentationBlock(e,n)}}.call(s.prototype)}),define("ace/mode/python",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/python_highlight_rules","ace/mode/folding/pythonic","ace/range"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./python_highlight_rules").PythonHighlightRules,o=e("./folding/pythonic").FoldMode,u=e("../range").Range,a=function(){this.HighlightRules=s,this.foldingRules=new o("\\:")};r.inherits(a,i),function(){this.lineCommentStart="#",this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"){var o=t.match(/^.*[\{\(\[\:]\s*$/);o&&(r+=n)}return r};var e={pass:1,"return":1,raise:1,"break":1,"continue":1};this.checkOutdent=function(t,n,r){if(r!=="\r\n"&&r!=="\r"&&r!=="\n")return!1;var i=this.getTokenizer().getLineTokens(n.trim(),t).tokens;if(!i)return!1;do var s=i.pop();while(s&&(s.type=="comment"||s.type=="text"&&s.value.match(/^\s+$/)));return s?s.type=="keyword"&&e[s.value]:!1},this.autoOutdent=function(e,t,n){n+=1;var r=this.$getIndent(t.getLine(n)),i=t.getTabString();r.slice(-i.length)==i&&t.remove(new u(n,r.length-i.length,n,r.length))},this.$id="ace/mode/python"}.call(a.prototype),t.Mode=a})
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
odoo.define('web.form_widgets', function (require) {
|
||||
"use strict";
|
||||
|
||||
var ajax = require('web.ajax');
|
||||
var core = require('web.core');
|
||||
var crash_manager = require('web.crash_manager');
|
||||
var data = require('web.data');
|
||||
@@ -1579,6 +1580,102 @@ var FieldToggleBoolean = common.AbstractField.extend({
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
This widget is intended to be used on Text fields. It will provide Ace Editor for editing XML and Python.
|
||||
*/
|
||||
|
||||
var AceEditor = common.AbstractField.extend(common.ReinitializeFieldMixin, {
|
||||
template: "AceEditor",
|
||||
willStart: function() {
|
||||
if (!window.ace && !this.loadJS_def) {
|
||||
this.loadJS_def = ajax.loadJS('/web/static/lib/ace/ace.js').then(function () {
|
||||
return $.when(ajax.loadJS('/web/static/lib/ace/mode-python.js'),
|
||||
ajax.loadJS('/web/static/lib/ace/mode-xml.js'),
|
||||
ajax.loadJS('/web/static/lib/ace/theme-monokai.js'));
|
||||
});
|
||||
}
|
||||
return $.when(this._super(), this.loadJS_def);
|
||||
},
|
||||
initialize_content: function () {
|
||||
if (! this.get("effective_readonly")) {
|
||||
var self = this;
|
||||
|
||||
this.aceEditor = ace.edit(this.$('.ace-view-editor')[0]);
|
||||
this.aceEditor.setOptions({"maxLines": Infinity});
|
||||
this.aceEditor.setTheme("ace/theme/monokai");
|
||||
this.aceEditor.$blockScrolling = true;
|
||||
|
||||
var scrollIntoViewIfNeeded = _.throttle(function () {
|
||||
var node = self.aceEditor.renderer.textarea;
|
||||
if (node.scrollIntoViewIfNeeded) {
|
||||
node.scrollIntoViewIfNeeded(false);
|
||||
} else {
|
||||
var offsetParent = node.offsetParent;
|
||||
while (offsetParent) {
|
||||
var elY = 0;
|
||||
var elH = node.offsetHeight+20;
|
||||
var parent = node;
|
||||
while (offsetParent && parent) {
|
||||
elY += node.offsetTop;
|
||||
// get if a parent have a scrollbar
|
||||
parent = node.parentNode;
|
||||
while (parent != offsetParent &&
|
||||
(parent.tagName === "BODY" || ["auto", "scroll"].indexOf(window.getComputedStyle(parent).overflowY) === -1)) {
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
node = parent;
|
||||
if (parent !== offsetParent) {
|
||||
elY -= parent.offsetTop;
|
||||
parent = null;
|
||||
}
|
||||
offsetParent = node.offsetParent;
|
||||
}
|
||||
|
||||
if ((node.tagName === "BODY" || ["auto", "scroll"].indexOf(window.getComputedStyle(node).overflowY) !== -1) &&
|
||||
(node.scrollTop + node.clientHeight) < (elY + elH)) {
|
||||
node.scrollTop = (elY + elH) - node.clientHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
var $moveTextAreaToCursor = this.aceEditor.renderer.$moveTextAreaToCursor;
|
||||
self.aceEditor.renderer.$moveTextAreaToCursor = function() {
|
||||
$moveTextAreaToCursor.call(this);
|
||||
if (parseInt($(self.aceEditor.renderer.textarea).css('top'), 10) >= 0) {
|
||||
scrollIntoViewIfNeeded();
|
||||
}
|
||||
};
|
||||
|
||||
this.aceSession = this.aceEditor.getSession();
|
||||
this.aceSession.setUseWorker(false);
|
||||
this.aceSession.setMode("ace/mode/"+(this.options.mode || 'xml'));
|
||||
|
||||
this.aceEditor.on("blur", function() {
|
||||
if (self.aceSession.getUndoManager().hasUndo()) {
|
||||
self.set_value(self.aceSession.getValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
destroy_content: function() {
|
||||
if (this.aceEditor) {
|
||||
this.aceEditor.destroy();
|
||||
}
|
||||
},
|
||||
render_value: function() {
|
||||
if (! this.get("effective_readonly")) {
|
||||
var value = formats.format_value(this.get('value'), this);
|
||||
this.aceSession.setValue(value);
|
||||
|
||||
} else {
|
||||
var txt = this.get("value") || '';
|
||||
this.$(".oe_form_text_content").text(txt);
|
||||
}
|
||||
},
|
||||
focus: function() {
|
||||
return this.aceEditor.focus();
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Registry of form fields, called by :js:`instance.web.FormView`.
|
||||
@@ -1614,8 +1711,8 @@ core.form_widget_registry
|
||||
.add('kanban_state_selection', KanbanSelection)
|
||||
.add('statinfo', StatInfo)
|
||||
.add('timezone_mismatch', TimezoneMismatch)
|
||||
.add('label_selection', LabelSelection);
|
||||
|
||||
.add('label_selection', LabelSelection)
|
||||
.add('ace', AceEditor);
|
||||
|
||||
/**
|
||||
* Registry of widgets usable in the form view that can substitute to any possible
|
||||
|
||||
@@ -267,6 +267,15 @@
|
||||
.o_group.o_inner_group.oe_subtotal_footer {
|
||||
.oe_right;
|
||||
}
|
||||
|
||||
//ace
|
||||
.o_ace_view_editor {
|
||||
width: 100%;
|
||||
.ace-view-editor {
|
||||
height: 300px;
|
||||
padding: 0 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Overriden style when form view in modal
|
||||
|
||||
@@ -1250,6 +1250,19 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="AceEditor">
|
||||
<t t-if="!widget.get('effective_readonly')">
|
||||
<div class="oe_form_field o_ace_view_editor oe_ace_open">
|
||||
<div class="ace-view-editor"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="widget.get('effective_readonly')">
|
||||
<div class="oe_form_field oe_form_field_text" t-att-style="widget.node.attrs.style">
|
||||
<span class="oe_form_text_content"></span>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="notification-box">
|
||||
<div t-att-class="'alert alert-dismissible alert-' + type" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
|
||||
@@ -6,7 +6,6 @@ var Class = require('web.Class');
|
||||
var core = require('web.core');
|
||||
var Widget = require('web.Widget');
|
||||
var base = require('web_editor.base');
|
||||
var ace_call = require('website.ace_call');
|
||||
var website = require('website.website');
|
||||
var local_storage = require('web.local_storage');
|
||||
|
||||
@@ -22,17 +21,26 @@ var Ace = Widget.extend({
|
||||
'click a[data-action=ace]': 'launchAce',
|
||||
},
|
||||
launchAce: function (e) {
|
||||
ace_call.load();
|
||||
var self = this;
|
||||
if (!window.ace && !this.loadJS_def) {
|
||||
this.loadJS_def = ajax.loadJS('/web/static/lib/ace/ace.js').then(function () {
|
||||
return $.when(ajax.loadJS('/web/static/lib/ace/mode-xml.js'),
|
||||
ajax.loadJS('/web/static/lib/ace/theme-monokai.js'));
|
||||
});
|
||||
}
|
||||
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (this.globalEditor) {
|
||||
this.globalEditor.open();
|
||||
} else {
|
||||
this.globalEditor = new ViewEditor(this);
|
||||
this.globalEditor.appendTo($(document.body));
|
||||
}
|
||||
|
||||
return $.when(this.loadJS_def).then(function () {
|
||||
if (self.globalEditor) {
|
||||
self.globalEditor.open();
|
||||
} else {
|
||||
self.globalEditor = new ViewEditor(self);
|
||||
self.globalEditor.appendTo($(document.body));
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -217,6 +225,7 @@ var ViewEditor = Widget.extend({
|
||||
var editingSession = self.buffers[viewId] = new ace.EditSession(result[0].arch);
|
||||
editingSession.setMode("ace/mode/xml");
|
||||
editingSession.setUndoManager(new ace.UndoManager());
|
||||
editingSession.setUseWorker(false);
|
||||
editingSession.on("change", function () {
|
||||
setTimeout(function () {
|
||||
var $option = self.$('#ace-view-list').find('[value='+viewId+']');
|
||||
|
||||
@@ -306,13 +306,13 @@ body .modal {
|
||||
}
|
||||
|
||||
// ACE EDITOR
|
||||
.oe_ace_view_editor {
|
||||
.o_ace_view_editor {
|
||||
.o-position-absolute(@odoo-navbar-height, 0, 0);
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
background: #2F3129;
|
||||
color: white;
|
||||
.oe_ace_view_editor_title {
|
||||
.o_ace_view_editor_title {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
padding-top: 0;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="website.ace_view_editor">
|
||||
<div class="oe_ace_view_editor oe_ace_closed">
|
||||
<div class="form-inline oe_ace_view_editor_title">
|
||||
<div class="o_ace_view_editor oe_ace_closed">
|
||||
<div class="form-inline o_ace_view_editor_title">
|
||||
<select id="ace-view-list" class="form-control oe_view_list">
|
||||
<!-- filled in JS -->
|
||||
</select>
|
||||
|
||||
@@ -33,9 +33,6 @@
|
||||
|
||||
<script type="text/javascript" src="/website/static/src/js/website.editor.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/jQuery.transfo.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/ace/ace.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/ace/theme-monokai.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/ace/mode-xml.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/vkbeautify/vkbeautify.0.99.00.beta.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/jquery.mjs.nestedSortable/jquery.mjs.nestedSortable.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.mobile.js"></script>
|
||||
|
||||
@@ -292,7 +292,7 @@
|
||||
<notebook colspan="4">
|
||||
<page string="Python Code" name='code' autofocus="autofocus"
|
||||
attrs="{'invisible': [('state', '!=', 'code')]}">
|
||||
<field name="code" placeholder="Enter Python code here. Help about Python expression is available in the help tab of this document."/>
|
||||
<field name="code" widget="ace" options="{'mode': 'python'}" placeholder="Enter Python code here. Help about Python expression is available in the help tab of this document."/>
|
||||
</page>
|
||||
|
||||
<page string="Worflow Signal" autofocus="autofocus"
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
attrs="{'readonly': [('state','=','base')]}"/>
|
||||
<field name="related"/>
|
||||
<field name="depends"/>
|
||||
<field name="compute"/>
|
||||
<field name="compute" widget="ace" options="{'mode': 'python'}"/>
|
||||
</group>
|
||||
<h3>How to define a computed field</h3>
|
||||
<p>Computed fields are defined with the fields
|
||||
@@ -276,7 +276,7 @@
|
||||
attrs="{'readonly': [('state','=','base')]}"/>
|
||||
<field name="related"/>
|
||||
<field name="depends" attrs="{'required': [('compute', '!=', False)] }"/>
|
||||
<field name="compute"/>
|
||||
<field name="compute" widget="ace" options="{'mode': 'python'}"/>
|
||||
</group>
|
||||
<h3>How to define a computed field</h3>
|
||||
<p>Computed fields are defined with the fields
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<page string="Architecture">
|
||||
<button type="object" name="open_translations"
|
||||
string="Edit Translations" class="oe_link oe_right"/>
|
||||
<field name="arch_base"/>
|
||||
<field name="arch_base" widget="ace" options="{'mode': 'xml'}"/>
|
||||
</page>
|
||||
<page string="Groups">
|
||||
<field name="groups_id"/>
|
||||
|
||||
Reference in New Issue
Block a user