Google UI Script validateMatches doesn't work, validateNotMatches does - validation

I am designing a form using Google Scripting. I have two text boxes that need to hold time values, and I want to make sure the input is valid.
function setupTimeValidators(widget) {
var timeRe = /(0[1-9])|(1[0-2]):[0-5][0-9] ?[ap]m/i;
var onValid = (UiApp.getActiveApplication().createClientHandler()
.validateMatches(widget, timeRe)
.forTargets(widget)
.setStyleAttribute("background", "#FFFFFF"));
var onInvalid = (UiApp.getActiveApplication().createClientHandler()
.validateNotMatches(widget, timeRe)
.forTargets(widget)
.setStyleAttribute("background", "#FFCCCC"));
widget.addKeyUpHandler(onValid);
widget.addKeyUpHandler(onInvalid);
}
The onInvalid parts changes the textbox background color as soon as I start typing in the textbox, but it never changes back to white when I get to 01:11 pm. (I have tested this with other values.)
I am sure my regular expression works, because I tested it like so:
function test() {
Browser.msgBox(/(0[1-9])|(1[0-2]):[0-5][0-9] ?[ap]m/i.test("01:11 pm")); // true
Browser.msgBox(/(0[1-9])|(1[0-2]):[0-5][0-9] ?[ap]m/i.test("00:11 pm")); // false
Browser.msgBox(/(0[1-9])|(1[0-2]):[0-5][0-9] ?[ap]m/i.test("10:11 pm")); // true
Browser.msgBox(/(0[1-9])|(1[0-2]):[0-5][0-9] ?[ap]m/i.test("10:90 pm")); // false
}
Any ideas what could be going on? Thanks!

You can't use a regex object with validateMatches or validateNotMatches.. you should use a string representation of the regex, as such:
var timeRe = "(0[1-9])|(1[0-2]):[0-5][0-9] ?[ap]m";
var flags = "i";
...
.validateMatches(widget, timeRe, flags)

Related

How to show and hide standard InDesign panels through scripting?

I’m able to get a reference to, for instance, the “Scripts” panel; but it doesn’t seem to have the show and hide methods like panels created through scripting (see code below). How can I get it to show or hide programmatically, without invoking the corresponding menu item?
function findPanelByName(name) { // String → Panel|null
for (var iPanel = 0; iPanel < app.panels.length; iPanel++) {
var panel = app.panels[iPanel];
if (panel.name == name) {
return panel;
}
}
return null;
}
var scriptsPanel = findPanelByName('Scripts');
scriptsPanel.show(); // → “scriptsPanel.show is not a function”
A few things: Your method to get the right panel is unneccessarily complicated. You could just get the panel by using the panel collection's item method like so:
var scriptsPanel = app.panels.item('Scripts');
Then, you don't need to use show() to show the panel (as that method does not exist), but you can just show the panel, by settings its visible property to true:
scriptsPanel.visible = true;
And lastly, in case anybody else is supposed to use the script, you should make sure, that it works with international versions of InDesign as well. In my German version, the above panel for example would not exist, as it is named Skripte instead of Scripts. To avoid that you can use the language independent key of InDesign:
var scriptsPanel = app.panels.item('$ID/Scripting');
So, in conclusion, the entire script could be shortened up to this one-liner
app.panels.item('$ID/Scripting').visible = true;

kendo ui editor how to modify user selection with range object

