I would like to prevent the pressing of ENTER if the kendo editor body has a certain height (to limit the max height of the editor field). But none of my tries worked.
<textarea data-role="editor"
data-bind="events: { keydown: onEditorKeyDown }">
</textarea>
viewModel.onEditorKeyDown = function (e) {
var key = e.keyCode;
if (key == 13) {
var editor = e.sender;
var body = editor.body;
var height = body.scrollHeight;
if (height > 195) {
?? //tried e.preventDefault(), return false etc.
}
}
};
I've managed to find two solutions. One is dirty hack and other doesn't match your requirements 100%. But both perform what is needed more or less.
New paragraph is added via embedded editor insertParagraph command which overrides default keydown logic. So the first solution is to temporary override this command using keydown and keyup events like this:
<textarea data-role="editor"
data-bind="events: { keydown: onEditorKeyDown, keyup: onEditorKeyUp }">
</textarea>
// this should probably be moved to viewModel, it's here for demo puproses only
var savedCommand;
var viewModel = kendo.observable({
html: null,
isVisible: true,
onChange: function() {
kendoConsole.log("event :: change (" + kendo.htmlEncode(this.get("html")) + ")");
}
});
viewModel.onEditorKeyDown = function (e) {
var key = e.keyCode;
if (key == 13) {
var editor = e.sender;
var body = editor.body;
var height = body.scrollHeight;
if (height > 195) {
savedCommand = editor.toolbar.tools.insertParagraph.command;
editor.toolbar.tools.insertParagraph.command = function() {};
}
}
};
viewModel.onEditorKeyUp = function (e) {
var key = e.keyCode;
if (key == 13) {
if (savedCommand) {
var editor = e.sender;
editor.toolbar.tools.insertParagraph.command = savedCommand;
savedCommand = undefined;
}
}
};
kendo.bind($("#example"), viewModel);
This works fine, but looks a bit ugly.
Other solution is to execute editor undo command if needed. It works too, but empty line always flickers for a moment:
<textarea data-role="editor"
data-bind="events: { keyup: onEditorKeyUp }"></textarea>
var viewModel = kendo.observable({
html: null,
isVisible: true,
onChange: function() {
kendoConsole.log("event :: change (" + kendo.htmlEncode(this.get("html")) + ")");
}
});
viewModel.onEditorKeyUp = function (e) {
var key = e.keyCode;
if (key == 13) {
var editor = e.sender;
var body = editor.body;
while (body.scrollHeight > 195) {
editor.exec('undo');
}
}
};
kendo.bind($("#example"), viewModel);
UPD: I've found event better solution. You can use execute event, see http://docs.telerik.com/kendo-ui/api/javascript/ui/editor#events-execute
Then check conditions and filter by command name to cancel insert of new paragraph:
execute: function(e) {
alert(e.name);
e.preventDefault();
}
Related
I want to add a custom button to the Summernote toolbar that opens up a dialog that has a textbox for a URL and several checkboxes for settings. I then want to use the info from the dialog to scrape web pages and do processing on the content. The ultimate goal is to place the scraped content into the editor starting where the cursor is. I've searched and found some code on creating a custom button, but not any solid examples of implementing a dialog. I went through the summernote.js code to see how the Insert Image dialog works and that left me really confused. The test code I've got so far is in the code block, below. Thanks in advance to anyone who can help get me sorted out.
var showModalDialog = function(){
alert("Not Implemented");
};
var AddWiki = function(context) {
var ui = $.summernote.ui;
var button = ui.button({
contents: '<i class="fa fa-plus"/> Add Wiki',
tooltip: "Set a New Wiki",
class: "btn-primary",
click: function() {
showModalDialog();
}
});
return button.render();
};
$(".tw-summernote-instance textarea").summernote({
airMode: false,
dialogsInBody: false,
toolbar: [["mybutton", ["customButton"]]],
buttons: {
customButton: AddWiki
},
callbacks: {
onInit: function(e) {
var o = e.toolbar[0];
jQuery(o)
.find("button:first")
.addClass("btn-primary");
}
}
});
I found a good, simple example of what I wanted to do. Here's the code:
(function(factory) {
/* global define */
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
// Node/CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals
factory(window.jQuery);
}
}(function($) {
$.extend($.summernote.plugins, {
'synonym': function(context) {
var self = this;
var ui = $.summernote.ui;
var $editor = context.layoutInfo.editor;
var options = context.options;
context.memo('button.synonym', function() {
return ui.button({
contents: '<i class="fa fa-snowflake-o">',
tooltip: 'Create Synonym',
click: context.createInvokeHandler('synonym.showDialog')
}).render();
});
self.initialize = function() {
var $container = options.dialogsInBody ? $(document.body) : $editor;
var body = '<div class="form-group">' +
'<label>Add Synonyms (comma - , - seperated</label>' +
'<input id="input-synonym" class="form-control" type="text" placeholder="Insert your synonym" />'
'</div>'
var footer = '<button href="#" class="btn btn-primary ext-synonym-btn">OK</button>';
self.$dialog = ui.dialog({
title: 'Create Synonym',
fade: options.dialogsFade,
body: body,
footer: footer
}).render().appendTo($container);
};
// You should remove elements on `initialize`.
self.destroy = function() {
self.$dialog.remove();
self.$dialog = null;
};
self.showDialog = function() {
self
.openDialog()
.then(function(data) {
ui.hideDialog(self.$dialog);
context.invoke('editor.restoreRange');
self.insertToEditor(data);
console.log("dialog returned: ", data)
})
.fail(function() {
context.invoke('editor.restoreRange');
});
};
self.openDialog = function() {
return $.Deferred(function(deferred) {
var $dialogBtn = self.$dialog.find('.ext-synonym-btn');
var $synonymInput = self.$dialog.find('#input-synonym')[0];
ui.onDialogShown(self.$dialog, function() {
context.triggerEvent('dialog.shown');
$dialogBtn
.click(function(event) {
event.preventDefault();
deferred.resolve({
synonym: $synonymInput.value
});
});
});
ui.onDialogHidden(self.$dialog, function() {
$dialogBtn.off('click');
if (deferred.state() === 'pending') {
deferred.reject();
}
});
ui.showDialog(self.$dialog);
});
};
this.insertToEditor = function(data) {
console.log("synonym: " + data.synonym)
var dataArr = data.synonym.split(',');
var restArr = dataArr.slice(1);
var $elem = $('<span>', {
'data-function': "addSynonym",
'data-options': '[' + restArr.join(',').trim() + ']',
'html': $('<span>', {
'text': dataArr[0],
'css': {
backgroundColor: 'yellow'
}
})
});
context.invoke('editor.insertNode', $elem[0]);
};
}
});
}));
I want to write a ckeditor plugin that adds a checkbox to the toolbar.
When an img element is selected, the checkbox should reflect whether the image has a certain class or not.
When something else than an image is selected, the checkbox should be
disabled, or maybe invisible.
When I check or uncheck the checkbox, the class should be added/removed to/from the img.
In other words, I want to add something else than a button to the toolbar (those that are added with editor.ui.addButton), and that something should be a checkbox.
How do I do that?
I managed to do it anyway, using editor.ui.add and editor.ui.addHandler. Here is a screenshot:
plugin.js:
CKEDITOR.plugins.add('add_zoomable_to_image',
{
init: function( editor )
{
var disabled_span_color = "#cccccc";
var enabled_span_color = "#000000";
var cb;
var span;
var html =
"<div style='height: 25px; display: inline-block'>" +
"<div style='margin: 5px'><input class='add_zoomable_cb' type='checkbox' disabled='disabled'>" +
"<span class='add_zoomable_span' style='color: " + disabled_span_color + "'> Add zoomable to image</span>" +
"</div>" +
"</div>";
editor.ui.add('add_zoomable_to_image', "zoomable_button", {});
editor.ui.addHandler("zoomable_button",
{
create: function ()
{
return {
render: function (editor, output)
{
output.push(html);
return {};
}
};
}
});
editor.on("selectionChange", function()
{
var sel = editor.getSelection();
var ranges = sel.getRanges();
if (ranges.length == 1)
{
var el = sel.getStartElement();
if (el.is('img'))
{
enable_input();
if (el.hasClass('zoomable'))
check_cb();
else
uncheck_cb();
}
else
disable_input();
}
else
disable_input();
});
editor.on("instanceReady", function ()
{
cb = $('.add_zoomable_cb');
span = $('.add_zoomable_span');
cb.change(function()
{
var element = editor.getSelection().getStartElement();
if (cb.is(':checked'))
element.addClass('zoomable');
else
element.removeClass('zoomable');
});
});
function enable_input()
{
cb.removeAttr('disabled');
span.css('color', enabled_span_color);
}
function disable_input()
{
uncheck_cb();
cb.attr('disabled', 'disabled');
span.css('color', disabled_span_color);
}
function check_cb()
{
cb.attr('checked', 'checked');
}
function uncheck_cb()
{
cb.removeAttr('checked');
}
}
});
CKEditor doesn't include the option to add a checkbox to the toolbar, so your first step is to look at their code and extend it to add the checkbox.
Then look at how other buttons work to modify the content and detect when the selection in the editor changes to reflect its new state, and apply those ideas to your checkbox.
I use the https://github.com/erikvold/toolbarbutton-jplib/ package to add a toolbar button from an addon-sdk addon to the firefox navigation bar. Is it possible to add a drop down menu to this button (as I understand there are no easy ways)?
This is how I do it; hope it works for you.
var winUtils = require("window-utils");
var delegate = {
onTrack: function(window) {
if(window.location != "chrome://browser/content/browser.xul") {
// console.log("=> win location false");
return;
}
console.log("window tracked");
var document = window.document;
var navBar = document.getElementById('nav-bar');
var btn = document.createElement('toolbarbutton');
btn.setAttribute('id', 'button');
btn.setAttribute('type', 'menu-button');
btn.setAttribute('class', 'toolbarbutton-1');
btn.setAttribute('image', 'http://www.facebook.com/favicon.ico');
btn.addEventListener('command', function() {
console.log("this=" + this.id);
// your callback code here
}
, false);
var menupopup = document.createElement('menupopup');
menupopup.setAttribute('id', 'menupopup');
menupopup.addEventListener('command', function(event) {
// TODO your callback
}
, false);
//menu items
var menuitem1 = document.createElement('menuitem');
menuitem1.setAttribute('id', 'menuitem1');
menuitem1.setAttribute('label', 'Menu item1');
menuitem1.setAttribute('class', 'menuitem-iconic');
menuitem1.addEventListener('command', function(event) {
// CODE
}
, false);
}
winUtils.WindowTracker(delegate);
This code requires a few more lines to actually work:
var delegate = {
onTrack: function(window) {
if(window.location != "chrome://browser/content/browser.xul") {
// console.log("=> win location false");
return;
}
var document = window.document;
var navBar = document.getElementById('nav-bar');
var btn = document.createElement('toolbarbutton');
btn.setAttribute('id', 'button');
btn.setAttribute('type', 'menu-button');
btn.setAttribute('class', 'toolbarbutton-1');
btn.setAttribute('image', 'http://www.facebook.com/favicon.ico');
btn.addEventListener('command', function() {
console.log("this=" + this.id);
// your callback code here
}
, false);
var menupopup = document.createElement('menupopup');
menupopup.setAttribute('id', 'menupopup');
menupopup.addEventListener('command', function(event) {
// TODO your callback
}
, false);
//menu items
var menuitem1 = document.createElement('menuitem');
menuitem1.setAttribute('id', 'menuitem1');
menuitem1.setAttribute('label', 'Menu item1');
menuitem1.setAttribute('class', 'menuitem-iconic');
menuitem1.addEventListener('command', function(event) {
// CODE
}
, false);
menupopup.appendChild(menuitem1);
btn.appendChild(menupopup);
navBar.appendChild(btn);
console.log("window tracked");
}
};
//let utils = require('api-utils/window-utils');
let utils = require('sdk/deprecated/window-utils'); // for new style sdk
utils.WindowTracker(delegate);
Toggle button in jqgrid top toolbar is defined using Oleg answer as
var autoedit;
$("#grid_toppager_left table.navtable tbody tr").append(
'<td class="ui-pg-button ui-corner-all" >' +
'<div class="ui-pg-div my-nav-checkbox">' +
'<input tabindex="-1" type="checkbox" id="AutoEdit" '+(autoedit ? 'checked ' : '')+'/>' +
'<label title="Toggle autoedit" for="AutoEdit">this text is ignored in toolbar</label></div></td>'
);
$("#AutoEdit").button({
text: false,
icons: {primary: "ui-icon-star"}
}).click(function () {
autoedit = $(this).is(':checked');
});
Answer from how to add standard textbox command to jqgrid context menu is used to autogenerate context menu for grid from toolbar.
In generated context menu for this item only text "this text is ignored in toolbar" appears and selecting it does nothing.
How to make it work or remove this item from context menu?
Look at the demo or the same demo with another themes: this and this.
First of all I modified the code of the jquery.contextmenu.js to support jQuery UI Themes. Then I modified the code more, to be able to create context menu more dynamically. In the modified version of jquery.contextmenu.js one can crate menu and the bindings not only in the onContextMenu, but also in onShowMenu. Inside of onContextMenu one can create just the empty menu
<div id="myMenu"><ul></ul></div>
It is important only if one use dynamically switching of the icons of the text of buttons from the navigator bar.
You can download the modified version of the file here.
In the demo I used the last modification of the code from the answer, so the standard context menu can be still used in the grid on selected text or in the enabled input/textarea fields. The context menu of the browser will be displayed in the case:
I modified the code of createContexMenuFromNavigatorButtons function from the answer to the following:
var getSelectedText = function () {
var text = '';
if (window.getSelection) {
text = window.getSelection();
} else if (document.getSelection) {
text = document.getSelection();
} else if (document.selection) {
text = document.selection.createRange().text;
}
return typeof (text) === 'string' ? text : text.toString();
},
createContexMenuFromNavigatorButtons = function (grid, pager) {
var menuId = 'menu_' + grid[0].id, menuUl = $('<ul>'),
menuDiv = $('<div>').attr('id', menuId);
menuUl.appendTo(menuDiv);
menuDiv.appendTo('body');
grid.contextMenu(menuId, {
bindings: {}, // the bindings will be created in the onShowMenu
onContextMenu: function (e) {
var p = grid[0].p, i, lastSelId, $target = $(e.target),
rowId = $target.closest("tr.jqgrow").attr("id"),
isInput = $target.is(':text:enabled') ||
$target.is('input[type=textarea]:enabled') ||
$target.is('textarea:enabled');
if (rowId && !isInput && getSelectedText() === '') {
i = $.inArray(rowId, p.selarrrow);
if (p.selrow !== rowId && i < 0) {
// prevent the row from be unselected
// the implementation is for "multiselect:false" which we use,
// but one can easy modify the code for "multiselect:true"
grid.jqGrid('setSelection', rowId);
} else if (p.multiselect) {
// Edit will edit FIRST selected row.
// rowId is allready selected, but can be not the last selected.
// Se we swap rowId with the first element of the array p.selarrrow
lastSelId = p.selarrrow[p.selarrrow.length - 1];
if (i !== p.selarrrow.length - 1) {
p.selarrrow[p.selarrrow.length - 1] = rowId;
p.selarrrow[i] = lastSelId;
p.selrow = rowId;
}
}
return true;
} else {
return false; // no contex menu
}
},
onShowMenu: function (e, $menu) {
var options = this, $menuUl = $menu.find('ul:first').empty();
$('table.navtable .ui-pg-button', pager).each(function () {
var $spanIcon, text, $td, id, $li, $a, button,
$div = $(this).children('div.ui-pg-div:first'),
gridId = grid[0].id;
if ($div.length === 1) {
text = $div.text();
$td = $div.parent();
if (text === '') {
text = $td.attr('title');
}
if (this.id !== '' && text !== '') {
id = 'menuitem_' + this.id;
if (id.length > gridId.length + 2) {
id = id.substr(0, id.length - gridId.length - 1);
}
} else {
// for custom buttons
id = $.jgrid.randId();
}
$li = $('<li>').attr('id', id);
$spanIcon = $div.children('span.ui-icon');
if ($spanIcon.length > 0) {
// standard navGrid button or button added by navButtonAdd
$li.append($('<a>')
.text(text)
.prepend($spanIcon.clone().css({
float: 'left',
marginRight: '0.5em'
})));
$menuUl.append($li);
options.bindings[id] = (function ($button) {
return function () { $button.click(); };
}($div));
} else {
button = $div.children("input").data("button");
if (button !== undefined) {
$a = $('<a>')
.text(button.options.label)
.prepend(
$('<label>').addClass("ui-corner-all").css({
float: 'left',
width: '16px',
borderWidth: '0px',
marginRight: '0.5em'//'4px'
}).append(
$('<span>').addClass("ui-button-icon-primary ui-icon " +
button.options.icons.primary)
.css({
float: 'left',
marginRight: '0.5em'
})
)
);
$li.append($a);
if (button.type === "checkbox" && button.element.is(':checked')) {
$a.find('label:first').addClass("ui-state-active");
}
$menuUl.append($li);
options.bindings[id] = (function ($button, isCheckbox) {
if (isCheckbox) {
return function () {
if ($button.is(':checked')) {
$button.siblings('label').removeClass("ui-state-active");
} else {
$button.siblings('label').addClass("ui-state-active");
}
$button.click();
$button.button("refresh"); // needed for IE7-IE8
};
} else {
return function () { $button.click(); };
}
}(button.element, button.type === "checkbox"));
}
}
}
});
return $menu;
}
});
},
autoedit = false;
and fill the check-button in the navigator bar with the code which is changed only a little:
$("#pager_left table.navtable tbody tr").append(
'<td class="ui-pg-button ui-corner-all">' +
'<div class="ui-pg-div my-nav-checkbox">' +
'<input tabindex="-1" type="checkbox" id="AutoEdit" />' +
'<label title="Checkx caption which should appear as button tooltip"' +
' for="AutoEdit">Autoedit</label></div></td>'
);
$("#AutoEdit").button({
text: false,
icons: {primary: "ui-icon-mail-closed"}
}).click(function () {
var iconClass, $this = $(this);
if (!autoedit) { // $this.is(':checked')) {
autoedit = true;
iconClass = "ui-icon-mail-open";
} else {
autoedit = false;
iconClass = "ui-icon-mail-closed";
}
$this.button("option", {icons: {primary: iconClass}});
});
createContexMenuFromNavigatorButtons($grid, '#pager');
UPDATED: One more demo which support buttons added by new inlineNav method you can find here. Additionally I included in the demo the function normalizePagers which I use to improve the look of the pager:
How you can see the contextmenu includes only enabled buttons from the navigator bar.
Inside client control I generate a button, with script to run.
I want to call object's Print() method when this button is clicked, the result value must be passed to Print() as well.
How can I do that?
This is my object:
Type.registerNamespace("CustomControls");
CustomControls.FirstObj = function(element) {
CustomControls.FirstObj.initializeBase(this, [element]);
this._targetControlDelegate === null
this.markUp = '<div><input type="button" id="theButton" value="Button!" onclick="Foo()"/><script type="text/javascript">function Foo() {return "result";}</script></div>';
}
CustomControls.FirstObj.prototype = {
dispose: function() {
CustomControls.FirstObj.callBaseMethod(this, 'dispose');
},
initialize: function() {
var div;
div = document.createElement('div');
div.name = div.id = "divName";
div.innerHTML = this.markUp;
document.body.appendChild(div);
var targetControl = $get("theButton");
// if (targetControl != null) {
// if (this._targetControlDelegate === null) {
// this._targetControlDelegate = Function.createDelegate(this, this._targetControlHandler);
// }
// Sys.UI.DomEvent.addHandler(targetControl, 'click', this._targetControlDelegate);
// }
CustomControls.FirstObj.callBaseMethod(this, 'initialize');
},
// _targetControlHandler: function(event) {
//
//
// },
_Print: function(result) {
//Alert Result
},
}
CustomControls.FirstObj.registerClass('CustomControls.FirstObj', Sys.UI.Control);
if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
Update:
I guess there is no solution for my problem.
Maybe there is an alternative approach that you can suggest?
One way would be to store the FirstObj object in a property of the button it just created:
initialize: function() {
var div = document.createElement("div");
div.name = div.id = "divName";
div.innerHTML = this.markUp;
document.body.appendChild(div);
var targetControl = $get("theButton");
targetControl.__firstObj = this;
CustomControls.FirstObj.callBaseMethod(this, 'initialize');
}
That would allow you to use that property to refer to the FirstObj object inside your markup:
CustomControls.FirstObj = function(element) {
CustomControls.FirstObj.initializeBase(this, [element]);
this._targetControlDelegate = null;
this.markUp = '<div><input type="button" id="theButton" value="Button!"'
+ 'onclick="this.__firstObj._Print(Foo());" />'
+ '<script type="text/javascript">function Foo() {return "result";}'
+ '</script></div>';
}