Ckeditor - Add multiple child elements to list element - html

In CKeditor is it possible to add multiple child elements to a list element?
E.g.
- Heading
Paragraph
- Heading2
Paragraph2
Backstory-
I've created a style that lets the user style up an order list. Part of this style is to have a heading and a paragrpah within each list item element.
Problem-
When the adding the heading element and pressing enter, to be able to add the paragraph, a new list item is added instead. If the shift+enter/shift+enter button combination is pressed, a new list is created without creating a new list item, however the cursor remains within the heading element, and so no paragraph can be added this way.
Is there a way around this?

I've found a way to fix the problem by overriding the key press event in CKEditor. When a key is pressed, it checks that:
It was the Enter key that was pressed;
The cursor is within a list;
The node the cursor is in isn't empty;
The cursor is at the end of the node.
If so it
Adds a paragraph after the current node.
Moves the cursor into it.
The code:
CKEDITOR.on(
'instanceReady',
function(ev) {
var editorInstance = CKEDITOR.instances[ev.editor.name];
var editorWindow = editorInstance.window.$;
var editorDocument = $editor_instance.document.$;
var editorBody = editorDocument.body;
/***
* Fix the editor not letting you add sub elements to lists
*/
editorInstance.on( 'key', function( event ) {
// Get the HTML event
var e = event.data;
// If the enter key was pressed
if(e.keyCode == 13) {
// Find which element the cursor is in
var selection = editorWindow.getSelection();
var cursorElem = $(selection.anchorNode);
// Only override the default behaviour if we're in a list element, and the user has typed something
if(cursorElem.closest('li').length && cursorElem.text().trim().length) {
// get the current cursor position
var range = editorWindow.getSelection().getRangeAt(0)
// create range to find how many characters are after the cursor
var postRange = editorDocument.createRange();
postRange.selectNodeContents(cursorElem[0]);
postRange.setStart(range.endContainer, range.endOffset);
var nextText = postRange.cloneContents();
var isAtEnd = nextText.textContent.length === 0;
// Only override if we're at the end of the list element
if(isAtEnd) {
// cancel the default event
event.cancel();
// get the element to add the new paragraph after
var after = cursorElem;
// if the cursor is in a TEXT not instead of an actual HTML node then get the parent HTML code
if(cursorElem[0].nodeType == Node.TEXT_NODE) {
after = cursorElem.parent();
}
// Add the new paragraph
after.after('<p></p>');
var newParagraph = after.next();
// set selection to the new paragraph
var range = editorDocument.createRange();
range.setStart(newParagraph[0], 0);
selection.removeAllRanges();
selection.addRange(range);
}
}
}
});
}
);
CSS needed to make the new paragraph display:
p {
min-height: 1em;
}
P.s. Thanks for this answer showing how to check if the cursor was at the end of an element Contenteditable DIV - how can I determine if the cursor is at the start or end of the content

Related

Wrong code in tutorial for event listeners

