CKEditor select as a single element - html

I'd like to have an element in a CKEditor editable div be treated like an input for the purposes of mouse selection. The element is a span but I want to make it so that a user cannot select part of its content along with content outside of it. For instance, if it has the following DOM:
Foo <span>gobbledigook</span> bar
the user should be able to select within the span, within "Foo" or "bar", or starting from "Foo" and extending into "bar", but no select from "Foo" into the span content. For these purposes, <span> should be treated like an <input /> (see this jsbin on how that works).
The span is actually using CKEditor's widget plugin, which appears to satisfy the other direction (that is, you can select within the span, but dragging from within the span to outside the span doesn't extend the selection outside the span). It's just the outside -> inside that's not working.

I have a workaround for this:
editor.widgets.on("checkSelection", function(event) {
var range = editor.getSelection().getRanges()[0];
var start = range.startContainer;
var end = range.endContainer;
// widgetRepository.getByElement() chokes if the argument is a text node
if (start.type === CKEditor.NODE_TEXT) {
start = start.getParent();
}
if (end.type === CKEditor.NODE_TEXT) {
end = end.getParent();
}
var endWidget = editor.widgets.getByElement(end);
if (
// We only worry about selecting from outside of a widget to the inside of one
endWidget
// Selection ends inside a widget and the start is not in a widget *or* is within a different widget
&& editor.widgets.getByElement(start) !== endWidget
) {
range.setEndBefore(endWidget.wrapper);
range.select();
}
});
Basically, if I detect that a selection is ending within a widget that it didn't start in, I move the end to right before the widget wrapper.

Related

Having trouble with jquery dynamic input and regex matching. Partially works

