I need to disable the indentation of ordered and unordered lists (<ul>, <ol>) in CKEditor (4.2 or 4.3) entirely, because i need to convert (a very limited subset of) HTML into another markup language which does not support indentation.
I've tried several approaches, but didn't have any luck:
Try 1: Remove plugin via configuration
config.removePlugins = 'indent,indentlist,indentblock';
I guess that is not working because these plugins seem to be required - you can't remove them when building a CKEditor package from the website.
When viewing CKEDITOR.plugins via the FireBug console, those plugins are still there. There doesn't even exist an own plugin folder - seems they are builtin to the core.
Try 2: Override TAB key
I created a new plugin disableTab that does entirely nothing (except return true; on execution).
The plugin is registered as a handler for keystroke of the TAB key:
config.keystrokes = [
[ 9, 'disableTab' ] // disable TAB key to avoid nesting!
];
Unfortunately, the plugin doesn't work when pressing tab on the first level of a list (<li> or <ol>). Interestingly, it works when pressing TAB in the second level of a list (ol > li > ol > li), it does not produce more nested lists below the second level. I know for sure my plugin is executed, because i inserted an alert() in my plugin for testing. At least, this is what happens in my Firefox.
But i need to disable indentation entirely, not only above level > 2.
Try 3: Block keystroke via blockedKeystrokes in editor configuration:
Doesn't work, even though it should according to the documentation:
config.blockedKeystrokes = [ 9 ];
Try 4: Remove keystroke during runtime
According to the API documentation this code should disable the keystroke, but it doesn't work for some reason:
for (instance in CKEDITOR.instances) {
var editor = CKEDITOR.instances[instance];
editor.setKeystroke(9, false);
}
Any idea how to remove the indentation of lists in CKEditor?
I don't understand why none of these approaches work. If you know why, please let me know.
Update:
Interestingly, this code greets me for almost every key event, except pressing the TAB key:
editor.on('key', function(e) { alert ("hi"); return false; });
So it seems my setup (LinuxMint 13 [Gnome 2] + Firefox 18 + CKEditor 4.2) does not fire the key event handler for the TAB key. Maybe the indent plugin uses some other event? Blur?
Update 2:
This is a Firefox (maybe linux only) issue. Several approaches work fine with Chrome or Internet Explorer.
I just checked quickly and it looks like although indentlist is required by the list plugin, if you:
0) Download CKEditor sources from http://github.com/ckeditor/ckeditor-dev
1) Remove
requires: 'indentlist',
from plugins/list/plugin.js
2) Remove
indentlist: 1,
indentblock: 1,
from dev/builder/build-config.js
3) Build release package with dev/builder/build.sh (on Windows use "Git Bash" shell)
You will find in the dev/builder/release/ckeditor folder the release version that you need.
(it's rather uncommon that a required plugin is not really required, but it's uncommon as well that one do not need indentation for lists ;-) )
try changing your code to:
editor = CKEDITOR.replace( 'element_name' );
editor.on('key', function(e) {
var key = e.data.keyCode;
if(key==9) {
return false;
}
that should work, just change 'element_name' to the textarea that you are replacing with ckeditor
The built-in indentlist plugin cancels the event bubbling when it is processed, so a standard event listener for the tab key isn't being fired. If you prioritize your event to run first, you can capture the tab key event and stop it from indenting your lists.
Ex:
editor.on('key', function (evt) {
if (editor.mode != 'wysiwyg') {
return false;
}
if (evt.data.keyCode == this.indentKey || evt.data.keyCode == 9) {
evt.cancel();
return false;
}
}, null, null, 1);
If you want to only block the indentation on numbered lists, you can add these conditionals:
editor.on('key', function (evt) {
var path = editor.elementPath();
if (editor.mode != 'wysiwyg') {
return false;
}
if (evt.data.keyCode == this.indentKey || evt.data.keyCode == 9 && path.contains('ol')) {
evt.cancel();
return false;
}
}, null, null, 1);
The event priority is outlined in the documentation here: https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_event.html#method-on
Related
I'm using the jsTree plugin in my current Angular 2 project.
All is working well, but I'm now trying to add specific behavior:
When the user is editing a node and he presses the tab key, I want to automatically add a new empty sibling node directly underneath the node he was editing, and enable edit mode on it so that he can continue typing and effectively use the tab key as a keyboard shortcut to quickly create large numbers of nodes on the same level.
I've tried adding a keyup event listener to the high level div containing jsTree:
(keyup)="onKeyUp($event)"
The onKeyUp function:
onKeyUp(e: any) {
if (e.code === 'Tab' && this.renamingNode) {
// pressed tab while renaming subitem, insert new sibling item and start editing
}
}
Finally, the (simplified) code for the jsTree edit:
let scope = this;
$(this.currentTree).jstree().create_node(selectedItem, { 'text': '', 'type': 'value' }, 'last', function callback(e: any) {
scope.renamingNode = true;
// enable renaming of node
$(scope.currentTree).jstree().edit(e, null, function callback(addedNode: any, status: boolean, cancelled: boolean) {
scope.renamingNode = false;
// code to add addedNode to database using service
}
}
This doesn't work out as intended. When debugging, I can see that every key is captured when the user is renaming a node, but the 'tab' keypress seems to be suppressed by jsTree somehow. The onKeyUp function is not triggered, instead the default jsTree behavior proceeds, renaming the node and selecting it.
I have also looked into the different jsTree.edit callback function parameters (nodeObject, status, cancelled) but none of those seem to be useful in my case.
This is not too high on my priority last, it's more a nice-to-have, but I am genuinely curious how I could implement this.. Does anyone have any ideas?
You will have to monitor tab key on the input that pops up when you start editing a node, as below. Also check out demo - Fiddle Demo
$(document).on('keydown', '.jstree-rename-input', function(e){
if (e.keyCode == '9') {
var focusedNodeId = $('#jstree').jstree()._data.core.focused,
focusedNode = $('#jstree').jstree().get_node( '#'+focusedNodeId );
newNode = $("#jstree").jstree('create_node', '#'+focusedNodeId);
// simulate timeout to ensure new node is in the DOM and is visible
// before we start editing it
setTimeout(function(){
$("#jstree").jstree('edit', newNode);
}, 0);
}
})
The problem is with the behaviour of the event "visibilitychange".
It's triggered:
- When I switch to a different tab inside the browser window.
When I click in minimize / restore buttons for the browser window.
(this is ok)
It's not triggered:
- When I switch to a different window/program using ALT+TAB.
When I switch to a different window/program clicking on taskbar.
(this SHOULD trigger, because, just like when minimizing, the window's visibility may change)
W3 Page Visibility API Documentation: http://www.w3.org/TR/page-visibility/
There is no definition of "page visibility" regarding ALT+TAB/program switching in the spec sheet. I'm guessing it has something to do in between the OS and the Browser.
TESTED IN
Browsers:
Chrome 40.0.2214.115 m / Firefox 36.0.1 / Internet Explorer 11.0.9600.17107
OS: Windows 8.1
Is there a workaround to fix this behaviour? The implementation is fairly simple, I listen to the "visibilitychange" event using jQuery, and then in its callback, I check for the value of "document.visibilityState", but the problem is that the event is not firing when expected.
$(document).on('visibilitychange', function() {
if(document.visibilityState == 'hidden') {
// page is hidden
} else {
// page is visible
}
});
This can be done without jQuery too, but the ALT+TAB and taskbar switch hide/show expected behaviour is still missing:
if(document.addEventListener){
document.addEventListener("visibilitychange", function() {
// check for page visibility
});
}
I've also tried the ifvisible.js module (https://github.com/serkanyersen/ifvisible.js) but the behaviour is the same.
ifvisible.on('blur', function() {
// page is hidden
});
ifvisible.on('focus', function() {
// page is visible
});
I haven't tested in other browsers because if I can't make it work in Chrome on Windows I really don't care about the other browsers yet.
Any help or suggestions?
UPDATE
I tried using different vendor prefixes for the event name (visibilitychange, webkitvisibilitychange, mozvisibilitychange, msvisibilitychange) but but still the event is not triggered when I switch to a different program in the taskbar or ALT+TAB, or even if I open the start menu thing in windows with the windows key, which covers the whole screen.
I can reproduce the exact same issue in Chrome, Firefox and Internet Explorer.
UPDATE #2
Here's a roundup post I wrote for this issue and a workaround in pure Javascript to solve the encountered problems.
UPDATE #3
Edited to include a copy of the sourced blog post. (see accepted answer)
Here's a roundup post I wrote for this issue and a workaround in pure JavaScript to solve the encountered problems.
Edited to include a copy of the sourced blog post:
In any kind of javascript application we develop there may be a
feature or any change in the application which reacts according to the
current user visibility state, this could be to pause a playing video
when the user ALT+TABs to a different window, tracking stats about how
the users interact with our application, how often does him switch to
a different tab, how long does it take him to return and a lot of
performance improvements that can benefit from this kind of API.
The Page Visibility API provides us with two top-level attributes:
document.hidden (boolean) and document.visibilityState (which could be
any of these strings: “hidden”, “visible”, “prerender”, “unloaded”).
This would not be not good enough without an event we could listen to
though, that’s why the API also provides the useful visibilitychange
event.
So, here’s a basic example on how we could take action on a visibility
change:
function handleVisibilityChange() {
if(document.hidden) {
// the page is hidden
} else {
// the page is visible
}
}
document.addEventListener("visibilitychange", handleVisibilityChange, false);
We could also check for document.visibilityState value.
Dealing with vendor issues George Berkeley by John Smibert
Some of the implementations on some browsers still need that the
attributes or even the event name is vendor-prefixed, this means we
may need to listen to the msvisibilitychange event or check for the
document.webkitHidden or the document.mozHidden attributes. In order
to do so, we should check if any vendor-prefixed attribute is set, and
once we know which one is the one used in the current browser (only if
there’s the need for a prefix), we can name the event and attributes
properly.
Here’s an example approach on how to handle these prefixes:
var browserPrefixes = ['moz', 'ms', 'o', 'webkit'];
// get the correct attribute name
function getHiddenPropertyName(prefix) {
return (prefix ? prefix + 'Hidden' : 'hidden');
}
// get the correct event name
function getVisibilityEvent(prefix) {
return (prefix ? prefix : '') + 'visibilitychange';
}
// get current browser vendor prefix
function getBrowserPrefix() {
for (var i = 0; i < browserPrefixes.length; i++) {
if(getHiddenPropertyName(browserPrefixes[i]) in document) {
// return vendor prefix
return browserPrefixes[i];
}
}
// no vendor prefix needed
return null;
}
// bind and handle events
var browserPrefix = getBrowserPrefix();
function handleVisibilityChange() {
if(document[getHiddenPropertyName(browserPrefix )]) {
// the page is hidden
console.log('hidden');
} else {
// the page is visible
console.log('visible');
}
}
document.addEventListener(getVisibilityEvent(browserPrefix), handleVisibilityChange, false);
Other issues There is a challenging issue around the “Page Visibility”
definition: how to determine if the application is visible or not if
the window focus is lost for another window, but not the actual
visibility on the screen? what about different kinds of visibility
lost, like ALT+TAB, WIN/MAC key (start menu / dash), taskbar/dock
actions, WIN+L (lock screen), window minimize, window close, tab
switching. What about the behaviour on mobile devices?
There’s lots of ways in which we may lose or gain visibility and a lot
of possible interactions between the browser and the OS, therefore I
don’t think there’s a proper and complete “visible page” definition in
the W3C spec. This is the definition we get for the document.hidden
attribute:
HIDDEN ATTRIBUTE On getting, the hidden attribute MUST return true if
the Document contained by the top level browsing context (root window
in the browser’s viewport) [HTML5] is not visible at all. The
attribute MUST return false if the Document contained by the top level
browsing context is at least partially visible on at least one screen.
If the defaultView of the Document is null, on getting, the hidden
attribute MUST return true.
To accommodate accessibility tools that are typically full screen but
still show a view of the page, when applicable, this attribute MAY
return false when the User Agent is not minimized but is fully
obscured by other applications.
I’ve found several inconsistencies on when the event is actually
fired, for example (Chrome 41.0.2272.101 m, on Windows 8.1) the event
is not fired when I ALT+TAB to a different window/program nor when I
ALT+TAB again to return, but it IS fired if I CTRL+TAB and then
CTRL+SHIFT+TAB to switch between browser tabs. It’s also fired when I
click on the minimize button, but it’s not fired if the window is not
maximized and I click my editor window which is behing the browser
window. So the behaviour of this API and it’s different
implementations are still obscure.
A workaround for this, is to compensate taking advantage of the better
implemented focus and blur events, and making a custom approach to the
whole “Page Visibility” issue using an internal flag to prevent
multiple executions, this is what I’ve come up with:
var browserPrefixes = ['moz', 'ms', 'o', 'webkit'],
isVisible = true; // internal flag, defaults to true
// get the correct attribute name
function getHiddenPropertyName(prefix) {
return (prefix ? prefix + 'Hidden' : 'hidden');
}
// get the correct event name
function getVisibilityEvent(prefix) {
return (prefix ? prefix : '') + 'visibilitychange';
}
// get current browser vendor prefix
function getBrowserPrefix() {
for (var i = 0; i < browserPrefixes.length; i++) {
if(getHiddenPropertyName(browserPrefixes[i]) in document) {
// return vendor prefix
return browserPrefixes[i];
}
}
// no vendor prefix needed
return null;
}
// bind and handle events
var browserPrefix = getBrowserPrefix(),
hiddenPropertyName = getHiddenPropertyName(browserPrefix),
visibilityEventName = getVisibilityEvent(browserPrefix);
function onVisible() {
// prevent double execution
if(isVisible) {
return;
}
// change flag value
isVisible = true;
console.log('visible}
function onHidden() {
// prevent double execution
if(!isVisible) {
return;
}
// change flag value
isVisible = false;
console.log('hidden}
function handleVisibilityChange(forcedFlag) {
// forcedFlag is a boolean when this event handler is triggered by a
// focus or blur eventotherwise it's an Event object
if(typeof forcedFlag === "boolean") {
if(forcedFlag) {
return onVisible();
}
return onHidden();
}
if(document[hiddenPropertyName]) {
return onHidden();
}
return onVisible();
}
document.addEventListener(visibilityEventName, handleVisibilityChange, false);
// extra event listeners for better behaviour
document.addEventListener('focus', function() {
handleVisibilityChange(true);
}, false);
document.addEventListener('blur', function() {
handleVisibilityChange(false);
}, false);
window.addEventListener('focus', function() {
handleVisibilityChange(true);
}, false);
window.addEventListener('blur', function() {
handleVisibilityChange(false);
}, false);
I welcome any feedback on this workaround. Some other great sources
for ideas on this subject:
Using the Page Visibility API Using PC Hardware more efficiently in
HTML5: New Web Performance APIs, Part 2 Introduction to the Page
Visibility API Conclusion The technologies of the web are continuously
evolving, we’re still recovering from a dark past where tables where
the markup king, where semantics didn’t mattered, and they weren’t any
standards around how a browser should render a page.
It’s important we push these new standards forward, but sometimes our
development requirements make us still need to adapt to these kind of
transitions, by handling vendor prefixes, testing in different
browsers and differents OSs or depend on third-party tools to properly
identify this differences.
We can only hope for a future where the W3C specifications are
strictly revised, strictly implemented by the browser developer teams,
and maybe one day we will have a common standard for all of us to work
with.
As for the Page Visibility API let’s just kinda cite George Berkeley
and say that:
“being visible” is being perceived.
A working solution is proposed described here: https://stackoverflow.com/a/9502074/698168. It uses a combination of the W3C Page Visibility API, blur/focus and mouse movements. Hidden HTML pages related to Alt+Tab are identified in a probabilistic way (i.e. you cannot determine if your page is hidden with 100% accuracy).
we can do like below when switching between tabs and switching between applications
var pageVisible = true;
function handleVisibilityChange() {
if (document.hidden) {
pageVisible = false;
} else {
pageVisible = true;
}
console.log("handleVisibilityChange")
console.log("pageVisible", pageVisible)
// some function call
}
document.addEventListener("visibilitychange", handleVisibilityChange, false);
window.addEventListener('focus', function() {
pageVisible = true;
// some function call
}, false);
window.addEventListener('blur', function() {
pageVisible = false;
// some function call
}, false);
There's a very simple solution to this I have come across.
You just need to pass false to the useCapture while attaching an event listener to the document. Works like a charm!
document.addEventListener('visibilitychange', function () {
// code goes here
}, false)
I am writing tests for an AngularJS directive which fires events of a <textarea> when certain keys are pressed. It all works fine per my manual testing. I want to be good and have a full unit-test suite too, but I have run into a problem I can't solve on my own:
I want to send a specific keyCode in my triggerHandler() call in my test, but I can't find a way to specify the key that actually works. I am aware of a lot of questions and answers on the topic of building and sending events with specific data, but none of them work on my setup:
My setup
Karma test runner
PhantomJS browser running the tests (but also tried Firefox and Chrome without success)
I'm not using jQuery and I'm hoping there is a regular JS solution. There must be!
Test code
var event = document.createEvent("Events");
event.initEvent("keydown", true, true);
event.keyCode = 40; // in debugging the test in Firefox, the event object can be seen to have no "keyCode" property even after this step
textarea.triggerHandler(event); // my keydown handler does not fire
The strange thing is, I can type the first 3 lines into the console in Chrome and see that the event is being created with the keyCode property set to 40.
So it seems like it should work.
Also, when I call the last line like this textarea.triggerHandler("keydown"); it works and the event handler is triggered. However, there is no keyCode to work with, so it is pointless.
I suspect it may be something to do with the nature of the test running against a DOM that is different to a regular page running in the browser. But I can't figure it out!
I've used the following solution to test it and having it working in Chrome, FF, PhantomJS and IE9+ based on this SO answer.
It doesn't work in Safari - tried millions of other solution without any success...
function jsKeydown(code){
var oEvent = document.createEvent('KeyboardEvent');
// Chromium Hack: filter this otherwise Safari will complain
if( navigator.userAgent.toLowerCase().indexOf('chrome') > -1 ){
Object.defineProperty(oEvent, 'keyCode', {
get : function() {
return this.keyCodeVal;
}
});
Object.defineProperty(oEvent, 'which', {
get : function() {
return this.keyCodeVal;
}
});
}
if (oEvent.initKeyboardEvent) {
oEvent.initKeyboardEvent("keydown", true, true, document.defaultView, false, false, false, false, code, code);
} else {
oEvent.initKeyEvent("keydown", true, true, document.defaultView, false, false, false, false, code, 0);
}
oEvent.keyCodeVal = code;
if (oEvent.keyCode !== code) {
console.log("keyCode mismatch " + oEvent.keyCode + "(" + oEvent.which + ") -> "+ code);
}
document.getElementById("idToUseHere").dispatchEvent(oEvent);
}
// press DEL key
jsKeydown(46);
Hope it helps
Update
Today I've found and tested this solution which is offers a much wider coverage of browsers (enabling the legacy support):
https://gist.github.com/termi/4654819
All the credit goes to the author of this GIST.
The code does support Safari, PhantomJS and IE9 - tested for the first 2.
Adding to #MarcoL answer, I'd like to point out for future readers who might stumble on this question, that the methods initKeyboardEvent and initKeyEvent are deprecated methods, and should no longer be used. See here and here.
Instead as the MDN docs suggested, events should be created via their respective constructor.
I'm writing a user script for a complex web app. The existing code is catching the 'j' and 'k' keydown events.
I'd like to be able to find this function to see what it's doing. Is there a way to list all the key event handlers in a document? Or maybe a way to set a breakpoint somehow in Chrome Developer Tools for when I press the letter?
Yes, in the developer tools, go to the Scripts tab, select the page, go to Event Listener Breakpoints, Keyboard, keydown.
Though this might not necessarily help you much, e.g. if the script is minified or they use a library. But you can give it a try.
If you can get a piece of your script to run first and if the keys are handled at the document level, you can install this intercept to see what part of the code is setting the keyboard handler:
var oldListener = document.addEventListener;
document.addEventListener = function(type, listener, capture) {
if (type == "keydown" || type == "keyup" || type == "keypress") {
console.log("type=" + type + " listener=" + listener.toString().slice(0, 80));
}
return (oldListener.apply(this, arguments));
}
SendKeys is method to sending keystroke to an application.
Can I do it in Javascript, to send keystroke in browser ?
REF :
http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.aspx
If you would be able to send keystrokes on the OS Level, this would be a big security issue.
You could (for instance) install any kind of software on the client machine if you where able to send keystrokes to the needed install dialogs.
Yes, you could come up with an active-x control or some other tools to be installed on the client machine. But because of the security issues with such a tool, I wouldn't do it -- even in a controlled environment.
Most of the time, there is a way to achieve your needed functionality without breaching the security.
Update: If you want to jump to the next tabfield, you have to use the focus() method to set the focus to the next element. Unfortunately, you have to find the next element by yourself in javascript, but this should not be a big problem - you can keep an ordered list of all your elements in javascript.
btw: http://forums.devarticles.com/javascript-development-22/moving-to-next-tabindex-on-event-2382.html
There are lots of JS Framework implemented event simulation inside web page.
Is it possible to simulate key press events programmatically? for jQuery
Javascript: simulate a click on a link for YUI
However, simpler method is that the third post of the link given by Ralf which focus the "next" textfield regarding to the tabIndex property of elements inside a form element.
There might be a more brilliant way if you make up a list of textfield's IDs and the order you want to be.
Of course, the tabIndex list might not be generated by yourself but by walking around the textfield.
Create a loop to generate the list when document is loaded (DOMContentLoaded):
var tabIndexList = new Array();
function tabIndexListGeneration(){
var form = document.getElementById("Your form ID"), // remember to fill in your form ID
textfields = form.getElementsByTagName("input"),
textfieldsLength = textfields.length;
for(var i=0;i<textfieldsLength;i++){
if(textfields[i].getAttribute("type") !== "text" || textfields[i].getAttribute("tabIndex") <= 0)continue;
/* tabIndex = 0 is neglected as it places the latest, if you want it, change 0 to -1
* and change tabIndexPointer = 0 into tabIndexPointer = -1 below */
tabIndexList[textfields[i].getAttribute("tabIndex")] = textfields[i];
}
}
// You can use the function of JS Framework if you don't like the method below.
if(document.addEventListener){
document.addEventListener("DOMContentLoaded", tabIndexListGeneration, false);
}else{
window.attachEvent("onload", tabIndexListGeneration);
}
And inside the event of "text input equals textfield maxlength":
var tabIndexPointer = target.getAttribute("tabIndex"); // target is the DOM object of current textfield
while(!(++tabIndexPointer in tabIndexList)){
if(tabIndexPointer >= tabIndexList.length)
tabIndexPointer = 0; // or other action after all textfields were focused
}
tabIndexList[tabIndexPointer].focus(); // if other action needed, put it right after while ended
Note: form textfields' structure must not be mutated otherwise an error would be given out.
If textfield generate dynamically, run tabIndexListGeneration() to regenerate the list.
This works for me. The ActiveXObject needs to be opened in IE.
var PaintProg = new ActiveXObject("WScript.Shell"); //paste to mspaint
PaintProg.Run("mspaint.exe \\\\srv4\\photos\\image1.jpg",9,false);
var PaintTimer = window.setInterval(PaintPaste,1000);
function PaintPaste()
{
if (PaintProg.AppActivate("Paint",true) == true)
{
PaintProg.SendKeys('"^(v)""%(F)""x""~"',true);
window.clearInterval(PaintTimer);
}
}
Not by default in most browsers, no. You may be able to get it to work using ActiveX if it's going to be running in Internet Explorer, however.