I am following this tutorial to build a store locator page with a Mapbox map.
I don't want to add custom markers because I already have custom map labels (symbols?), which means I don't need the optional last section of the tutorial and stop right after Add Event Listeners.
Once this is completed, the page should react to clicks in the side panel list, as well as on the map (2 event listeners). However, in the demo provided in the tutorial for that particular step, you can tell the code for the second event listener, the one making the map clickable, is not functioning, which makes me believe there is a mistake in the provided code:
// Add an event listener for when a user clicks on the map
map.on('click', function(e) {
// Query all the rendered points in the view
var features = map.queryRenderedFeatures(e.point, { layers: ['locations'] });
if (features.length) {
var clickedPoint = features[0];
// 1. Fly to the point
flyToStore(clickedPoint);
// 2. Close all other popups and display popup for clicked store
createPopUp(clickedPoint);
// 3. Highlight listing in sidebar (and remove highlight for all other listings)
var activeItem = document.getElementsByClassName('active');
if (activeItem[0]) {
activeItem[0].classList.remove('active');
}
// Find the index of the store.features that corresponds to the clickedPoint that fired the event listener
var selectedFeature = clickedPoint.properties.address;
for (var i = 0; i < stores.features.length; i++) {
if (stores.features[i].properties.address === selectedFeature) {
selectedFeatureIndex = i;
}
}
// Select the correct list item using the found index and add the active class
var listing = document.getElementById('listing-' + selectedFeatureIndex);
listing.classList.add('active');
}
});
Would anyone be able to tell what is wrong with this code?
Turns out the code is incomplete in that the cursor doesn't change to a pointer as you hover over a map label/marker so it doesn't clue you into realising you can click on it, hence my assumption it wasn't working at all. I assume the general users who would then face the map would be equally deceived unless the pointer shows up. So in the tutorial, if you do go ahead and click the marker, it will have the expected behaviour and display the popup, although no pointer is shown.
Here is how to create the pointer, based on this fiddle: https://jsfiddle.net/Twalsh88/5j70wm8n/25/
map.on('mouseenter', 'locations', function(e) {
// Change the cursor style as a UI indicator.
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'locations', function() {
map.getCanvas().style.cursor = '';
});

knowing the location of where a user has clicked

I am looking at creating a webpage with user feedback where the user can click within a certain element of the page, which brings up a comment box for the user to enter details, if the user left a note a post-it note would be left where they clicked. this note indication has to move when the user scrolls so that the note does not move away from the element it was left on.
Is this possible? I have been trying to search this on google but I only seem to get how to disable right click.
If it is possible where could I find the relevant information.
Cheers
To get via JS the coordinates where a user has clicked:
(function() {
window.onmousedown = handleMouseMove;
function handleMouseMove(event) {
event = event || window.event; // IE-ism
console.log(event.clientX);
console.log(event.clientY);
}
})();
Here you have an example of how to move a DIV where user clicks:
(function() {
window.onmousedown = handleMouseMove;
function handleMouseMove(event) {
event = event || window.event; // IE-ism
console.log(event.clientX);
moveDiv(event.clientX,event.clientY);
}
})();
function moveDiv(x_pos,y_pos){
var d = document.getElementById('myDiv');
d.style.left = x_pos + "px";
d.style.top = y_pos + "px";
}
Example
First, get mouse click coordinates: getting the X/Y coordinates of a mouse click on an image with jQuery
Than put element with 'position: absolute' at specified location.

Actionscript get values of controls in multiple HGroup

I have a flex mobile app which based upon a list of questions generates a set of controls depending on what kind of response is required, this is the input form.
The structure is
Group
-----Hgroup
-----------Question1, CheckBox, TextInput
-----Hgroup
-----------Question2, CheckBox, TextInput
-----Hgroup
-----------Question3, CheckBox, TextInput
-----Hgroup
-----------Question4, CheckBox, TextInput
end group
How do I loop through the group in Actionscript, into the HGroup and return the value of, in this example, the checkbox and textInput?
I have numerous example of how to determine the control type in the container, just not how to retrieve the values.
Thanks in Advance
To loop and type check use:
for (var i:int; i < hGroup.numElements; i++) {
var child:DisplayObject = hGroup.getElementAt(i);
if (child is TextInput) {
var textInput:TextInput = child as TextInput;
// do your stuff
}
else if (child is CheckBox) {
var checkBox:CheckBox = child as CheckBox;
// do your stuff
}
}

How can I add an event listener to scrolling content?

I want to put some mouse event to scrolling text in dynamic text field.
For example: I type several sentences into one dynamic text field. For each sentence, I want to have a different mouse event. Like this:
sentenceA.addEventListener(MouseEvent.CLICK, functionA);
function functionA(evt:MouseEvent):void
{
trace("bla");
}
How can I add an eventlistener to each sentence in the scrolling text? Because the position of the mouse event should move when the relative text scrolling.
here you go...
_txt.htmlText = "<a href='event:sentence1'>This is one sentenc.</a> <a href='event:sentence2'>This is another sentence.</a>"
_txt.addEventListener(TextEvent.LINK, clickCopyHandler);
function clickCopyHandler(e:TextEvent):void {
trace(e.text);
}
var sentences:Array = [ sentenceA, sentenceB, ...... ];
function clickAllText(e:MouseEvent):void
{
var index:int;
for each(var s:String in sentences)
{
index = dynamicTextField.text.indexOf(s);
if(index >= 0 && index <= 3)
{
//if the sentence is in the front
this["sentence"+sentences.indexOf(s)]();
break;
}
}
}
function sentence0():void
{
}
function sentence1():void
{
}
If that is not the case, you would need to either use different text fields, or
get the proximity of the mouse point to the sentence x and y location on the
display.
Take a closer look at flash.event.TextEvent.LINK, it's thrown when you click on html text which has an attribute href='event:someText'. Consider the following example
var tf:TextField = new TextField();
tf.htmlText = "<a href='event:first'>sentence1.</a><a href='event:second'>sentence2.</a>";
tf.addEventListener("link", clickHandler); //link is value of flash.event.TextEvent.LINK
function clickHandler(e:TextEvent):void {
//here should be something like the following
if (e.text == "first") sentence1();
else if (e.text == "second") sentence2();
}
well, I guess you caught the idea! The problem is that sentences are to be put into tag, but you can make links look like ordinary text. If that variant does not fits your needs, you may try to compare selection boundaries(tf.selectionBeginIndex and tf.selectionEndIndex) with boundaries of sentences after click.

Is there a way to keep an object always at the top of the display list?

Is there a way to make a display object always be at the top of the display list?
For example, I know you can set the childIndex, i.e:
setChildIndex(myDisplayObject, numChildren-1);
But is there a way that an object has sensed that something else has been added to the display list and restack themselves accordingly?
You can listen to the Event.ADDED event on the container. This event will bubble up, so you'll get called whenever a display object is added to the container or any of its children.
Here's an example of how you could do this. You'll see the black box always stays on top.
var container:DisplayObjectContainer = this; // this is a frame script but just change this line as necessary
container.addEventListener(Event.ADDED,handleAdded,true);
function handleAdded(e:Event):void {
// this if tries to cover some edge cases, unlikely to happen in your code, from your description
if(container == topElement.parent && container.numChildren > 0 && container.getChildIndex(topElement) != container.numChildren - 1) {
container.setChildIndex(topElement,numChildren - 1);
}
}
function getSprite(c:uint):Sprite {
var sp:Sprite = new Sprite();
sp.graphics.beginFill(c);
sp.graphics.drawRect(0,0,100,100);
sp.graphics.endFill();
return sp;
}
var topElement:Sprite = getSprite(0);
container.addChild(topElement);
var sp:Sprite = getSprite(0xff0000);
container.addChild(sp);
sp.addChild(getSprite(0xff00));
var sp2:Sprite = getSprite(0xff);
container.addChild(sp2);
However, I think it's much simpler and cleaner just to have 2 containers, say, top and bottom, kind of like layers. In top you'd add the element that always must be on top (or this could be your element as you probably don't need to have this extra container). In bottom you'd add and remove whatever you want. Then you can forget about manually restacking stuff (at least to keep the top element atop).
You can override the addChild() method in the parent object. In that method you can move your child to the top using
setChildIndex(myDisplayObject, numChildren-1);
In this way everytime an object is added to the parent, the parent moves your object to the top.