I'm using ExtJS 3.2.1 and I need a component almost identical to the bundled HtmlEditor, with one exception: it must start editing the HTML source code directly. The reason I don't use a normal TextArea is that the user should be able to preview the result of his actions before submitting.
I've tried calling toggleSourceEdit(), as per ExtJS documentation, with no success. Debugging, I see that the editor object has the sourceEditMode property set to true, and the Source Edit button seems as if it was "pressed", but clicking on it does not render the typed HTML, and clicking it again goes to the Source Mode.
I've tried calling toggleSourceEdit() after the container show() method, on the container afterLayout listener and on the editor afterRender listener. I've tried also calling it on another button that I added to the container. The result is the same on every try.
The only other option I see is updating ExtJS to 3.3.0, but I haven't seem anything related on the changelogs. Either way, it's going to be my next step. EDIT: The app had another problems when updating, we'll make a bigger effort to update later. As of right now, we are using the HtmlEditor in its original setting.
Thanks!
ran into the same problem (using 3.3.0 by the way)
stumbled upon a fix by dumb luck. i have no idea why this works, but second time is the charm. call it twice in a row to achieve the desired effect..
HTMLEditor.toggleSourceEdit(true);
HTMLEditor.toggleSourceEdit(true);
hope that helps!
Rather calling toggleSourceEdit(), try to setup the configuration while you create HtmlEditor Object
Using toggleSourceEdit() caused some problems for me. One was that this seemed to put the editor somewhere in limbo between source edit and WYSIWYG mode unless I used a timeout of 250ms or so. It also puts the focus in that editor, and I don't want to start the form's focus in the editor, especially since it's below the fold and the browser scrolls to the focused html editor when it opens.
The only thing that worked for me was to extend Ext.form.HtmlEditor and then overwrite toggleSourceEdit, removing the focus command. Then adding a listener for toggling to the source editor when the component is initialized. This is for Ext 4.1 and up. For older versions, replace me.updateLayout() with me.doComponentLayout().
var Namespace = {
SourceEditor: Ext.define('Namespace.SourceEditor', {
extend: 'Ext.form.HtmlEditor',
alias: 'widget.sourceeditor',
initComponent: function() {
this.callParent(arguments);
},
toggleSourceEdit: function (sourceEditMode) {
var me = this,
iframe = me.iframeEl,
textarea = me.textareaEl,
hiddenCls = Ext.baseCSSPrefix + 'hidden',
btn = me.getToolbar().getComponent('sourceedit');
if (!Ext.isBoolean(sourceEditMode)) {
sourceEditMode = !me.sourceEditMode;
}
me.sourceEditMode = sourceEditMode;
if (btn.pressed !== sourceEditMode) {
btn.toggle(sourceEditMode);
}
if (sourceEditMode) {
me.disableItems(true);
me.syncValue();
iframe.addCls(hiddenCls);
textarea.removeCls(hiddenCls);
textarea.dom.removeAttribute('tabindex');
//textarea.focus();
me.inputEl = textarea;
} else {
if (me.initialized) {
me.disableItems(me.readOnly);
}
me.pushValue();
iframe.removeCls(hiddenCls);
textarea.addCls(hiddenCls);
textarea.dom.setAttribute('tabindex', -1);
me.deferFocus();
me.inputEl = iframe;
}
me.fireEvent('editmodechange', me, sourceEditMode);
me.updateLayout();
}
})
}
Then to use it:
Ext.create('Namespace.SourceEditor', {
/*regular options*/
listeners: {
initialize: function(thisEditor) {
thisEditor.toggleSourceEdit();
}
}
});
htmlEditor.toggleSourceEdit(true);
one time should be enough if you do this listening to the afterrender event of the editor.
Related
Often I realize halfway through a notebook that I forgot an import and I want to move it to the top of the notebook (where I try to keep most of my imports). Is there a way to add a keyboard shortcut to ~/.jupyter/custom/custom.js that moves a cell to the top of a notebook?
Currently I do this by cutting the cell, scrolling to the top of the notebook, pasting and scrolling back down to where I was (often losing my place on the way back).
Below is some code from fastai forums to accomplish a different task: going to the running cell:
Jupyter.keyboard_manager.command_shortcuts.add_shortcut('CMD-I', {
help : 'Go to Running cell',
help_index : 'zz',
handler : function (event) {
setTimeout(function() {
// Find running cell and click the first one
if ($('.running').length > 0) {
//alert("found running cell");
$('.running')[0].scrollIntoView();
}}, 250);
return false;
}
});
It's partially documented; however you have to know a little JavaScript to create a keyboard shortcut.
As you've figured out, edit custom.js to bind a keyboard shortcut, as mentioned in the documentation (the second method must be used if the action is not built-in)
For the documentation, read the source code (either online or in site-packages/notebook/static/notebook/js/*.js if you installed jupyter locally). In particular, actions.js contains example bindings and notebook.js contains the functions that you can call on the notebook.
This is an example. It has the disadvantage of modifying the jupyter clipboard. To avoid that, you might be able to look into the source code of notebook.move_cell_down function to see how it's implemented, and modify it correspondingly.
Jupyter.keyboard_manager.command_shortcuts.add_shortcut('cmdtrl-I', {
help : 'Move cell to first in notebook',
handler : function (env) {
var index = env.notebook.get_selected_index();
env.notebook.cut_cell();
env.notebook.select(0);
env.notebook.paste_cell_above();
env.notebook.select(index);
}
});
I want to let users select (and copy) text within TinyMCE.
I'm not quite sure, but it seems regarding security that browsers don't allow that.
This Codepen is from the official TinyMCE site:
https://codepen.io/tinymce/pen/NGegZK
Here you can select text.
When you add there the following parameter in the 2nd line of the JavaScript as followed, then you can't longer select text.
readonly: true,
How can I set "readonly: true" and let the user still select text?
I appreciate any help.
I faced this problem too. Moreover, the inability to select text is nothing compared to the inability to click links. I've submitted an issue about this a while ago, but there is still no reaction.
You can use a workaround for now (codepen):
readonly: 1,
setup: function(editor) {
editor.on('SwitchMode', function() {
if (editor.readonly) {
editor.readonly = 1;
}
});
}
It exploits the fact that the event-blocking code uses strict comparison internally (readonly === true) while the rest of the code works fine with any other truthy value, e.g. 1. Of course, this hack might stop working after an update in the future, but it's much better than nothing.
Update: better switch to the inline mode (codepen) if you use this hack. Otherwise clicking links leads to a mess.
I have checked the source code of the lastest nightly and it seems that the behavior is hardcoded. All events are discarded if the editor is in readonly mode. Which means that selection events are discarded too :
var isListening = function (editor) {
return !editor.hidden && !editor.readonly;
};
var fireEvent = function (editor, eventName, e) {
if (isListening(editor)) {
editor.fire(eventName, e);
} else if (isReadOnly(editor)) {
e.preventDefault();
}
};
I might be wrong but I don't think you can change this behavior through customization.
Regards
I solved this issue for achieve readonly mode by myself, I would create an iframe dom node and put the editor's html segment into it.
renderReportPreview = contentHtml => {
const iframe = document.querySelector('iframe[name=preview]')
if (iframe) {
const cssLink = document.createElement('link')
// cssLink.href = 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css'
// I prefer semantic-ui, semantic-ui is more like tinyMce style
cssLink.href = 'https://cdn.bootcss.com/semantic-ui/2.3.1/semantic.min.css'
cssLink.rel = 'stylesheet'
cssLink.type = 'text/css'
iframe.contentWindow.document.head.appendChild(cssLink)
// I escape the origin editor's content, so I need decode them back
iframe.contentWindow.document.body.innerHTML = htmlDecode(contentHtml)
const allTables = iframe.contentWindow.document.body.querySelectorAll(
'table'
)
Array.from(allTables).forEach(table => {
// ui celled table for compatible semantic-ui
// table table-bordered for compatible bootstrap
table.className = 'ui celled table table-bordered'
})
this.setState({
previewRendered: true,
})
}
}
More detail on https://github.com/tinymce/tinymce/issues/4575
It was previously possible to select text in readonly mode, until the fix for #4249 broke it in 4.7.12.
We've just started tracking a fix that allows text to be selected and links to be clicked, follow either of the tickets linked here to be updated when we release a fix.
In CKEditor, how does one disable drag and dropping?
I do not want people to accidentally drag and drop other elements of the page into their respective editors.
I assume this requires intercepting browser specific events and blocking them but I am unsure how to do this in CKEditor.
Thanks for your help!
TAKEN FROM THIS STACKOVERFLOW ANSWER
At first I try disable with config.removePlugins = 'dragdrop,basket'; but it doesn't work at all.
Then I found this link, which help me to solved this problem and write a plugin to do the job.
Anyway I wrote here how to do the trick too.
With a Litle modification I wrote this plugin:
To use it you have to create a folder inside of ./plugins named "dropoff". Then create a file named plugin.js and put this content:
CKEDITOR.plugins.add('dropoff', {
init: function (editor) {
function regectDrop(event) {
event.data.preventDefault(true);
};
editor.on('contentDom', function() {
editor.document.on('drop',regectDrop);
});
}
});
After it, you have to registrate it on ckeditor config.js.
config.extraPlugins = 'dropoff';
If you already using an extra pluging just put a "," before like this:
config.extraPlugins = 'mypreviousplugin,dropoff';
And be Happy! \o/
I'm not sure why I can't get the button element using my UI hash. This is what my Layout looks like:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
ui: {
btnSave: "#btnSave"
},
events: {
"click #ui.btnSave": "onSave"
},
onInitialize: function () {
this.listenTo(App.vent, "DisableSaveButton", function(val) {
this.disableSaveButton(val);
},this);
},
disableSaveButton: function () {
this.ui.btnSave.prop("disabled",val).toggleClass("ui-state-disabled",val);
},
onSave: function () {
alert("saved!");
}
})
In VS2013, when my breakpoint hits the line inside disableSaveButton method, I entered $("#btnSave") into the Watch window and I was able to get the element back. I could tell because it had a length of 1. From this, I know the button is rendered. However, if I enter this.ui.btnSave into the Watch window, I would get an element with length of 0.
My BaseLayout object is basically a custom object extended from Marionette.Layout
Marionette version: 1.8.8
Any ideas why I can't find the button element using this.ui.btnSave?
Thanks in advance!
Got some help from a coworker and the issue might be because the element is out of scope. Basically, inside the Layout object, 'this' does not contain the element. We were able replace 'this.ui.btnSave' with '$("#btnSave",this.buttonset.el)' and that works fine. buttonset is the region that actually contains the html element.
This seems like an inconsistency because even though the ui hash didn't work, the click event utilizing the ui hash did work.
UPDATE 6/3/2015:
Another coworker of mine provided a better solution. Basically, in my Layout I use a display function to display my view. It looks something like this:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
display: function() {
$(this.buttonset.el).html(_.template($("#buttonset-view").html(), {"viewType": viewType}));
}
})
Basically, I'm saying to set the html of my region, which is this.buttonset.el, to my template's html. As of now, my layout doesn't know any of the elements inside the region. It just contains a region which displays the elements. So there is some sort of disconnect between my layout and the elements in my region.
The correct solution, as opposed to my earlier workaround, is to simply add the following line of code at the end:
this.bindUIElements();
From Marionette Annotated Source:
This method binds the elements specified in the “ui” hash inside the
view’s code with the associated jQuery selectors.
So this final code looks like this:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
display: function() {
$(this.buttonset.el).html(_.template($("#buttonset-view").html(), {"viewType": viewType}));
this.bindUIElements();
}
})
With this, I was able to finally able to retrieve my element using this.ui.btnSave.
I need to be able to change the filebrowserUploadUrl of CKEditor when I change some details on the page, as the querystring I pass through is used by the custom upload process I've put in place.
I'm using the JQuery plugin. Here's my code:
$('#Content').ckeditor({
extraPlugins: 'autogrow',
autoGrow_maxHeight: 400,
removePlugins: 'resize'
});
$("#Content").ckeditorGet().on("instanceReady", function () {
this.on("focus", function () {
// Define browser Url from selected fields
this.config.filebrowserUploadUrl = filebrowserUploadUrl: '/my-path-to-upload-script/?ID1=' + $("ID1").val() + '&ID2=' + $("#ID2").val();
});
});
This works fine the first time, but if I come out of the dialogue and change the value of #ID1 and #ID2, it keeps the previous values. When I debug, the filebrowserUploadUrl is set correctly, but it doesn't affect the submission values. It seems the config values are cached.
Is there any way to change a config value on the fly?
Currently I don't see any possibility to change this URL on the fly without hacking.
Take a look at http://dev.ckeditor.com/browser/CKEditor/trunk/_source/plugins/filebrowser/plugin.js#L306
This element.filebrowser.url property is set once and as you can see few lines above it will be reused again. You can try to somehow find this element and reset this property, but not having deeper understanding of the code of this plugin I don't know how.
Second option would be to change this line #L284 to:
url = undefined;
However, I haven't check if this is the correct solution :) Good luck!
BTW. Feel free to fill an issue on http://dev.ckeditor.com.
I solved this by reloading the editor whenever a change occurred; I actually went through the source code for the browser plugin etc, but couldn't get any changes to work (and of course, I really didn't want to change anything for future upgrades).
function setFileBrowserUrl() {
// Remove editor instance
$("#Content").ckeditorGet().destroy();
// Recreate editor instance (needed to reset the file browser url)
createEditor();
}
function createEditor() {
$('#Content').ckeditor({
filebrowserUploadUrl: '/my-path-to-upload-script/?ID1=' + $("ID1").val() + '&ID2=' + $("#ID2").val(),
extraPlugins: 'autogrow',
autoGrow_maxHeight: 400,
removePlugins: 'resize'
});
}
Then I call setFileBrowserUrl every time the relevant elements on the page change. Not ideal, but it works for my purposes :)