App Script Google Docs Extension Selects Too Much Text - javascript

The intention is for the extension to grab the selected text and pop it into a card search engine, then return the resulting webpage as a hyperlink attached to the selected text. The first half of that works fine however the printing seems to select the entirety of the paragraph instead of just the intended selected area. Is there a fix to this?
function websiteCall() {
const hostText = getSelectedText();
const linkage = searchFunction(cleanName(hostText));
if (linkage) {
Logger.log(linkage);
DocumentApp.getActiveDocument().getSelection().getRangeElements()[0].getElement().asText().editAsText().setLinkUrl(linkage);
}
}
I initially asked on stackOverflow a similar question which resulted in the final DocumentApp... line. However, it has the described problem and I wasn't able to catch it at the time due to how I use the script in my work.

I believe your goal is as follows.
You want to set the hyperlink to the selected text. The text is part of a paragraph.
In your script, a whole paragraph is used. And, in your script, when a text is not selected, an error occurs. In this case, how about the following modification?
Modified script:
function websiteCall() {
const hostText = getSelectedText();
const linkage = searchFunction(cleanName(hostText));
if (linkage) {
Logger.log(linkage);
const select = DocumentApp.getActiveDocument().getSelection();
if (select) {
const ele = select.getRangeElements()[0];
ele.getElement().asText().editAsText().setLinkUrl(ele.getStartOffset(), ele.getEndOffsetInclusive(), linkage);
}
}
}
When you select a text of a part of a paragraph and run the script, the hyperlink of linkage is set to the selected text.
References:
Class RangeElement
setLinkUrl(startOffset, endOffsetInclusive, url)

Related

TinyMCE retrieving elements using DomQuery

I'm dealing with TinyMCE to create a WYSIWYG editor, but there is a problem and now I'm stuck on it.
I need to create a system where users are allowed to create specific documents, each devided into sections i.e. a wrapper. Inside every section there are textual and block elements (p, table, img and so on).
Now, the problem is: when a new section needs to be created I'm using the following code
function insertRawSection () {
// Close the current section and open the next one
Editor.execCommand('mceInsertRawHtml',false,`</section><section><h1>${ZERO_SPACE}</h1>`)
}
This code works, but the real problem comes out when I need to move the cursor at the start of the new h1 element.
I can't retrieve the new heading because if I look for it with DomQuery it doesn't appear.
The code I use to lookup the h1 element is the following
function insertRawSection () {
// Close the current section and open the next one
Editor.execCommand('mceInsertRawHtml',false,`</section><section><h1 data-pointer>${ZERO_SPACE}</h1>`)
// Lookup the last inserted heading
console.log($('[data-pointer]'))
}
Note: The variable $ is not JQuery but is TinyMCE.DomQuery (everything is correctly setted up)
The log print only the previous existent headings, but not the last one. Probably there is somethings like a refresh to execute, but what i have to do in order to "communicate" between this command and the DomQuery APIs?
Instead of using mceInsertRawHtml same can be achieved using dom methods of tinymce.
var ed = tinymce.activeEditor;
var currentNode = ed.selection.getNode();
var newEle = ed.dom.create('section', {}, '<h1></h1>');
ed.dom.insertAfter(newEle, currentNode);
ed.selection.select(newEle.firstChild.firstChild);
ed.selection.collapse(false);
ed.focus();
Once the element is created same can be selected using dom methods to place the cursor at the begining/ending of the element.

JS - surroundContents only retains highlight on text about 20% of the highlight attempts

I am using a mouseup event to trigger a function which highlights text and surrounds the highlighted text with a span (function from stack overflow):
function highlightText(e) {
var t = window.getSelection().toString();
if (t) {
$("#mySpan").remove();
var range = window.getSelection().getRangeAt(0);
newNode = document.createElement("span");
newNode.id = 'mySpan';
range.surroundContents(newNode);
}
}
The main problem I am encountering is that as long as surroundContents is included, the text remains highlighted only about 20% of the highlight attempts (otherwise highlighting disappears immediately). I tried adding a setTimeout, not calling surroundContent for 1s. I also tried removing the remove() statement, but still no good.
Any ideas on why this is happening?
I was facing the same problem with Chromium on Android. In some specific cases, the call of range.surroundContents(newNode) would cause a very weird behaviour of page reload and so on. After checking the documentation of the function:
This method is nearly equivalent to
newNode.appendChild(range.extractContents());
range.insertNode(newNode). After surrounding, the boundary points of
the range include newNode.
So the obvious thing was to apply another way highlight the text. I found mark.js library which did exactly what I wanted without that annoying side effect. (Here's a JSFiddle sample that shows how it's used to highlight just selection). The difference is that library was not using range.surroundContents(newNode) nor newNode.appendChild but rather node.replaceChild.
Based on that, here's the solution to the problem I was having and I think it applies to your case as well.
function surroundRangeWithSpan(range) {
var span = document.createElement('span');
// The text is within the same node (no other html elements inside of it)
if (range.startContainer.isEqualNode(range.endContainer) && range.startContainer.childNodes.length == 0) {
// Here you customise your <span> element
customSurroundContents(range, span);
} else {
// Here you have to break the selection down
}
return span;
}
function customSurroundContents(range, span) {
var node = range.commonAncestorContainer;
var startNode = node.splitText(range.startOffset);
var ret = startNode.splitText(range.toString().length);
span.textContent = startNode.textContent;
startNode.parentNode.replaceChild(span, startNode);
}
And you pass window.getSelection().getRangeAt(0) to the function.
The likely cause of the failure is the selected text encompasses only the beginning or the ending of a non-text node, and not both of them.
So if were to run that code only selecting "This is Bo" in the following it will fail (and throw an exception) because it doesn't also capture the closing tag in the selection:
This is <em>bold</em>
So ending up with:
This is <em>bo
Reference: https://developer.mozilla.org/en-US/docs/Web/API/Range/surroundContents