Kendo UI 2015.2.805 Kendo UI Editor for Jacascript
I want to extend the kendo ui editor by adding a custom tool that will convert a user selected block that spans two or more paragraphs into block of single spaced text. This can be done by locating all interior p tags and converting them into br tags, taking care not to change the first or last tag.
My problem is working with the range object.
Getting the range is easy:
var range = editor.getRange();
The range object has a start and end container, and a start and end offset (within that container). I can access the text (without markup)
console.log(range.toString());
Oddly, other examples I have seen, including working examples, show that
console.log(range);
will dump the text, however that does not work in my project, I just get the word 'Range', which is the type of the object. This concerns me.
However, all I really need however is a start and end offset in the editor's markup (editor.value()) then I can locate and change the p's to br's.
I've read the telerik documentation and the referenced quirksmode site's explanation of html ranges, and while informative nothing shows how to locate the range withing the text (which seems pretty basic to me).
I suspect I'm overlooking something simple.
Given a range object how can I locate the start and end offset within the editor's content?
EDIT: After additional research it appears much more complex than I anticipated. It seems I must deal with the range and/or selection objects rather than directly with the editor content. Smarter minds than I came up with the range object for reasons I cannot fathom.
Here is what I have so far:
var range = letterEditor.editor.getRange();
var divSelection;
divSelection = range.cloneRange();
//cloning may be needless extra work...
//here manipulate the divSelection to how I want it.
//divSeletion is a range, not sure how to manipulate it
var sel = letterEditor.editor.getSelection()
sel.removeAllRanges();
sel.addRange(divSelection);
EDIT 2:
Based on Tim Down's Solution I came up with this simple test:
var html;
var sel = letterEditor.editor.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
html = html.replace("</p><p>", "<br/>")
var range = letterEditor.editor.getRange();
range.deleteContents();
var div = document.createElement("div");
div.innerHTML = html;
var frag = document.createDocumentFragment(), child;
while ((child = div.firstChild)) {
frag.appendChild(child);
}
range.insertNode(frag);
The first part, getting the html selection works fine, the second part also works however the editor inserts tags around all lines so the result is incorrect; extra lines including fragments of the selection.
The editor supports a view html popup which shows the editor content as html and it allows for editing the html. If I change the targeted p tags to br's I get the desired result. (The editor does support br as a default line feed vs p, but I want p's most of the time). That I can edit the html with the html viewer tool lets me know this is possible, I just need identify the selection start and end in the editor content, then a simple textual replacement via regex on the editor value would do the trick.
Edit 3:
Poking around kendo.all.max.js I discovered that pressing shift+enter creates a br instead of a p tag for the line feed. I was going to extend it to do just that as a workaround for the single-space tool. I would still like a solution to this if anyone knows, but for now I will instruct users to shift-enter for single spaced blocks of text.
This will accomplish it. Uses Tim Down's code to get html. RegEx could probably be made more efficient. 'Trick' is using split = false in insertHtml.
var sel = letterEditor.editor.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
var block = container.innerHTML;
var rgx = new RegExp(/<br class="k-br">/gi);
block = block.replace(rgx, "");
rgx = new RegExp(/<\/p><p>/gi);
block = block.replace(rgx, "<br/>");
rgx = new RegExp(/<\/p>|<p>/gi);
block = block.replace(rgx, "");
letterEditor.editor.exec("insertHtml", { html: block, split: false });
}

Select hyphenated word with double-click

