mirror of
https://github.com/OCA/web.git
synced 2025-02-22 13:21:25 +02:00
[MIG] migration to 13.0
This commit is contained in:
committed by
bobrador
parent
e155a9692e
commit
2ff7041d04
@@ -0,0 +1,7 @@
|
||||
.o_field_text_markdown blockquote {
|
||||
background: #f9f9f9;
|
||||
border-left: 10px solid #ccc;
|
||||
margin: 1.5em 10px;
|
||||
padding: 1em 10px 0.1em 10px;
|
||||
quotes: "\201C""\201D""\2018""\2019";
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global marked */
|
||||
/* global showdown */
|
||||
/* Copyright 2014 Sudokeys <http://www.sudokeys.com>
|
||||
* Copyright 2017 Komit - <http:///komit-consulting.com>
|
||||
* Copyright 2019 Alexandre Díaz - <dev@redneboa.es>
|
||||
@@ -12,6 +12,7 @@ odoo.define("web_widget_text_markdown.FieldTextMarkDown", function(require) {
|
||||
|
||||
var _t = core._t;
|
||||
var LIBS_PATH = "/web_widget_text_markdown/static/src/lib/";
|
||||
var CUST_LIBS_PATH = "/web_widget_text_markdown/static/src/css/";
|
||||
|
||||
var FieldTextMarkDown = basic_fields.FieldText.extend({
|
||||
className: [
|
||||
@@ -19,16 +20,59 @@ odoo.define("web_widget_text_markdown.FieldTextMarkDown", function(require) {
|
||||
"o_field_text_markdown",
|
||||
].join(" "),
|
||||
jsLibs: [
|
||||
LIBS_PATH + "marked.js",
|
||||
LIBS_PATH + "dropzone.js",
|
||||
LIBS_PATH + "bootstrap-markdown.js",
|
||||
LIBS_PATH + "showdown.js",
|
||||
LIBS_PATH + "showdown-footnotes.js",
|
||||
LIBS_PATH + "showdown-table.js",
|
||||
LIBS_PATH + "showdown-toc.js",
|
||||
],
|
||||
cssLibs: [
|
||||
LIBS_PATH + "bootstrap-markdown.min.css",
|
||||
CUST_LIBS_PATH + "web_widget_text_markdown.css",
|
||||
],
|
||||
cssLibs: [LIBS_PATH + "bootstrap-markdown.min.css"],
|
||||
|
||||
_getValue: function() {
|
||||
return this.$markdown.getContent();
|
||||
},
|
||||
|
||||
start: function() {
|
||||
this._super();
|
||||
this.shw_render_html = new showdown.Converter({
|
||||
extensions: ["table", "footnotes", "toc"],
|
||||
emoji: true,
|
||||
underline: true,
|
||||
tablesHeaderId: true,
|
||||
omitExtraWLInCodeBlocks: true,
|
||||
noHeaderId: true,
|
||||
prefixHeaderId: true,
|
||||
rawPrefixHeaderId: true,
|
||||
ghCompatibleHeaderId: true,
|
||||
rawHeaderId: true,
|
||||
headerLevelStart: false,
|
||||
parseImgDimensions: true,
|
||||
simplifiedAutoLink: true,
|
||||
literalMidWordUnderscores: false,
|
||||
literalMidWordAsterisks: true,
|
||||
strikethrough: true,
|
||||
tables: true,
|
||||
ghCodeBlocks: true,
|
||||
tasklists: true,
|
||||
smoothLivePreview: true,
|
||||
smartIndentationFix: true,
|
||||
disableForced4SpacesIndentedSublists: true,
|
||||
simpleLineBreaks: true,
|
||||
requireSpaceBeforeHeadingText: true,
|
||||
ghMentions: true,
|
||||
ghMentionsLink: "https://github.com/{u}",
|
||||
encodeEmails: true,
|
||||
openLinksInNewWindow: true,
|
||||
backslashEscapesHTMLTags: true,
|
||||
completeHTMLDocument: true,
|
||||
metadata: true,
|
||||
splitAdjacentBlockquotes: true,
|
||||
});
|
||||
},
|
||||
|
||||
_prepareInput: function() {
|
||||
var $input = this._super.apply(this, arguments);
|
||||
_.defer(
|
||||
@@ -45,116 +89,52 @@ odoo.define("web_widget_text_markdown.FieldTextMarkDown", function(require) {
|
||||
);
|
||||
return $input;
|
||||
},
|
||||
_getHtmlValue: function(value) {
|
||||
return this.shw_render_html.makeHtml(this._formatValue(value));
|
||||
},
|
||||
|
||||
_renderReadonly: function() {
|
||||
this.$el.html(marked(this._formatValue(this.value)));
|
||||
this.$el.html(this._getHtmlValue(this.value));
|
||||
},
|
||||
|
||||
_getMarkdownOptions: function() {
|
||||
var self = this;
|
||||
var markdownOpts = {
|
||||
iconlibrary: "fa",
|
||||
autofocus: false,
|
||||
width: "o_field_text_markdown",
|
||||
savable: false,
|
||||
language: this.getSession().user_context.lang,
|
||||
onPreview: function(e) {
|
||||
var render_val = self._getHtmlValue(e.getContent());
|
||||
return render_val;
|
||||
},
|
||||
};
|
||||
|
||||
// Only can create attachments on non-virtual records
|
||||
if (this.res_id) {
|
||||
var self = this;
|
||||
markdownOpts.dropZoneOptions = {
|
||||
paramName: "ufile",
|
||||
url: "/web/binary/upload_attachment",
|
||||
acceptedFiles: "image/*",
|
||||
width: "o_field_text_markdown",
|
||||
params: {
|
||||
csrf_token: core.csrf_token,
|
||||
session_id: this.getSession().override_session,
|
||||
callback: "",
|
||||
model: this.model,
|
||||
id: this.res_id,
|
||||
},
|
||||
success: function() {
|
||||
self._markdownDropZoneUploadSuccess(this);
|
||||
},
|
||||
error: function() {
|
||||
self._markdownDropZoneUploadError(this);
|
||||
},
|
||||
init: function() {
|
||||
self._markdownDropZoneInit(this);
|
||||
},
|
||||
};
|
||||
|
||||
if (_t.database.multi_lang && this.field.translate) {
|
||||
markdownOpts.additionalButtons = [
|
||||
[
|
||||
{
|
||||
name: "oTranslate",
|
||||
data: [
|
||||
{
|
||||
name: "cmdTranslate",
|
||||
title: _t("Translate"),
|
||||
icon: {glyph: "glyphicon glyphicon-flag"},
|
||||
callback: this._markdownTranslate,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
if (_t.database.multi_lang && this.field.translate) {
|
||||
markdownOpts.additionalButtons = [
|
||||
[
|
||||
{
|
||||
name: "oTranslate",
|
||||
data: [
|
||||
{
|
||||
name: "cmdTranslate",
|
||||
title: _t("Translate"),
|
||||
icon: {fa: "fa fa-flag"},
|
||||
// eslint-disable-next-line max-len
|
||||
callback: this._markdownTranslate.bind(self),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return markdownOpts;
|
||||
},
|
||||
|
||||
_getAttachmentId: function(response) {
|
||||
var matchElms = response.match(/"id":\s?(\d+)/);
|
||||
if (matchElms && matchElms.length) {
|
||||
return matchElms[1];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_markdownDropZoneInit: function(markdown) {
|
||||
var self = this;
|
||||
var caretPos = 0;
|
||||
var $textarea = null;
|
||||
markdown.on("drop", function(e) {
|
||||
$textarea = $(e.target);
|
||||
caretPos = $textarea.prop("selectionStart");
|
||||
});
|
||||
markdown.on("success", function(file, response) {
|
||||
var text = $textarea.val();
|
||||
var attachment_id = self._getAttachmentId(response);
|
||||
if (attachment_id) {
|
||||
var ftext =
|
||||
text.substring(0, caretPos) +
|
||||
"\n\n" +
|
||||
text.substring(caretPos);
|
||||
$textarea.val(ftext);
|
||||
} else {
|
||||
self.do_warn(_t("Error"), _t("Can't create the attachment."));
|
||||
}
|
||||
});
|
||||
markdown.on("error", function(file, error) {
|
||||
console.warn(error);
|
||||
});
|
||||
},
|
||||
|
||||
_markdownDropZoneUploadSuccess: function() {
|
||||
this.isDirty = true;
|
||||
this._doDebouncedAction();
|
||||
this.$markdown.$editor.find(".dz-error-mark:last").css("display", "none");
|
||||
},
|
||||
|
||||
_markdownDropZoneUploadError: function() {
|
||||
this.$markdown.$editor.find(".dz-success-mark:last").css("display", "none");
|
||||
},
|
||||
|
||||
_markdownTranslate: function() {
|
||||
this._onTranslate();
|
||||
// Event is the click event from callback
|
||||
this._onTranslate(event);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/* Copyright 2019 Alexandre Díaz - <dev@redneboa.es>
|
||||
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
|
||||
.o_field_text_markdown {
|
||||
.dz-preview {
|
||||
display: inline-block;
|
||||
margin: 0.5em;
|
||||
|
||||
.dz-success-mark svg {
|
||||
background-color: green;
|
||||
border-radius: 30px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.dz-error-mark svg {
|
||||
background: red;
|
||||
border-radius: 30px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
(function () {
|
||||
var footnotes = function () {
|
||||
return [{
|
||||
type: 'lang',
|
||||
filter: function filter(text) {
|
||||
return text.replace(/^\[\^([\d\w]+)\]:\s*((\n+(\s{2,4}|\t).+)+)$/mg, function (str, name, rawContent, _, padding) {
|
||||
var content = converter.makeHtml(rawContent.replace(new RegExp('^' + padding, 'gm'), ''));
|
||||
return '<div class="footnote" id="footnote-' + name + '"><a href="#footnote-' + name + '"><sup>[' + name + ']</sup></a>:' + content + '</div>';
|
||||
});
|
||||
}},
|
||||
{
|
||||
type: 'lang',
|
||||
filter: function filter(text) {
|
||||
return text.replace(/^\[\^([\d\w]+)\]:( |\n)((.+\n)*.+)$/mg, function (str, name, _, content) {
|
||||
return '<small class="footnote" id="footnote-' + name + '"><a href="#footnote-' + name + '"><sup>[' + name + ']</sup></a>: ' + content + '</small>';
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'lang',
|
||||
filter: function filter(text) {
|
||||
return text.replace(/\[\^([\d\w]+)\]/m, function (str, name) {
|
||||
return '<a href="#footnote-' + name + '"><sup>[' + name + ']</sup></a>';
|
||||
});
|
||||
}
|
||||
}];
|
||||
};
|
||||
|
||||
// Client-side export
|
||||
if (typeof window !== 'undefined' && window.showdown && window.showdown.extensions) {
|
||||
window.showdown.extensions.footnotes = footnotes;
|
||||
}
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = footnotes;
|
||||
}
|
||||
|
||||
}());
|
||||
112
web_widget_text_markdown/static/src/lib/showdown-table.js
Normal file
112
web_widget_text_markdown/static/src/lib/showdown-table.js
Normal file
@@ -0,0 +1,112 @@
|
||||
/*! showdown-table 17-06-2015 */
|
||||
/*
|
||||
* Basic table support with re-entrant parsing, where cell content
|
||||
* can also specify markdown.
|
||||
*
|
||||
* Tables
|
||||
* ======
|
||||
*
|
||||
* | Col 1 | Col 2 |
|
||||
* |======== |====================================================|
|
||||
* |**bold** | ![Valid XHTML] (http://w3.org/Icons/valid-xhtml10) |
|
||||
* | Plain | Value |
|
||||
*
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var table = function (converter) {
|
||||
|
||||
var tables = {}, style = 'text-align:left;', filter;
|
||||
tables.th = function (header) {
|
||||
if (header.trim() === '') {
|
||||
return '';
|
||||
}
|
||||
var id = header.trim().replace(/ /g, '_').toLowerCase();
|
||||
return '<th id="' + id + '" style="' + style + '">' + header + '</th>';
|
||||
};
|
||||
tables.td = function (cell) {
|
||||
return '<td style="' + style + '">' + converter.makeHtml(cell) + '</td>';
|
||||
};
|
||||
tables.ths = function () {
|
||||
var out = '',
|
||||
i = 0,
|
||||
hs = [].slice.apply(arguments);
|
||||
for (i; i < hs.length; i += 1) {
|
||||
out += tables.th(hs[i]) + '\n';
|
||||
}
|
||||
return out;
|
||||
};
|
||||
tables.tds = function () {
|
||||
var out = '', i = 0, ds = [].slice.apply(arguments);
|
||||
for (i; i < ds.length; i += 1) {
|
||||
out += tables.td(ds[i]) + '\n';
|
||||
}
|
||||
return out;
|
||||
};
|
||||
tables.thead = function () {
|
||||
var out,
|
||||
hs = [].slice.apply(arguments);
|
||||
out = '<thead>\n';
|
||||
out += '<tr>\n';
|
||||
out += tables.ths.apply(this, hs);
|
||||
out += '</tr>\n';
|
||||
out += '</thead>\n';
|
||||
return out;
|
||||
};
|
||||
tables.tr = function () {
|
||||
var out,
|
||||
cs = [].slice.apply(arguments);
|
||||
out = '<tr>\n';
|
||||
out += tables.tds.apply(this, cs);
|
||||
out += '</tr>\n';
|
||||
return out;
|
||||
};
|
||||
filter = function (text) {
|
||||
var i = 0, lines = text.split('\n'), line, hs, out = [];
|
||||
for (i; i < lines.length; i += 1) {
|
||||
line = lines[i];
|
||||
if (line.trim().match(/^[|].*[|]$/)) {
|
||||
line = line.trim();
|
||||
var tbl = [];
|
||||
tbl.push('<table class="table table-bordered">');
|
||||
hs = line.substring(1, line.length - 1).split('|');
|
||||
tbl.push(tables.thead.apply(this, hs));
|
||||
line = lines[++i];
|
||||
if (!line.trim().match(/^[|][-=|: ]+[|]$/)) {
|
||||
line = lines[--i];
|
||||
} else {
|
||||
line = lines[++i];
|
||||
tbl.push('<tbody>');
|
||||
while (line.trim().match(/^[|].*[|]$/)) {
|
||||
line = line.trim();
|
||||
tbl.push(tables.tr.apply(this, line.substring(1, line.length - 1).split('|')));
|
||||
line = lines[++i];
|
||||
}
|
||||
tbl.push('</tbody>');
|
||||
tbl.push('</table>');
|
||||
out.push(tbl.join('\n'));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
out.push(line);
|
||||
}
|
||||
return out.join('\n');
|
||||
};
|
||||
return [
|
||||
{
|
||||
type: 'lang',
|
||||
filter: filter
|
||||
}
|
||||
];
|
||||
};
|
||||
if (typeof window !== 'undefined' && window.showdown && window.showdown.extensions) {
|
||||
window.showdown.extensions.table = table;
|
||||
}
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = table;
|
||||
}
|
||||
}());
|
||||
|
||||
//# sourceMappingURL=showdown-table.js.map
|
||||
156
web_widget_text_markdown/static/src/lib/showdown-toc.js
Normal file
156
web_widget_text_markdown/static/src/lib/showdown-toc.js
Normal file
@@ -0,0 +1,156 @@
|
||||
(function(){
|
||||
|
||||
var toc = function(converter) {
|
||||
return [
|
||||
{
|
||||
type: 'output',
|
||||
filter: function(source) {
|
||||
var elements = $(source);
|
||||
var output = [];
|
||||
var headingLevel = null;
|
||||
var tocId = null;
|
||||
for (var i=0; i<elements.length; i++) {
|
||||
var element = $(elements[i]);
|
||||
var results = null;
|
||||
|
||||
// Does the element consist only of [toc]?
|
||||
// If so, we can replace this element with out list.
|
||||
if (element.text().trim()=='[toc]') {
|
||||
element = $('<ol>',{'class':'showdown-toc'});
|
||||
headingLevel = null;
|
||||
tocId = output.length;
|
||||
}
|
||||
|
||||
// Does this item contain a [toc] with other stuff?
|
||||
// If so, we'll split the element into two
|
||||
else if (results = element.text().trim().match(/^([\s\S]*?)((?:\\)?\[toc\])([\s\S]*)$/)) {
|
||||
|
||||
// If there was a \ before the [toc] they're trying to escape it,
|
||||
// so return the [toc] string without the \ and carry on. For
|
||||
// some reason (I'm guessing a bug in showdown) you actually
|
||||
// appear to need two \ (\\) in order to get this to show up for
|
||||
// the filter. Leaving this code here anyway for now because it's
|
||||
// "the right thing to do"(tm).
|
||||
if (results[2][0]=='\\') {
|
||||
element.text(results[1]+results[2].substr(1)+results[3]);
|
||||
}
|
||||
|
||||
// Otherwise start building a new table of contents.
|
||||
else {
|
||||
var before = null;
|
||||
var after = null;
|
||||
|
||||
// Create two of the same element.
|
||||
if (element.prop('tagName')) {
|
||||
if (results[1].trim().length>0) {
|
||||
before = $('<'+element.prop('tagName')+'>').text(results[1]);
|
||||
}
|
||||
if (results[3].trim().length>0) {
|
||||
after = $('<'+element.prop('tagName')+'>').text(results[3]);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise if there's no tagName assume it's a text node
|
||||
// and create two of those.
|
||||
else {
|
||||
if (results[1].trim().length>0) {
|
||||
before = document.createTextNode(results[1]);
|
||||
}
|
||||
if (results[3].trim().length>0) {
|
||||
after = document.createTextNode(results[3]);
|
||||
}
|
||||
}
|
||||
|
||||
// Our new table of contents container.
|
||||
toc = $('<ol>',{'class':'showdown-toc'});
|
||||
|
||||
// If there was text before our [toc], add that in
|
||||
if (before) {
|
||||
output.push(before);
|
||||
}
|
||||
|
||||
// Keep track of where our current table is in the elements array.
|
||||
tocId = output.length;
|
||||
|
||||
// If there was text after, push the contents onto the array and
|
||||
// use the after part as our current element.
|
||||
if (after) {
|
||||
output.push(toc);
|
||||
element = after;
|
||||
}
|
||||
|
||||
// Otherwise use the contents as the current element.
|
||||
else {
|
||||
element = toc;
|
||||
}
|
||||
|
||||
// Reset the heading level - we're going to start looking for new
|
||||
// headings again
|
||||
headingLevel = null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// If we've started a table of contents, but have nothing in it yet,
|
||||
// look for the first header tag we encounter (after the [toc]).
|
||||
// That's going to be what we use as contents entries for this table
|
||||
// of contents.
|
||||
else if (tocId && !headingLevel && element.prop("tagName")) {
|
||||
switch (element.prop("tagName")) {
|
||||
case 'H1':
|
||||
case 'H2':
|
||||
case 'H3':
|
||||
case 'H4':
|
||||
case 'H5':
|
||||
case 'H6':
|
||||
headingLevel = parseInt(element.prop('tagName').substr(1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we know what header level we're looking for (either we just
|
||||
// found it above, or we're continuing to look for more) then check to
|
||||
// see if this heading should be added to the contents.
|
||||
if (tocId && headingLevel) {
|
||||
switch (element.prop('tagName')) {
|
||||
case 'H1':
|
||||
case 'H2':
|
||||
case 'H3':
|
||||
case 'H4':
|
||||
case 'H5':
|
||||
case 'H6':
|
||||
var thisLevel = parseInt(element.prop('tagName').substr(1));
|
||||
if (thisLevel==headingLevel) {
|
||||
output[tocId] = $(output[tocId]).append($('<li>').append($('<a>',{href:'#'+element.attr('id'),text:element.text()})));
|
||||
}
|
||||
// If we move up in what would be the document tree
|
||||
// (eg: if we're looking for H2 and we suddenly find an
|
||||
// H1) then we can probably safely assume that we want
|
||||
// the table of contents to end for this section.
|
||||
else if (thisLevel<headingLevel) {
|
||||
toc = null
|
||||
tocId = null;
|
||||
headingLevel = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Push whatever element we've been looking at onto the output array.
|
||||
output.push(element);
|
||||
}
|
||||
// Build some HTML to return
|
||||
// Return it.
|
||||
return $('<div>').append(output).html();
|
||||
}
|
||||
}
|
||||
|
||||
];
|
||||
};
|
||||
|
||||
// Client-side export
|
||||
if (typeof window !== 'undefined' && window.showdown && window.showdown.extensions) { window.showdown.extensions.toc = toc; }
|
||||
// Server-side export
|
||||
if (typeof module !== 'undefined') module.exports = toc;
|
||||
|
||||
}());
|
||||
|
||||
5460
web_widget_text_markdown/static/src/lib/showdown.js
Normal file
5460
web_widget_text_markdown/static/src/lib/showdown.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user