* [summernote] working commit * fix set_input method, esc key fix * styling fixes * removed old texteditorversion-14
@@ -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", | |||
@@ -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; | |||
} |
@@ -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 <!-- markdown --> \ | |||
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'); | |||
} | |||
}); | |||
@@ -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 <p>"> | |||
<p>{{ __("Paragraph") }}</p></a> | |||
</li> | |||
<li><a href="#" data-edit="formatBlock <h1>"> | |||
<h1>{{ __("Heading") }} 1</h1></a> | |||
</li> | |||
<li><a href="#" data-edit="formatBlock <h2>"> | |||
<h2>{{ __("Heading") }} 2</h2></a> | |||
</li> | |||
<li><a href="#" data-edit="formatBlock <h3>"> | |||
<h3>{{ __("Heading") }} 3</h3></a> | |||
</li> | |||
<li><a href="#" data-edit="formatBlock <h4>"> | |||
<h4>{{ __("Heading") }} 4</h4></a> | |||
</li> | |||
<li><a href="#" data-edit="formatBlock <h5>"> | |||
<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> |
@@ -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; |
@@ -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' | |||
} |
@@ -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; | |||
} | |||
} |