UPDATE: Per the recommendation below, here's specifically what I'd like to do: If I double-click the mouse cursor anywhere from the "b" to the "n" of "blue-green", I want all of the word "blue-green" should be highlighted. How can this be done? Currently, depending on where you click, it treats "blue-green" as three separate character strings. So, if you double click between the "b" and "e" of "blue" it highlights only "blue" and not "-green." If you double-click the hyphen, it highlights the hyphen alone. And if you double-click between the "g" and "n" of "green" it highlights only "green" and not "blue-".
ORIGINAL: When I double-click a hyphenated word or set of characters (e.g. "123-abc" or "blue-green" etc.), only the part of the word that I double-clicked is highlighted. I'd like the whole word to be highlighted.
I'm using Windows 7 Pro. If it needs to be done on a per-application basis, I'm most interested in fixing it for Google Chrome, but any Windows-compatible web browser would be OK.
Old question, but I happen to have been working on the same issue. Here's my solution:
jsFiddle.net
"use strict"
// Tweak to make a double-click select words with hyphens
//
// As of 2016-0816, None of the major Mac browser selects whole words
// with hyphens, like "ad-lib". This tweak fixes the hypen issue.
//
// Note: Firefox 48.0 doesn't automatically select whole words with
// apostrophes like "doesn't". This tweak also treats that.
;(function selectWholeWordsWithHyphens(){
var pOutput = document.getElementById("output")
var selection = window.getSelection()
// Regex designed to find a word+hyphen before the selected word.
// Example: ad-|lib|
// It finds the last chunk with no non-word characters (except for
// ' and -) before the first selected character.
var startRegex = /(\w+'?-?)+-$/g
// Regex designed to find a hyphen+word after the selected word.
// Example: |ad|-lib
var endRegex = /^-('?-?\w+)+/
// Edge case: check if the selection contains no word
// characters. If so, then don't do anything to extend it.
var edgeRegex = /\w/
document.body.ondblclick = selectHyphenatedWords
function selectHyphenatedWords(event) {
if (!selection.rangeCount) {
return
}
var range = selection.getRangeAt(0)
var container = range.startContainer
var string = container.textContent
var selectionUpdated = false
if (string.substring(range.startOffset, range.endOffset)
.search(edgeRegex) < 0) {
// There are no word characters selected
return
}
extendSelectionBackBeforeHypen(string, range.startOffset)
extendSelectionForwardAfterHyphen(string, range.endOffset)
if (selectionUpdated) {
selection.removeAllRanges()
selection.addRange(range)
}
function extendSelectionBackBeforeHypen(string, offset) {
var lastIndex = 0
var result
, index
string = string.substring(0, offset)
while (result = startRegex.exec(string)) {
index = result.index
lastIndex = startRegex.lastIndex
}
if (lastIndex === offset) {
range.setStart(container, index)
selectionUpdated = true
}
}
function extendSelectionForwardAfterHyphen(string, offset) {
if (!offset) {
return
}
string = string.substring(offset)
var result = endRegex.exec(string)
if (result) {
range.setEnd(container, offset + result[0].length)
selectionUpdated = true
}
}
}
})()
It's a standard through all programs that it will do that because they all run off the operating system's typing configuration/program thing. To fix it you would need to do something in System32. I don't know what you would need to do but I suspect this is your problem. You should probably go into more detail though about specifically what it is you want.

Break line in long label text

Is there any trick to break a label text? Because '\n' '\r' '\n\r' don't work.
Many Thanks
if you use those 2 parameters you do what you want don't you ?
app.createLabel(text).setWidth(width).setWordWrap(true)
here is an example (among other widgets ;-):
function showurl() {
var app = UiApp.createApplication();
app.setTitle("Anchor in a popup ;-)");
var panel = app.createFlowPanel()
var image = app.createImage('https://sites.google.com/site/appsscriptexperiments/home/photo.jpg').setPixelSize(50, 50)
var link = app.createAnchor('This is your link', 'https://sites.google.com/site/appsscriptexperiments/home');
var lab = app.createLabel("wrap it because it's too narrow").setWidth(90).setWordWrap(true);
var quit = app.createButton('quit');
panel.add(image).add(link).add(lab).add(quit);
app.add(panel);
var doc = SpreadsheetApp.getActive();
doc.show(app);
}
EDIT : I found an old post(on the Google group forum, thanks again Henrique ;-) about breaking lines in toast messages and here is the code I used for that case... the principle should work for Labels too but I didn't try.
To use it, just use \n (where you want to break the line) in a variable containing your text and pass it through this function. (there are some comment in the script to explain)
function break_(msg){
var temp = escape(msg);// shows codes of all chars
msg = unescape(temp.replace(/%20/g,"%A0")); // replace spaces by non break spaces
temp = msg.replace("\n"," "); // and replace the 'newline' by a normal space
return temp; // send back the result
}
Would something like this work?
//takes a line of text and returns a flex table broken by \n
function breakLabel(text) {
var app = UiApp.getActiveApplication();
var flexTable = app.createFlexTable();
text = text.split('\n'); // split into an array
for (var i=0; i<text.length; i++){
flexTable.setWidget(i, 0, app.createLabel(text[i].toString()));
}
return flexTable;
}
Adding them to a vertical panel helps as well (not the way you want, but still..):
var vPanel = app.createVerticalPanel().setSize(100,100);
var label = app.createLabel('predominantly blabla blala blabla');
app.add(vPanel.add(label));
See reference
For anyone just now stumbling upon this, the best solution seems to be creating an HTML output for anything that needs line breaks.
Documentation
var htmlApp = HtmlService
.createHtmlOutput('<p>A change of speed, a change of style...</p>')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setTitle('My HtmlService Application')
.setWidth(250)
.setHeight(300);
SpreadsheetApp.getActiveSpreadsheet().show(htmlApp);
// The script resumes execution immediately after showing the dialog.

How to set cursor position to end of text in CKEditor?

Is there a way to set the cursor to be at the end of the contents of a CKEditor?
This developer asked too, but received no answers:
http://cksource.com/forums/viewtopic.php?f=11&t=19877&hilit=cursor+end
I would like to set the focus at the end of the text inside a CKEditor. When I use:
ckEditor.focus();
It takes me to the beginning of the text already inside the CKEditor.
Dan's answer got strange results for me, but minor change (in addition to typo fix) made it work:
var range = me.editor.createRange();
range.moveToElementEditEnd( range.root );
me.editor.getSelection().selectRanges( [ range ] );
According to the documentation for CKEditor 4, you can do the following once you have the editor object.
var range = editor.createRange();
range.moveToPosition( range.root, CKEDITOR.POSITION_BEFORE_END );
editor.getSelection().selectRanges( [ range ] );
Link: http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection (under selectRanges function).
After a bit of fiddling, I've got it to work with the following code:
$(document).ready(function() {
CKEDITOR.on('instanceReady', function(ev) {
ev.editor.focus();
var s = ev.editor.getSelection(); // getting selection
var selected_ranges = s.getRanges(); // getting ranges
var node = selected_ranges[0].startContainer; // selecting the starting node
var parents = node.getParents(true);
node = parents[parents.length - 2].getFirst();
while (true) {
var x = node.getNext();
if (x == null) {
break;
}
node = x;
}
s.selectElement(node);
selected_ranges = s.getRanges();
selected_ranges[0].collapse(false); // false collapses the range to the end of the selected node, true before the node.
s.selectRanges(selected_ranges); // putting the current selection there
}
});
The idea is:
Get the root node (not body)
Advance to next node, until there are no more nodes to advance to.
Select last node.
Collapse it
Set range
Here's a similar answer to #peter-tracey. In my case my plugin is inserting a citation. If the user has made a selection, I needed to disable the selection and place the cursor at the end of the sentence.
// Obtain the current selection & range
var selection = editor.getSelection();
var ranges = selection.getRanges();
var range = ranges[0];
// Create a new range from the editor object
var newRange = editor.createRange();
// assign the newRange to move to the end of the current selection
// using the range.endContainer element.
var moveToEnd = true;
newRange.moveToElementEditablePosition(range.endContainer, moveToEnd);
// change selection
var newRanges = [newRange];
selection.selectRanges(newRanges);
// now I can insert html without erasing the previously selected text.
editor.insertHtml("<span>Hello World!</span>");
CKEditor 3.x:
on : {
'instanceReady': function(ev) {
ev.editor.focus();
var range = new CKEDITOR.dom.range( ev.editor.document );
range.collapse(false);
range.selectNodeContents( ev.editor.document.getBody() );
range.collapse(false);
ev.editor.getSelection().selectRanges( [ range ] );
}
}
based on pseudo-code provided by the developers here:
https://dev.ckeditor.com/ticket/9546#comment:3
You have to focus editor, get document object, put it in range,
collapse range (with false parameter), select body (with
selectNodeContents), collapse it (with false parameter) and finally
select range. It is best to do it all in instanceReady event.
This is the easiest solution provided by the ckeditor API. I have tested it on IE10+, ff, safari and Chrome:
range = editor.createRange();
// the first parameter is the last line text element of the ckeditor instance
range.moveToPosition(new CKEDITOR.dom.node(editor.element.$.children[pos - 1]), CKEDITOR.POSITION_BEFORE_END)
range.collapse()
editor.getSelection().selectRanges([ range ])
This will work for sure.
CKEDITOR.config.startupFocus = 'end';
have you tried ckEditor.Selection.Collapse(false);
According to CKEditor 4 documentation, another option is:
const range = this.ckeditor.createRange()
range.moveToElementEditablePosition(range.root, true)
this.ckeditor.getSelection().selectRanges([range])
Link: https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_dom_range.html#method-moveToElementEditablePosition

Resources