I have a dynamic input field where a user can add as many colors as he wants using an "Add" button.
The array works fine. Posts fine. My issue is with a some regex matching. Basically if a user enters one of the colors in the regex pattern a div container with another input shows. This works fine.
The issue:
User enters "purple" - no match, nothing shows. Good.
User enters "blue" - match, div shows. user deletes "blue" div disappears. Good.
User enters "red" - match, div appears. Good.
User enters "yellow" - no match, div disappears. Not Good.
Once the match has occurred I need the div to stay visible. What's happening though is it's removing the div if the next input is not a match.
$("#add").click(function(e){
$('input[name="item_color[]"]').keyup(function() {
var data = $(this).val();
var regx = /(blue|red|orange)/gmi;
if (data.match(regx)){
$("#divcolor").show();
}
else {
$("#divcolor").hide();
}
});
});
I've tried removing the
$("#divcolor").hide();
which somewhat works. except if the user goes back through the inputs and deletes the match that caused the div to show initially the div continues to show.
Basically I just need it to show the div if any match occurs in any of the inputs and hide the div if no matches occur. I really need the div to show/hide on keyup is the biggest thing.
Any help would be appreciated. I'm sure its something easy. I just can't wrap my mind around the logic.
You could solve the issue by checking all inputs each time you call the handler and set a var to true, if there is a match:
let matches = false;
$('input[name="item_color[]"]').each(function() {
if ($(this).val().match(regx)){
matches = true;
}
});
Furthermore the issue, that you mentioned in the comments, that the event handler isn't working when you define it outside the add handler, is related to the fact, that the inputs aren't created at the time of the definition. To prevent it you could attach the event listener directly to the body but add a selector to it:
$('body').on('keyup', 'input[name="item_color[]"]', function() {
Then the event listener is attached to an element that exists and the selector is used not before the listener is called.
Working example:
$("#add").click(function(){
$('#input-wrapper').append('<input name="item_color[]">');
});
$('body').on('keyup', 'input[name="item_color[]"]', function() {
let matches = false;
var regx = /(blue|red|orange)/gmi;
$('input[name="item_color[]"]').each(function() {
if ($(this).val().match(regx)){
matches = true;
}
});
if (matches){
$("#divcolor").show();
}
else {
$("#divcolor").hide();
}
});
#divcolor {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="add">Add</button>
<div id="input-wrapper">
<input name="item_color[]">
</div>
<div id="divcolor">
<p>div visible</p>
</div>

How do I get a Google Apps Script Document Outline from the API?

I am able to get the contents of a documents outline by searching all the paragraphs and seeing the heading each paragraph is using. This works but isn't ideal. If I manually remove an item from the outline, it doesn't change the heading of that item to normal. Therefore when I run my code again it'll detect the removed item as still being part of the outline since it's heading matches one used for the outline.
var searchHeading = DocumentApp.ParagraphHeading.HEADING4;
var paragraphs = getParagraphs();
return paragraphs.filter(function(paragraph) {
return (paragraph.header == searchHeading);
});
function getParagraphs() {
var paragraphs = DocumentApp.getActiveDocument().getBody().getParagraphs();
return paragraphs.map(function(p, index) {
return {
paragraph: p.getText(),
header: p.getHeading(),
id: index
};
});
}
Is there anyway to get the contents of the outline without parsing each paragraph and filtering those whose header matches a specific heading, ideally from an API call?
EDIT: Added getParagraphs()

Google App Script Page break at cursor?

I have a button in my sidebar of which's purpose is to insert a page break where the cursor is. I tried scripting it to just insert a page break (not at the cursor but just in the body) and it works fine.
However, I can't seem to get it to work at the cursor:
function pageBreak() {
var cursor = DocumentApp.getActiveDocument().getOffset();
insertPageBreak(cursor);
}
How can I accomplish this?
You need to know the element type at the cursor position. Not all element types allow a page break to be inserted.
Begin by getting the element at the cursor position, then work from there.
function insertPgBrk() {
var theCursor = DocumentApp.getActiveDocument().getCursor();
var theElement = theCursor.getElement();
var elementTypeAtCursor = theElement.getType();
Logger.log('elementTypeAtCursor: ' + elementTypeAtCursor);
if (elementTypeAtCursor === DocumentApp.ElementType.PARAGRAPH) {
Logger.log('its the right type');
theElement.insertPageBreak(0);
};
};
You can't insert a page break in a TEXT element. The element needs to be a PARAGRAPH or a LISTITEM.

Drop down input in IE8

How can I make the drop down show all the content of one option when it is expanded? If an option in the drop down is, for instance, a whole sentence and select tag width is small, the user in IE will not be able to read whole option. This is not the case in Mozilla where the whole content is shown when drop down is expanded.
Is there any way to avoid this behavior in IE8,
Thanks
I had a similar constraint when working against IE8 and the oh so famous drop down list truncating. I have multiple drop down lists on my page, one after another, some inside top nav content, and IE8 decides to cut off my attribute option text properties. Now, like many of us, I don't want to set the width obscurely large, so this option is out of question.
After a lot of research, I couldn't find a great answer, so I went ahead and fixed it with jQuery and CSS:
First, let's make sure we are only passing our function in IE8:
var isIE8 = $.browser.version.substring(0, 2) === "8.";
if (isIE8) {
//fix me code
}
Then, to allow the select to expand outside of the content area, let's wrap our drop down lists in div's with the correct structure, if not already, and then call the helper function:
var isIE8 = $.browser.version.substring(0, 2) === "8.";
if (isIE8) {
$('select').wrap('<div class="wrapper" style="position:relative; display: inline-block; float: left;"></div>').css('position', 'absolute');
//helper function for fix
ddlFix();
}
Now onto the events. Since IE8 throws an event after focusing in for whatever reason, IE will close the widget after rendering when trying to expand. The work around will be to bind to 'focusin' and 'focusout' a class that will auto expand based on the longest option text. Then, to ensure a constant min-width that doesn't shrink past the default value, we can obtain the current select list width, and set it to the drop down list min-width property on the 'onchange' binding:
function ddlFix() {
var minWidth;
$('select')
.each(function () {
minWidth = $(this).width();
$(this).css('min-width', minWidth);
})
.bind('focusin', function () {
$(this).addClass('expand');
})
.change(function () {
$(this).css('width', minWidth);
})
.bind('focusout', function () {
$(this).removeClass('expand');
});
}
Lastly, make sure to add this class in the style sheet:
select:focus, select.expand {
width: auto;
}

How can I stop Flash from changing indent when user Clicks on hyperlink in TextField?

I have a TextField which I initialize by setting htmlText.
The text has anchor tags (hyperlinks).
When a user clicks on the hyperlink, the indentation of the second and subsequent lines in the paragraph changes. Why? How do I stop it?
My html has an image at the beginning of the line, followed by the tag, followed by more text. To style the hyper links to look blue always and underlined when the mouse is over them, I do this:
var css:StyleSheet = new StyleSheet();
css.parseCSS("a {color: #0000FF;} a:hover {text-decoration: underline;}");
stepText.styleSheet = css;
stepText.htmlText = textToUse;
stepText.visible = true;
Here is a fragment of the html text (with newlines and exrta whitespace added to improve readability - originally it was one long line):
<textformat indent="-37" blockindent="37" >
<img src="media/interface/level-1-bullets/solid-circle.png"
align="left"
hspace="8"
vspace="1"/>
American Dental Association. (n.d.). <i>Cleaning your teeth and gums (oral hygiene)</i>.
Retrieved 11/24/08, from
<a href="http://www.ada.org/public/topics/cleaning_faq.asp"
target="_blank">http://www.ada.org/public/topics/cleaning_faq.asp
</a>
</textformat>
<br/>
As it turns out, the text field is of a width such that it wraps and the second line starts with "Retrieved 11/24/08". Clicking on the hyper link causes this particular line to be indented. Subsequent paragraphs are not affected.
ASIDE: The image is a list bullet about 37 pixels wide. (I used images instead of li tags because Flash does not allow nested lists, so I faked it using a series of images with varying amounts of whitespace to simulate three levels of indentation.)
IDEA: I was thinking of changing all hyperlinks to use "event:" as the URL protocol, which causes a TextEvent.LINK event to be triggered instead of following the link. Then I would have to open the browser in a second call. I could use this event handler to set the html text to itself, which might clear the problem. (When I switch pages in my application and then come back to the page, everything is OKAY again.)
PROBLEM: If I use the "event:" protocol and user tries the right-mouse button click, they will get an error, or so I am told. (See http://www.blog.lessrain.com/as3-texteventlink-and-contextmenu-incompatibilities/ ) I do not like this trade-off.
I found a workaround, along the lines of my IDEA above.
1) Prefix all hyper-links with "event:".
2) Add an event listener on the control to process the request.
3) The listener launches the browser AND sets the text to empty string then its original value.
1) Prefixing with event:.
public static function hrefEvents(s:String):String {
var hrefRegex:RegExp = /href="/gm;
var output:String = s.replace(hrefRegex, "href=\"event:");
var dupe:RegExp = /event:event:/gm;
output = output.replace(dupe, "event:");
return output;
}
2) Add event listener
stepText.addEventListener(TextEvent.LINK, hyperlinkHandler);
3) Handle the LINK event.
private function hyperlinkHandler(e:TextEvent) {
var temp:String = stepText.htmlText;
stepText.htmlText = "";
stepText.htmlText = temp;
var url:String = e.text;
trace("Clicked on link to URL: " + url);
navigateToURL(new URLRequest(url), "_blank");
}
If I don't set the field to empty string first it does not think anything changed, so it does nothing.