Format text as user inputs in a contenteditable div

I'm attempting to make a page that allows users to input text and it will automatically format the input -- as in a screenplay format (similar to Amazon's StoryWriter).
So far I can check for text with ":contains('example text')" and add/remove classes to it. The problem is that all of the following p tags inherit that class.
My solution so far is to use .next() to remove the class I added, but that is limited since there might be need for a line break in the script (in dialogue for instance) and that will remove the dialogue class.
$('.content').on('input', function() {
$("p.input:contains('INT.')").addClass("high").next(".input").removeClass("high");
$("p.input:contains('EXT.')").addClass("high").next(".input").removeClass("high");
});
I can't get || to work in the :contains parameter either, but that's the least of my issues.
I have a JS fiddle
I've worked on this for a while now, and if I could change only the node that contains the text (INT. or EXT. in this example) and leaves the rest alone that would work and I could apply it to the rest of the script.
Any help would be appreciated, I'm new to the stackoverflow so thank you.
See the comments in the code below for an explanation of what's going on.
Fiddle Example
JQuery
var main = function(){
var content = $('.content');
content.on('input', function() {
$("p.input").each(function() {
//Get the html content for the current p input.
var text = $(this).html();
//indexOf will return a positive value if "INT." or "EXT." exists in the html
if (text.indexOf('INT.') !== -1 || text.indexOf('EXT.') !== -1) {
$(this).addClass('high');
}
//You could include additional "if else" blocks to check and apply different conditions
else { //The required text does not exist, so remove the class for the current input
$(this).removeClass('high');
}
});
});
};//main close
$(document).ready(main);

Filter by tag JAVASCRIPT

When I click a tag then the tag entered to text search and then another article that have a same tag will appear, how I can get it with javascript?
I have tried to search and I found some but nothing helping me and but I confused about my keyword, someone can give link or code?
Could you share a HTML snippet of your code ?
var searchbar = document.querySelector('header input.search');
var searchTerms = document.querySelectorAll('.my-selector-tag-chip');
Array.prototype.forEach.call(searchTerms, function (itemSearchTerm) {
itemSearchTerm.addEventListener('click', function (button) {
searchbar.value = this.innerText.toLowerCase();
searching();
});
The searching method is the launch of the search (inner the page content or for the whole website). You can add a listener for your search input, so the searching method call is useless.

How can I create tags using Rangy.js without a class attribute?

I've been playing with Rangy.js for selection ranges and so far really like it. I'm looking to wrap a selection range's text nodes within a certain tag and toggle this upon button click. I have it working great using the cssClassApplierModule with the exception of (and it makes sense due to the name) I HAVE to also give the dom element a class that it's applying to itself.
So right now when I select a range and apply for instance a strong tag, my end result is:
Text text text <strong class="test"> selected text </strong> text text text
And I'd like it to be:
Text text text <strong> selected text </strong> text text text
The code I have so far is as follows:
function gEBI(id) {
return document.getElementById(id);
}
var action;
function toggleAction() {
action.toggleSelection();
}
rangy.init();
// Enable buttons
var cssClassApplierModule = rangy.modules.CssClassApplier;
// Next line is pure paranoia: it will only return false if the browser has no support for ranges,
// selections or TextRanges. Even IE 5 would pass this test.
if (rangy.supported && cssClassApplierModule && cssClassApplierModule.supported) {
action = rangy.createCssClassApplier("test", {
elementTagName: "strong",
elementProperties: { }
});
var toggleActionButton = gEBI(nsID);
toggleActionButton.disabled = false;
toggleActionButton.ontouchstart = toggleActionButton.onmousedown = function () {
toggleAction();
return false;
};
}
I tried "" and null instead of "text" as the css class being passed, and it will toggle, but no longer toggle off and is obviously not the correct solution.
Any help appreciated.. Thanks!
Rangy's CSS class applier won't let you do this, unfortunately. The fundamental problem is that it relies on the CSS class to decide which elements and text nodes to surround or add/remove classes from. It's considerably simpler to detect the presence of a class than the more general case of detecting a style, such as boldness.
I did some work last year on a more ambitious and generic execCommand module that would do what you want. It got as far as a working demo but I got bogged down in tricky edge cases and stopped working on it. I do intend to go back to it but it's likely to be months before anything is ready.

Categories

Resources