* [summernote] working commit * fix set_input method, esc key fix * styling fixes * removed old texteditorversion-14
@@ -41,6 +41,7 @@ | |||||
"css/desk.min.css": [ | "css/desk.min.css": [ | ||||
"public/js/lib/datepicker/datepicker.min.css", | "public/js/lib/datepicker/datepicker.min.css", | ||||
"public/js/lib/awesomplete/awesomplete.css", | "public/js/lib/awesomplete/awesomplete.css", | ||||
"public/js/lib/summernote/summernote.css", | |||||
"public/css/bootstrap.css", | "public/css/bootstrap.css", | ||||
"public/css/font-awesome.css", | "public/css/font-awesome.css", | ||||
"public/css/octicons/octicons.css", | "public/css/octicons/octicons.css", | ||||
@@ -66,6 +67,7 @@ | |||||
"public/js/lib/awesomplete/awesomplete.min.js", | "public/js/lib/awesomplete/awesomplete.min.js", | ||||
"public/js/lib/Sortable.min.js", | "public/js/lib/Sortable.min.js", | ||||
"public/js/lib/taggle/taggle.min.js", | "public/js/lib/taggle/taggle.min.js", | ||||
"public/js/lib/summernote/summernote.js", | |||||
"public/js/lib/notify.js", | "public/js/lib/notify.js", | ||||
"public/js/lib/bootstrap.min.js", | "public/js/lib/bootstrap.min.js", | ||||
"public/js/lib/moment/moment-with-locales.min.js", | "public/js/lib/moment/moment-with-locales.min.js", | ||||
@@ -642,3 +642,23 @@ fieldset[disabled] .form-control { | |||||
.search-result { | .search-result { | ||||
margin-bottom: 24px; | 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({ | frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({ | ||||
editor_name: "bsEditor", | |||||
horizontal: false, | |||||
make_input: function() { | make_input: function() { | ||||
//$(this.input_area).css({"min-height":"360px"}); | |||||
this.has_input = true; | 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; | 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() { | 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) { | 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 = ""; | if(value == null) value = ""; | ||||
value = frappe.dom.remove_script_and_style(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; | this.last_value = value; | ||||
}, | }, | ||||
set_focus: function() { | 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() { | frappe.ui.keys.setup = function() { | ||||
$(window).on('keydown', function(e) { | $(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]) { | if(frappe.ui.keys.handlers[key]) { | ||||
var out = null; | var out = null; | ||||
for(var i=0, l = frappe.ui.keys.handlers[key].length; i<l; i++) { | 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) { | frappe.ui.keys.on = function(key, handler) { | ||||
if(!frappe.ui.keys.handlers[key]) { | if(!frappe.ui.keys.handlers[key]) { | ||||
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.keys.on('shift+ctrl+r', function(e) { | ||||
frappe.ui.toolbar.clear_cache(); | 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 { | .search-result { | ||||
margin-bottom: 24px; | 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; | |||||
} | |||||
} | } |