瀏覽代碼

Summernote editor (#2665)

* [summernote] working commit

* fix set_input method, esc key fix

* styling fixes

* removed old texteditor
version-14
Faris Ansari 8 年之前
committed by Rushabh Mehta
父節點
當前提交
d0bf9ae3d3
共有 9 個檔案被更改,包括 7219 行新增681 行删除
  1. +2
    -0
      frappe/public/build.json
  2. +20
    -0
      frappe/public/css/desk.css
  3. +90
    -109
      frappe/public/js/frappe/form/control.js
  4. +0
    -84
      frappe/public/js/frappe/ui/editor.html
  5. +0
    -471
      frappe/public/js/frappe/ui/editor.js
  6. +35
    -17
      frappe/public/js/frappe/ui/keyboard.js
  7. +1
    -0
      frappe/public/js/lib/summernote/summernote.css
  8. +7046
    -0
      frappe/public/js/lib/summernote/summernote.js
  9. +25
    -0
      frappe/public/less/desk.less

+ 2
- 0
frappe/public/build.json 查看文件

@@ -41,6 +41,7 @@
"css/desk.min.css": [
"public/js/lib/datepicker/datepicker.min.css",
"public/js/lib/awesomplete/awesomplete.css",
"public/js/lib/summernote/summernote.css",
"public/css/bootstrap.css",
"public/css/font-awesome.css",
"public/css/octicons/octicons.css",
@@ -66,6 +67,7 @@
"public/js/lib/awesomplete/awesomplete.min.js",
"public/js/lib/Sortable.min.js",
"public/js/lib/taggle/taggle.min.js",
"public/js/lib/summernote/summernote.js",
"public/js/lib/notify.js",
"public/js/lib/bootstrap.min.js",
"public/js/lib/moment/moment-with-locales.min.js",


+ 20
- 0
frappe/public/css/desk.css 查看文件

@@ -642,3 +642,23 @@ fieldset[disabled] .form-control {
.search-result {
margin-bottom: 24px;
}
.note-editor {
margin-top: 5px;
}
.note-editor.note-frame {
border-color: #d1d8dd;
}
.note-editor .btn {
outline: none !important;
}
.note-editor .dropdown-style > li > a > * {
margin: 0;
}
.note-editor .fa.fa-check {
color: #36414C !important;
}
.note-editor .dropdown-menu {
z-index: 100;
max-height: 300px;
overflow: scroll;
}

+ 90
- 109
frappe/public/js/frappe/form/control.js 查看文件

@@ -1555,130 +1555,111 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
});

frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({
editor_name: "bsEditor",
horizontal: false,
make_input: function() {
//$(this.input_area).css({"min-height":"360px"});
this.has_input = true;
this.make_rich_text_editor();
this.make_markdown_editor();
this.make_switcher();
this.make_editor();
this.hide_elements_on_mobile();
},
make_rich_text_editor: function() {
make_editor: function() {
var me = this;
this.editor_wrapper = $("<div>").appendTo(this.input_area);
var onchange = function(value) {
me.md_editor.val(value);
me.parse_validate_and_set_in_model(value);
}
this.editor = new (frappe.provide(this.editor_name))({
parent: this.editor_wrapper,
change: onchange,
field: this
});
this.editor.editor.on("blur", function() {
onchange(me.editor.clean_html());
});
this.editor.editor.keypress("ctrl+s meta+s", function() {
me.frm.save_or_update();
});
},
make_markdown_editor: function() {
var me = this;
this.md_editor_wrapper = $("<div class='hide'>")
.appendTo(this.input_area);
this.md_editor = $("<textarea class='form-control markdown-text-editor'>")
.appendTo(this.md_editor_wrapper)
.allowTabs()
.on("change", function() {
var value = $(this).val();
me.editor.set_input(value);
me.parse_validate_and_set_in_model(value);
this.editor = $("<div>").appendTo(this.input_area);
this.editor.summernote({
minHeight: 400,
toolbar: [
['magic', ['style']],
['style', ['bold', 'italic', 'underline', 'clear']],
['fontsize', ['fontsize']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['height', ['height']],
['misc', ['fullscreen', 'codeview']]
],
callbacks: {
onChange: function(value) {
me.parse_validate_and_set_in_model(value);
},
onKeydown: function(e) {
var key = frappe.ui.keys.get_key(e);
// prevent 'New DocType (Ctrl + B)' shortcut in editor
if(['ctrl+b', 'meta+b'].indexOf(key) !== -1) {
e.stopPropagation();
}
if(key.indexOf('escape') !== -1) {
if(me.note_editor.hasClass('fullscreen')) {
// exit fullscreen on escape key
me.note_editor
.find('.note-btn.btn-fullscreen')
.trigger('click');
}
}
}
},
icons: {
'align': 'fa fa-align',
'alignCenter': 'fa fa-align-center',
'alignJustify': 'fa fa-align-justify',
'alignLeft': 'fa fa-align-left',
'alignRight': 'fa fa-align-right',
'indent': 'fa fa-indent',
'outdent': 'fa fa-outdent',
'arrowsAlt': 'fa fa-arrows-alt',
'bold': 'fa fa-bold',
'caret': 'caret',
'circle': 'fa fa-circle',
'close': 'fa fa-close',
'code': 'fa fa-code',
'eraser': 'fa fa-eraser',
'font': 'fa fa-font',
'frame': 'fa fa-frame',
'italic': 'fa fa-italic',
'link': 'fa fa-link',
'unlink': 'fa fa-chain-broken',
'magic': 'fa fa-magic',
'menuCheck': 'fa fa-check',
'minus': 'fa fa-minus',
'orderedlist': 'fa fa-list-ol',
'pencil': 'fa fa-pencil',
'picture': 'fa fa-picture',
'question': 'fa fa-question',
'redo': 'fa fa-redo',
'square': 'fa fa-square',
'strikethrough': 'fa fa-strikethrough',
'subscript': 'fa fa-subscript',
'superscript': 'fa fa-superscript',
'table': 'fa fa-table',
'textHeight': 'fa fa-text-height',
'trash': 'fa fa-trash',
'underline': 'fa fa-underline',
'undo': 'fa fa-undo',
'unorderedlist': 'fa fa-list-ul',
'video': 'fa fa-video'
}
});

$('<div class="text-muted small">Add &lt;!-- markdown --&gt; \
to always interpret as markdown</div>')
.appendTo(this.md_editor_wrapper);
this.note_editor = $(this.input_area).find('.note-editor');
},
make_switcher: function() {
var me = this;
this.current_editor = this.editor;
this.switcher = $('<p class="text-right small">\
<a href="#" class="switcher"></a></p>')
.appendTo(this.input_area)
.find("a")
.click(function() {
me.switch();
return false;
});
this.render_switcher();
},
switch: function() {
if(this.current_editor===this.editor) {
// switch to md
var value = this.editor.get_value();
this.editor_wrapper.addClass("hide");
this.md_editor_wrapper.removeClass("hide");
this.current_editor = this.md_editor;
this.add_type_marker("markdown");
} else {
// switch to html
var value = this.md_editor.val();
this.md_editor_wrapper.addClass("hide");
this.editor_wrapper.removeClass("hide");
this.current_editor = this.editor;
this.add_type_marker("html");
}
this.render_switcher();
},
add_type_marker: function(marker) {
var opp_marker = marker==="html" ? "markdown" : "html";
if(!this.value) this.value = "";
if(this.value.indexOf("<!-- " + opp_marker + " -->")!==-1) {
// replace opposite marker
this.set_value(this.value.split("<!-- " + opp_marker + " -->").join("<!-- " + marker + " -->"));
} else if(this.value.indexOf("<!-- " + marker + " -->")===-1) {
// add marker (marker missing)
this.set_value(this.value + "\n\n\n<!-- " + marker + " -->");
}
},
render_switcher: function() {
this.switcher.html(__("Edit as {0}", [this.current_editor == this.editor ?
__("Markdown") : __("Rich Text")]));
hide_elements_on_mobile: function() {
this.note_editor.find('.note-btn-underline,\
.note-btn-italic, .note-fontsize,\
.note-color, .note-height, .btn-codeview')
.addClass('hidden-xs');
if($('.toggle-sidebar').is(':visible')) {
// disable tooltips on mobile
this.note_editor.find('.note-btn')
.attr('data-original-title', '');
}
},
get_value: function() {
return this.current_editor === this.editor
? this.editor.get_value()
: this.md_editor.val();
return this.editor.summernote('code');
},
set_input: function(value) {
this._set_input(value);

// guess editor type
var is_markdown = false;
if(value) {
if(value.indexOf("<!-- markdown -->") !== -1) {
var is_markdown = true;
}
if((is_markdown && this.current_editor===this.editor)
|| (!is_markdown && this.current_editor===this.md_editor)) {
this.switch();
}
}
},
_set_input: function(value) {
if(value == null) value = "";
value = frappe.dom.remove_script_and_style(value);
this.editor.set_input(value);
this.md_editor.val(value);
if(value !== this.get_value())
this.editor.summernote('code', value);
this.last_value = value;
},
set_focus: function() {
var editor = this.$wrapper.find('.text-editor');
if(editor) {
editor.focus();
return true;
}
return this.editor.summernote('focus');
}
});



+ 0
- 84
frappe/public/js/frappe/ui/editor.html 查看文件

@@ -1,84 +0,0 @@
<div class="frappe-list-toolbar frappe-ignore-click">
<div class="btn-toolbar" data-role="editor-toolbar"
style="margin-bottom: 7px;">
<div class="btn-group form-group editor-toolbar-list-group">
<a class="btn btn-default btn-small" data-edit="bold"
title="{{ __("Bold (Ctrl/Cmd+B)") }}"><b>B</b></a>
<a class="btn btn-default btn-small"
data-edit="insertunorderedlist" title="{{ __("Bullet list") }}">
<i class="octicon octicon-list-unordered"></i></a>
<a class="btn btn-default btn-small"
data-edit="insertorderedlist"
title="{{ __("Number list") }}">
<i class="octicon octicon-list-ordered"></i></a>
</div>
<div class="btn-group form-group editor-toolbar-text-group">
<a class="btn btn-default btn-small dropdown-toggle"
data-toggle="dropdown" title="{{ __("Font Size") }}">
<i class="fa fa-text-height"></i>
<small style="margin-left: 5px;" class="hidden-xs"></small>
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="#" data-edit="formatBlock &lt;p&gt;">
<p>{{ __("Paragraph") }}</p></a>
</li>
<li><a href="#" data-edit="formatBlock &lt;h1&gt;">
<h1>{{ __("Heading") }} 1</h1></a>
</li>
<li><a href="#" data-edit="formatBlock &lt;h2&gt;">
<h2>{{ __("Heading") }} 2</h2></a>
</li>
<li><a href="#" data-edit="formatBlock &lt;h3&gt;">
<h3>{{ __("Heading") }} 3</h3></a>
</li>
<li><a href="#" data-edit="formatBlock &lt;h4&gt;">
<h4>{{ __("Heading") }} 4</h4></a>
</li>
<li><a href="#" data-edit="formatBlock &lt;h5&gt;">
<h5>{{ __("Heading") }} 5</h5></a>
</li>
</ul>
</div>
<div class="btn-group form-group editor-toolbar-link-group">
<a class="btn btn-default btn-small btn-insert-img"
title="{{ __("Insert picture (or just drag & drop)") }}">
<i class="octicon octicon-file-media"></i></a>
<a class="btn btn-default btn-small btn-add-link"
title="{{ __("Insert Link") }}"><i class="fa fa-link"></i></a>
<a class="btn btn-default btn-small"
title="{{ __("Remove Link") }}" data-edit="unlink">
<i class="fa fa-unlink"></i></a>
</div>
<div class="btn-group hidden-xs form-group editor-toolbar-align-group">
<a class="btn btn-default btn-small" data-edit="justifyleft"
title="{{ __("Align Left (Ctrl/Cmd+L)") }}">
<i class="fa fa-align-left"></i></a>
<a class="btn btn-default btn-small" data-edit="justifycenter"
title="{{ __("Center (Ctrl/Cmd+E)") }}">
<i class="fa fa-align-center"></i></a>
<a class="btn btn-default btn-small" data-edit="justifyright"
title="{{ __("Align Right (Ctrl/Cmd+R)") }}">
<i class="fa fa-align-right"></i></a>
<a class="btn btn-default btn-small" data-edit="outdent"
title="{{ __("Reduce indent (Shift+Tab)") }}">
<i class="fa fa-indent"></i></a>
<a class="btn btn-default btn-small" data-edit="indent"
title="{{ __("Indent (Tab)") }}">
<i class="fa fa-outdent"></i></a>
<a class="btn btn-default btn-small"
data-edit="insertHorizontalRule"
title="{{ __("Horizontal Line Break") }}">
<i class="octicon octicon-horizontal-rule"></i></a>
</div>
<div class="btn-group form-group editor-toolbar-code-group">
<a class="btn btn-default btn-small btn-html hidden-xs"
title="{{ __("HTML") }}">
<i class="octicon octicon-code"></i></a>
<a class="btn btn-default btn-small btn-success" data-action="Save"
title="{{ __("Save") }}">
<i class="octicon octicon-check"></i></a>
</div>
<input type="file" data-edit="insertImage" />
</div>
</div>

+ 0
- 471
frappe/public/js/frappe/ui/editor.js 查看文件

@@ -1,471 +0,0 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

/* Inspired from: http://github.com/mindmup/bootstrap-wysiwyg */

// todo
// make it inline friendly

bsEditor = Class.extend({
init: function(options) {
this.options = $.extend({}, this.default_options, options || {});
this.edit_mode = true;
if(this.options.editor) {
this.setup_editor(this.options.editor);
this.setup_fixed_toolbar();
} else if(this.options.parent) {
this.wrapper = $("<div></div>").appendTo(this.options.parent);
this.setup_editor($("<div class='frappe-list'></div>").appendTo(this.wrapper));
this.setup_inline_toolbar();
this.editor.addClass("text-editor");
this.set_editing();
}
},
setup_editor: function(editor) {
var me = this;
this.editor = $(editor);
this.editor.on("click", function() {
if(me.edit_mode && !me.editing) {
me.set_editing();
}
}).on("mouseup keyup mouseout", function() {
var html = me.clean_html();
if(me.editing) {
me.toolbar.save_selection();
me.toolbar.update();
if(html != me.last_html) {
me.options.change && me.options.change(html);
me.last_html = html;
}
}
}).data("object", this);

this.bind_hotkeys();
this.init_file_drops();
},

set_editing: function() {
this.editor.attr('contenteditable', true);
this.toolbar.show();
if(this.options.editor)
this.toolbar.editor = this.editor.focus();
this.editing = true;
},

setup_fixed_toolbar: function() {
if(!window.bs_editor_toolbar) {
window.bs_editor_toolbar = new bsEditorToolbar(this.options)
}
this.toolbar = window.bs_editor_toolbar;
},
setup_inline_toolbar: function() {
this.toolbar = new bsEditorToolbar(this.options, this.wrapper, this.editor);
},
onhide: function() {
this.editing = false;
this.options.onsave && this.options.onsave(this);
this.options.change && this.options.change(this.get_value());
},
toggle_edit_mode: function(bool) {
// switch to enter editing mode
this.edit_mode = bool;
if(this.edit_mode) {
this.editor.trigger("click");
}
},
default_options: {
hotKeys: {
'ctrl+b meta+b': 'bold',
'ctrl+i meta+i': 'italic',
'ctrl+u meta+u': 'underline',
'ctrl+z meta+z': 'undo',
'ctrl+y meta+y meta+shift+z': 'redo',
'ctrl+l meta+l': 'justifyleft',
'ctrl+e meta+e': 'justifycenter',
'ctrl+j meta+j': 'justifyfull',
'shift+tab': 'outdent',
'tab': 'indent'
},
toolbar_selector: '[data-role=editor-toolbar]',
command_role: 'edit',
selection_marker: 'edit-focus-marker',
selection_color: 'darkgrey',
remove_typography: false,
max_file_size: 5,
},

bind_hotkeys: function () {
var me = this;
$.each(this.options.hotKeys, function (hotkey, command) {
me.editor.keydown(hotkey, function (e) {
if (me.editor.attr('contenteditable') && me.editor.is(':visible')) {
e.preventDefault();
e.stopPropagation();
me.toolbar.execCommand(command);
return false;
}
}).keyup(hotkey, function (e) {
if (me.editor.attr('contenteditable') && me.editor.is(':visible')) {
e.preventDefault();
e.stopPropagation();
return false;
}
});
});
},

clean_html: function() {

var html = this.editor.html() || "";

if(!$.trim(this.editor.text()) && !(this.editor.find("img"))) html = "";

// remove custom typography (use CSS!)
if(this.options.remove_typography) {
var tmp = $("<div></div>").html(html);
// remove style attributes
tmp.find("*")
.removeAttr("style")
.removeAttr("font");
html = tmp.html();
}

return html;
},

init_file_drops: function () {
var me = this;
this.editor.on('dragenter dragover', false)
.on('drop', function (e) {
var dataTransfer = e.originalEvent.dataTransfer;
e.stopPropagation();
e.preventDefault();
if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
me.insert_files(dataTransfer.files);
}
});
},

insert_files: function (files) {
var me = this;
this.editor.focus();
$.each(files, function (i, file) {
if (/^image\//.test(file.type)) {
me.get_image(file, function(image_url) {
me.toolbar.execCommand('insertImage', image_url);
})
}
});
},

get_image: function (fileobj, callback) {
var freader = new FileReader(),
me = this;

freader.onload = function() {
var dataurl = freader.result;
// add filename to dataurl
var parts = dataurl.split(",");
parts[0] += ";filename=" + fileobj.name;
dataurl = parts[0] + ',' + parts[1];
if(me.options.max_file_size) {
if(dataurl.length > (me.options.max_file_size * 1024 * 1024 * 1.4)) {
bs_get_modal("Upload Error", "Max file size (" + me.options.max_file_size + "M) exceeded.").modal("show");
throw "file size exceeded";
}
}
callback(dataurl);
}
freader.readAsDataURL(fileobj);
},

get_value: function() {
return this.clean_html()
},

set_input: function(value) {
if(this.options.field && this.options.field.inside_change_event)
return;
if(value==null) value = "";
this.last_html = value;
this.editor.html(value);
}

})

bsEditorToolbar = Class.extend({
init: function(options, parent, editor) {
this.options = options;
this.editor = editor;
this.inline = !!parent;
this.options.toolbar_style = $.extend((this.inline ? this.inline_style : this.fixed_style),
this.options.toolbar_style || {});
this.make(parent);
this.toolbar.css(this.options.toolbar_style);
this.setup_image_button();
this.bind_events();
//this.bind_touch();
},
fixed_style: {
position: "fixed",
top: "0px",
"padding-top": "5px",
width: "100%",
height: "45px",
"background-color": "black",
display: "none"
},
inline_style: {
"padding-top": "5px",
},
make: function(parent) {
if(!parent)
parent = $("body");
if(!parent.find(".frappe-list-toolbar").length) {
this.toolbar = $(frappe.render_template("editor")).prependTo(parent);

if(this.inline) {
this.toolbar.find("[data-action]").remove();
} else {
this.toolbar.find(".btn-toolbar").addClass("container");
}
}
},

setup_image_button: function() {
// magic-overlay
var me = this;
this.file_input = this.toolbar.find('input[type="file"]')
.css({
'visibility': 'hidden',
'width':0,
'height':0
});
this.toolbar.find(".btn-insert-img").on("click", function() {
me.file_input.trigger("click");
})
},

show: function() {
var me = this;
this.toolbar.toggle(true);
if(!this.inline) {
$("body").animate({"padding-top": this.toolbar.outerHeight() }, {
complete: function() { me.toolbar.css("z-index", 1001); }
});
}
},

hide: function() {
if(!this.editor)
return;
var me = this;
this.toolbar.css("z-index", 0);
if(!this.inline) {
$("body").animate({"padding-top": 0 }, {complete: function() {
me.toolbar.toggle(false);
}});
}

this.editor && this.editor.attr('contenteditable', false).data("object").onhide();
this.editor = null;
},

bind_events: function () {
var me = this;

// standard button events
this.toolbar.find('a[data-' + me.options.command_role + ']').click(function (e) {
me.restore_selection();
me.editor.focus();
me.execCommand($(this).data(me.options.command_role));
me.save_selection();
// close dropdown
if(me.toolbar.find("ul.dropdown-menu:visible").length)
me.toolbar.find('[data-toggle="dropdown"]').dropdown("toggle");
e.stopPropagation();
e.preventDefault();
return false;
});
this.toolbar.find('[data-toggle=dropdown]').click(function() { me.restore_selection() });

// link
this.toolbar.find(".btn-add-link").on("click", function() {
if(!me.toolbar.bs_link_editor) {
if(me.inline) {
me.toolbar.bs_link_editor = new bsLinkEditor(me);
} else {
if(!window.bs_link_editor) {
window.bs_link_editor = new bsLinkEditor(me);
}
me.toolbar.bs_link_editor = window.bs_link_editor;
}
}
me.toolbar.bs_link_editor.show();
});

// file event
this.toolbar.find('input[type=file][data-' + me.options.command_role + ']').change(function () {
me.restore_selection();
if (this.type === 'file' && this.files && this.files.length > 0) {
me.editor.data("object").insert_files(this.files);
}
me.save_selection();
this.value = '';

return false;
});

// save
this.toolbar.find("[data-action='Save']").on("click", function() { me.hide(); });

// edit html
this.toolbar.find(".btn-html").on("click", function() {
new bsHTMLEditor().show(me.editor);
});
},

update: function () {
var me = this;
if (this.toolbar) {
$(this.toolbar).find('.btn[data-' + this.options.command_role + ']').each(function () {
var command = $(this).data(me.options.command_role);

// try catch for buggy firefox!
try {
var query_command_state = document.queryCommandState(command);
} catch(e) {
var query_command_state = false;
}

if (query_command_state) {
$(this).addClass(me.options.active_toolbar_class);
} else {
$(this).removeClass(me.options.active_toolbar_class);
}
});
}
},

execCommand: function (commandWithArgs, valueArg) {
var commandArr = commandWithArgs.split(' '),
command = commandArr.shift(),
args = commandArr.join(' ') + (valueArg || '');
document.execCommand(command, 0, args);
this.update();
},

get_current_range: function () {
var sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
return sel.getRangeAt(0);
}
},

save_selection: function () {
this.selected_range = this.get_current_range();
},

restore_selection: function () {
var selection = window.getSelection();
if (this.selected_range) {
selection.removeAllRanges();
selection.addRange(this.selected_range);
}
},

mark_selection: function (input, color) {
this.restore_selection();
document.execCommand('hiliteColor', 0, color || 'transparent');
this.save_selection();
input.data(this.options.selection_marker, color);
},

// bind_touch: function() {
// var me = this;
// $(window).bind('touchend', function (e) {
// var isInside = (me.editor.is(e.target) || me.editor.has(e.target).length > 0),
// current_range = me.get_current_range(),
// clear = current_range && (current_range.startContainer === current_range.endContainer && current_range.startOffset === current_range.endOffset);
// if (!clear || isInside) {
// me.save_selection();
// me.update();
// }
// });
// }
});

bsHTMLEditor = Class.extend({
init: function() {
var me = this;
this.modal = bs_get_modal("<i class='fa fa-code'></i> Edit HTML", '<textarea class="form-control" \
style="height: 400px; width: 100%; font-family: Monaco, \'Courier New\', monospace; font-size: 11px">\
</textarea>');
this.modal.addClass("frappe-ignore-click");
this.modal.find(".btn-primary").removeClass("hide").html(__("Update")).on("click", function() {
me._html = me.modal.find("textarea").val();

$.each(me.editor.dataurls, function(key, val) {
me._html = replace_all(me._html, key, val);
});

var editor = me.editor.data("object");
editor.set_input(me._html);
editor.options.change && editor.options.change(editor.clean_html());
me.modal.modal("hide");
});
},
show: function(editor) {
var me = this;
this.editor = editor;
this.modal.modal("show");
var html = me.editor.html();
// pack dataurls so that html display is faster
this.editor.dataurls = {}
html = html.replace(/<img\s*src=\s*["\'](data:[^,]*),([^"\']*)["\']/g, function(full, g1, g2) {
var key = g2.slice(0,5) + "..." + g2.slice(-5);
me.editor.dataurls[key] = g1 + "," + g2;
return '<img src="'+ key+'"';
});
this.modal.find("textarea").val(html_beautify(html));
}
});

bsLinkEditor = Class.extend({
init: function(toolbar) {
var me = this;
this.toolbar = toolbar;
this.modal = bs_get_modal("<i class='fa fa-globe'></i> Insert Link", '<div class="form-group">\
<input type="text" class="form-control" placeholder="http://example.com" />\
</div>\
<div class="checkbox" style="position: static;">\
<label>\
<input type="checkbox"> <span>' + __("Open Link in a new Window") + '</span>\
</label>\
</div>\
<button class="btn btn-primary" style="margin-top: 7px;">' + __("Insert") + '</button>');

this.modal.addClass("frappe-ignore-click");
this.modal.find(".btn-primary").on("click", function() {
me.toolbar.restore_selection();
var url = me.modal.find("input[type=text]").val();
var selection = me.toolbar.selected_range.toString();
if(url) {
if(me.modal.find("input[type=checkbox]:checked").length) {
var html = "<a href='" + url + "' target='_blank'>" + selection + "</a>";
document.execCommand("insertHTML", false, html);
} else {
document.execCommand("CreateLink", false, url);
}
}
me.modal.modal("hide");
return false;
});
},
show: function() {
this.modal.find("input[type=text]").val("");
this.modal.modal("show");
}
});

bs_get_modal = frappe.get_modal;

+ 35
- 17
frappe/public/js/frappe/ui/keyboard.js 查看文件

@@ -2,23 +2,7 @@ frappe.provide('frappe.ui.keys.handlers');

frappe.ui.keys.setup = function() {
$(window).on('keydown', function(e) {
var key = e.key;
//safari doesn't have key property
if(!key) {
key = String.fromCharCode(e.keyCode).toLowerCase();
}
if(key.substr(0, 5)==='Arrow') {
// ArrowDown -> down
key = key.substr(5).toLowerCase();
}
if(e.ctrlKey || e.metaKey) {
// add ctrl+ the key
key = 'ctrl+' + key;
}
if(e.shiftKey) {
// add ctrl+ the key
key = 'shift+' + key;
}
var key = frappe.ui.keys.get_key(e);
if(frappe.ui.keys.handlers[key]) {
var out = null;
for(var i=0, l = frappe.ui.keys.handlers[key].length; i<l; i++) {
@@ -34,6 +18,29 @@ frappe.ui.keys.setup = function() {
});
}

frappe.ui.keys.get_key = function(e) {
var key = e.key;
//safari doesn't have key property
if(!key) {
var keycode = e.keyCode || e.which;
key = frappe.ui.keys.key_map[keycode] ||
String.fromCharCode(keycode);
}
if(key.substr(0, 5)==='Arrow') {
// ArrowDown -> down
key = key.substr(5).toLowerCase();
}
if(e.ctrlKey || e.metaKey) {
// add ctrl+ the key
key = 'ctrl+' + key;
}
if(e.shiftKey) {
// add ctrl+ the key
key = 'shift+' + key;
}
return key.toLowerCase();
}

frappe.ui.keys.on = function(key, handler) {
if(!frappe.ui.keys.handlers[key]) {
frappe.ui.keys.handlers[key] = [];
@@ -98,3 +105,14 @@ frappe.ui.keys.on('ctrl+up', function(e) {
frappe.ui.keys.on('shift+ctrl+r', function(e) {
frappe.ui.toolbar.clear_cache();
});

frappe.ui.keys.key_map = {
8: 'backspace',
9: 'tab',
13: 'enter',
16: 'shift',
17: 'ctrl',
91: 'meta',
18: 'alt',
27: 'escape'
}

+ 1
- 0
frappe/public/js/lib/summernote/summernote.css
文件差異過大導致無法顯示
查看文件


+ 7046
- 0
frappe/public/js/lib/summernote/summernote.js
文件差異過大導致無法顯示
查看文件


+ 25
- 0
frappe/public/less/desk.less 查看文件

@@ -504,4 +504,29 @@ textarea.form-control {

.search-result {
margin-bottom: 24px;
}

// summernote editor
.note-editor {
margin-top: 5px;

&.note-frame {
border-color: @border-color;
}

.btn {
outline: none !important;
}

.dropdown-style > li > a > * {
margin: 0;
}
.fa.fa-check {
color: @text-color !important;
}
.dropdown-menu {
z-index: 100;
max-height: 300px;
overflow: scroll;
}
}

Loading…
取消
儲存