Extjs messagebox prompt limit the text input entered by user - messagebox

I am using http://dev.sencha.com/playpen/docs/output/Ext.MessageBox.html#prompt to display an extjs prompt where the user can enter some text and click Ok. Now if I want to restrict the user to enter text not more than 100 characters, what should I do?
I understand I need to write some kind of an eventhandler but what is the event? Is it possible to look at a code sample?

When you invoke MessageBox.prompt it will return instance of the singleton which can be used to get dom reference of textbox element, this element can be used to specify attribute maxlength which can be used to limit length of text that can be entered
var dlg = Ext.MessageBox.prompt('Name', 'Please enter your name:', function(btn, text){
if (btn == 'ok'){
// process text value and close...
}
});
var textboxEl = dlg.getDialog().body.child('input[class=ext-mb-input]', true);
textboxEl.setAttribute('maxlength', 1); // second parameter is character length allowed so change it according to your need
Reference:
Character Limit in HTML

Updated for Ext 4 (with many thanks to SilentSakky for the original answer):
var dlg = Ext.MessageBox.prompt('Name', 'Please enter your name:', function(btn, text){
if (btn == 'ok'){
// process text value and close...
}
});
var textboxEl = dlg.getEl().query('input')[0];
textboxEl.setAttribute('maxlength', 1);

Related

Save document in a for loop using computeWithForm

I have the following button that can save multiple values in a for loop using computeWithForm
//to keep the code simple, I use hardcode value to save document except values in for loop
var forloopvalues = #DbLookup(#DbName(),"CompetencyCourseViewCourseFirst", "myFieldValue1",2 );//use myFieldValue1 to lookup in the view
var date ="13/03/2017";
for(var i =0;i<forloopvalues.length;i++)
{
var mdoc = database.createDocument();
cdate = session.createDateTime(date);
mdoc.replaceItemValue("Form", "myForm");
mdoc.replaceItemValue("myField1","myFieldValue1")
mdoc.replaceItemValue("myField2", forloopvalues[i]); //suppose there are four values in the forloopvalues
mdoc.replaceItemValue("myField3","myFieldValue3");
mdoc.replaceItemValue("myField4", "myFieldValue4");
mdoc.replaceItemValue("myField5", "myFieldValue5");
if(mdoc.computeWithForm(false,false))
{
mdoc.save(true, true);
getComponent("computedField1").setValue("Record saved");
}
else
{
}
}
When I click the button, it can save the document. In the Lotus Notes Client, I can see there are four documents in the view. All the computed fields in the form also filled. However, I notice myField2 only saves the first value in forloopvalues not all values. So although there are four documents, both of them save the first value only in myField2. Therefore, if the computed field in the form that contains myField2 plus other values, it only show the first value in myField2 plus other values
I review the code, there is forloopvalues[i] within the for loop and I don't understand why it only saves the first value only.
For example, assume forloopvalues are valueA, valueB, valueC, valueD. The myField2 only save valueA four times. If there is a computed field in the form that connect myField2 with other values together, the computed field value show valueA+othervalues four times.
I have another attempt, if I use document.save() + On document save in Run form validation in Data tab
In Lotus Notes Client to open the view and read the saved document. It will not show computed field in the form but the loop value is correct
For example, assume forloopvalues are valueA, valueB, valueC, valueD. The myField2 save valueA, valueB, valueC, valueD for each document. If there is a computed field in the form that connect myField2 with other values together, the computed field value show each forloopvalues+othervalues four times (e.g. valueA+othervalues, valueB+othervalues, valueC+othervalues, valueD+othervalues)
I believe computeWithForm is almost near the result although it save the first value in forloopvalues only. In Help Contents, I search about computeWithForm. It's syntax is
computeWithForm(dodatatypes:boolean, raiseerror:boolean) : boolean
and compare to the code in the application I think the syntax is fine. I double check the for loop and not understand why it only save the first value.
So how can I use computeWithForm to save the loop value. Thanks a lot.
update
I think computeWithForm is almost near the result because it can fill all the computed field in the form, but it saves one value in multiple times depends on the number of forloopvalues.
If forloopvalues has one value only, then it saves one time. It forloopvalues has three values, it save three times, each document (in myField2) contains the value of forloopvalues.
If I use document.save(), it can save documents for each value in forloopvalues individually but it will not fill the computed field in the form.
update
I try the following code to see if the value appears correctly inside the function
var forloopvalues = #DbLookup(#DbName(), "CompetencyCourseViewCourseFirst", "myFieldValue1", 2);
for (var index in forloopvalues) {
if (forloopvalues.hasOwnProperty(index)) {
makeAnewDoc(forloopvalues[index]);
}
}
function makeAnewDoc(field2Value) {
var mdoc = database.createDocument();
cdate = session.createDateTime(date);
mdoc.replaceItemValue("Form", "myForm");
mdoc.replaceItemValue("myField1", "myFieldValue1")
mdoc.replaceItemValue("myField2", field2Value);
if (!viewScope.resultMessage) {
viewScope.resultMessage = [];
}
if (mdoc.computeWithForm(false, false)) {
mdoc.save(true, true);
viewScope.resultMessage.push("Record saved " + forloopvalues[index]);
} else {
viewScope.resultMessage.push("Save failed for " + forloopvalues[index]);
}
// Bleeding memory otherwise!
mdoc.recycle();
}
The result is when I click the button, the screen can show this message (suppose forloopvalues has four values)
Record saved valueA ,Record saved valueB ,Record saved valueC ,Record saved valueD
Due to the screen can show the forloopvalues, I feel strange why it only saves the same value four times.
Your code actually would save the last value. Don't loop through the values and save the same document 4 times. ReplaceItemValue can take an array as input. So you can use your lookup result directly. No loop required. Should be faster too
Did misread your question on tiny mobile screen. You need to change your loop slightly:
var forloopvalues = #DbLookup(#DbName(), "CompetencyCourseViewCourseFirst", "myFieldValue1", 2);
var mdoc; // In SSJS (ES3 there are no block level variables!)
for (var index in forloopvalues) {
if (forloopvalues.hasOwnProperty(index)) {
mdoc = database.createDocument();
cdate = session.createDateTime(date);
mdoc.replaceItemValue("Form", "myForm");
mdoc.replaceItemValue("myField1", "myFieldValue1")
mdoc.replaceItemValue("myField2", forloopvalues[index]);
// Bind your UI field to a scope variable
// don't access them directly
if (!viewScope.resultMessage) {
viewScope.resultMessage = [];
}
if (mdoc.computeWithForm(false, false)) {
mdoc.save(true, true);
viewScope.resultMessage.push("Record saved " + index);
} else {
viewScope.resultMessage.push("Save failed for " + index);
}
// Bleeding memory otherwise!
mdoc.recycle();
}
}
or better: pull the function to save the document out (variables are function level in SSJS, not block level):
var forloopvalues = #DbLookup(#DbName(), "CompetencyCourseViewCourseFirst", "myFieldValue1", 2);
for (var index in forloopvalues) {
if (forloopvalues.hasOwnProperty(index)) {
makeAnewDoc(forloopvalues[index]);
}
}
function makeAnewDoc(field2Value) {
var mdoc = database.createDocument();
cdate = session.createDateTime(date);
mdoc.replaceItemValue("Form", "myForm");
mdoc.replaceItemValue("myField1", "myFieldValue1")
mdoc.replaceItemValue("myField2", field2Value);
if (!viewScope.resultMessage) {
viewScope.resultMessage = [];
}
if (mdoc.computeWithForm(false, false)) {
mdoc.save(true, true);
viewScope.resultMessage.push("Record saved " + index);
} else {
viewScope.resultMessage.push("Save failed for " + index);
}
// Bleeding memory otherwise!
mdoc.recycle();
}
Let us know how it goes.
ComputeWithForm broke at some point in the Domino 9.0.1 fix pack cycle. Not sure which FP broke it. IBM has confirmed this issue and is looking at a fix. The onload, onsave, and both options do not work. SPR #LHEYAKALAH

addEventListener in a chrome extension

I'm trying to make a simple domain check before sending an email on gmail. So I wrote the below code:
//debugger;
document.addEventListener('blur', function(event){
var target = event.target;
if (target.name !== 'to' && target.name !== 'cc' && target.name !== 'bcc') return;
console.log(target.name, ":", target.value);
},true); // event listener blur
I can see target.name on console window like "to:","cc:", or "bcc:". However, can not get value at all. Any advice appreciated. Thank you.
(What I believe to be)
The problem
Each time you add an address in recipient's field (to, cc, bcc), a new input field is appended to the element holding the recipient address (e.g. with name="to" and value=<email#addree.ss>). Furthermore, an empty textarea is always appended at the end and its purpose is to capture any new email address you might want to add (convert it to an input field as mentioned before and emptying itself again).
Use case:
You see 3 email addresses in the to field.
You click in the field (so it gains focus).
You click away (so it losses focus).
The following gets logged: to: (i.e. no value).
What actually happens, is that you log the value of the empty textarea at the end of the to field.
The solution:
Every time you catch a blur event related to a recipient's field, act upon (e.g. log) the values of all elements whose name equals the blurred field's name.
I know it doesn't make much sense, so here is an example:
document.addEventListener("blur", function(evt) {
var tname = evt.target.name;
if ((tname !== "to") && (tname !== "cc") && (tname !== "bcc")) {
return;
}
var elemList = document.querySelectorAll("[name='" + tname + "']");
[].slice.call(elemList).forEach(function(elem) {
console.log(elem.name, ":", elem.value);
});
}, true);

jquery-jable: How to display a field as read-only in the edit form?

I have a table pre-populated with the company LAN IP addresses with fields for associated data, status, etc. The (jquery-)jtable fields collection is configured like this.
fields: {
id: { title: 'ID'},
ip: { title: 'IP address, edit: false }
more: { ... }
}
This works but the problem is that when the edit dialog pops up the user can't see the ip address of the record being edited as jtable's edit form doesn't show the field.
I've read through the documentation but can't see any way to display a field as read-only in the edit form. Any ideas?
You don't need to hack the jTable library asset, this just leads to pains when you want to update to a later version. All you need to do is create a custom input via the jTable field option "input", see an example field setup to accomplish what you need here:
JobId: {
title: 'JobId',
create: true,
edit: true,
list: true,
input: function (data) {
if (data.value) {
return '<input type="text" readonly class="jtable-input-readonly" name="JobId" value="' + data.value + '"/>';
} else {
//nothing to worry about here for your situation, data.value is undefined so the else is for the create/add new record user interaction, create is false for your usage so this else is not needed but shown just so you know when it would be entered
}
},
width: '5%',
visibility: 'hidden'
},
And simple style class:
.jtable-input-readonly{
background-color:lightgray;
}
I have simple solution:
formCreated: function (event, data)
{
if(data.formType=='edit') {
$('#Edit-ip').prop('readonly', true);
$('#Edit-ip').addClass('jtable-input-readonly');
}
},
For dropdown make other options disabled except the current one:
$('#Edit-country option:not(:selected)').attr('disabled', true);
And simple style class:
.jtable-input-readonly{
background-color:lightgray;
}
I had to hack jtable.js. Start around line 2427. Changed lines are marked with '*'.
//Do not create element for non-editable fields
if (field.edit == false) {
//Label hack part 1: Unless 'hidden' we want to show fields even though they can't be edited. Disable the 'continue'.
* //continue;
}
//Hidden field
if (field.type == 'hidden') {
$editForm.append(self._createInputForHidden(fieldName, fieldValue));
continue;
}
//Create a container div for this input field and add to form
var $fieldContainer = $('<div class="jtable-input-field-container"></div>').appendTo($editForm);
//Create a label for input
$fieldContainer.append(self._createInputLabelForRecordField(fieldName));
//Label hack part 2: Create a label containing the field value.
* if (field.edit == false) {
* $fieldContainer.append(self._myCreateLabelWithText(fieldValue));
* continue; //Label hack: Unless 'hidden' we want to show fields even though they can't be edited.
* }
//Create input element with it's current value
After _createInputLabelForRecordField add in this function (around line 1430):
/* Hack part 3: Creates label containing non-editable field value.
*************************************************************************/
_myCreateLabelWithText: function (txt) {
return $('<div />')
.addClass('jtable-input-label')
.html(txt);
},
With the Metro theme both the field name and value will be grey colour.
Be careful with your update script that you're passing back to. No value will be passed back for the //edit: false// fields so don't include them in your update query.
A more simple version for dropdowns
$('#Edit-country').prop('disabled',true);
No need to disable all the options :)

Preventing tab to cycle through address bar

I realize this is probably an accessibility issue that may best be left alone, but I'd like to figure out if it possible to prevent the tab from visiting the address bar in the tabbing cycle.
My application has another method of cycling through input areas, but many new users instinctively try to use the tab, and it doesn't work as expected.
Here's a generic jquery implementation where you don't have to find the max tab index. Note that this code will also work if you add or remove elements in your DOM.
$('body').on('keydown', function (e) {
var jqTarget = $(e.target);
if (e.keyCode == 9) {
var jqVisibleInputs = $(':input:visible');
var jqFirst = jqVisibleInputs.first();
var jqLast = jqVisibleInputs.last();
if (!e.shiftKey && jqTarget.is(jqLast)) {
e.preventDefault();
jqFirst.focus();
} else if (e.shiftKey && jqTarget.is(jqFirst)) {
e.preventDefault();
jqLast.focus();
}
}
});
However, you should note that the code above will work only with visible inputs. Some elements may become the document's activeElement even if they're not input so if it's your case, you should consider adding them to the $(':input:visible') selector.
I didn't add code to scroll to the focus element as this may not be the wanted behavior for everyone... if you need it, just add it after the call to focus()
You can control the tabbing order (and which elements should be able to get focus at all) with the global tabindex attribute.
However, you can't prevent users to tab into another context not under control of the page (e.g. the browser's address bar) with this attribute. (It might be possible in combination with JavaScript, though.)
For such a (evil!) use case, you'd have to look into keyboard traps.
WCAG 2.0 has the guideline: 2.1.2 No Keyboard Trap. In Understanding SC 2.1.2 you can find "Techniques and Failures" for this guideline:
F10: Failure of Success Criterion 2.1.2 and Conformance Requirement 5 due to combining multiple content formats in a way that traps users inside one format type
FLASH17: Providing keyboard access to a Flash object and avoiding a keyboard trap
G21: Ensuring that users are not trapped in content
So maybe you get some ideas by that how such a trap would be possible.
I used to add two tiny, invisible elements on tabindex 1 and on the last tabindex. Add a onFocus for these two: The element with tabindex 1 should focus the last real element, the one with the max tabindex should focus the first real element. Make sure that you focus the first real element on Dom:loaded.
You could use Javascript and capture the "keydown" event on the element with the highest "tabindex". If the user presses the "TAB" key (event.keyCode==9) without the "Shift" key (event.shiftKey == false) then simply set the focus on the element with the lowest tabindex.
You would then also need to do the same thing in reverse for the element with the lowest tabindex. Capture the "keydown" event for this element. If the user presses the "TAB" key (event.keyCode==9) WITH the "Shift" key (event.shiftKey == true) then set the focus on the element with the highest tabindex.
This would effectively prevent the address bar from ever being focused using the TAB key. I am using this technique in my current project.
Dont forget to cancel the keydown event if the proper key-combination is pressed! With JQuery it's "event.preventDefault()". In standard Javascript, I believe you simply "return false".
Here's a JQuery-laden snippet I'm using...
$('#dos-area [tabindex=' + maxTabIndex + ']').on('keydown', function (e) {
if (e.keyCode == 9 && e.shiftKey == false) {
e.preventDefault();
$('#dos-area [tabindex=1]').focus();
}
});
$('#dos-area [tabindex=1]').on('keydown', function (e) {
if (e.keyCode == 9 && e.shiftKey == true) {
e.preventDefault();
$('#dos-area [tabindex=' + maxTabIndex + ']').focus();
}
});
Also keep in mind that setting tabindex=0 has undesirable results on the order in which things are focused. I always remember (for my purposes) that tabindex is a 1-based index.
Hi i have an easy solution. just place an empty span on the end of the page. Give it an id and tabindex = 0, give this span an onfocus event, when triggered let your focus jump to the first element on your page you want to cycle trough. This way you won't lose focus on the document, because if you do your events don't work anymore.
I used m-albert solution and it works. But in my case I do not control the tabindex properties. My intention is set the focus on a toolbar at the top of the page (first control) when user leaves the last control on the page.
$(':input:visible').last().on('keydown', function (e) {
if (e.keyCode == 9 && e.shiftKey == false) {
e.preventDefault();
$('html, body').animate({
scrollTop: 0
}, 500);
$(':input:visible', context).first().focus();
}
});
Where context can be any Jquery object, selector, or even document, or you can omit it.
The scrolling animation, of course, is optional.
Not sure if this is still a issue, but I implemented my own solution that does not use jQuery, can be used in the case when all the elements have tabindex="0", and when the DOM is subject to change. I added an extra argument for a context if you want to limit the tabcycling to a specific element containing the tabindex elements.
Some brief notes on the arguments:
min must be less than or equal to max, and contextSelector is an optional string that if used should be a valid selector. If contextSelector is an invalid selector or a selector that doesn't match with any elements, then the document object is used as the context.
function PreventAddressBarTabCyle(min, max, contextSelector) {
if( isNaN(min) ) throw new Error('Invalid argument: first argument needs to be a number type.')
if( isNaN(max) ) throw new Error('Invalid argument: second argument needs to be a number type.')
if( max < min ) throw new Error('Invalid arguments: first argument needs to be less than or equal to the second argument.')
min = min |0;
max = max |0;
var isDocumentContext = typeof(contextSelector) != 'string' || contextSelector == '';
if( min == max ) {
var tabCycleListener = function(e) {
if( e.keyCode != 9 ) return;
var context = isDocumentContext ? document : document.querySelector(contextSelector);
if( !context && !('querySelectorAll' in context) ) {
context = document;
}
var tabindexElements = context.querySelectorAll('[tabindex]');
if( tabindexElements.length <= 0 ) return;
var targetIndex = -1;
for(var i = 0; i < tabindexElements.length; i++) {
if( e.target == tabindexElements[i] ) {
targetIndex = i;
break;
}
}
// Check if tabbing backward and reached first element
if( e.shiftKey == true && targetIndex == 0 ) {
e.preventDefault();
tabindexElements[tabindexElements.length-1].focus();
}
// Check if tabbing forward and reached last element
if( e.shiftKey == false && targetIndex == tabindexElements.length-1 ) {
e.preventDefault();
tabindexElements[0].focus();
}
};
} else {
var tabCycleListener = function(e) {
if( e.keyCode != 9 ) return;
var context = isDocumentContext ? document : document.querySelector(contextSelector);
if( !context && !('querySelectorAll' in context) ) {
context = document;
}
var tabindex = parseInt(e.target.getAttribute('tabindex'));
if( isNaN(tabindex) ) return;
// Check if tabbing backward and reached first element
if (e.shiftKey == true && tabindex == min) {
e.preventDefault();
context.querySelector('[tabindex="' + max + '"]').focus();
}
// Check if tabbing forward and reached last element
else if (e.shiftKey == false && tabindex == max) {
e.preventDefault();
context.querySelector('[tabindex="' + min + '"]').focus();
}
};
}
document.body.addEventListener('keydown', tabCycleListener, true);
}
More notes:
If min is equal to max, then tab cycling will occur naturally until the last element in the context is reached. If min is strictly less than max, then tabbing will cycle naturally until either the min or the max is reached. If tabbing backwards and the min is reached, or tabbing forward and the max is reached, then the tabbing will cycle to the min element or max element respectively.
Example usage:
// For all tabindex elements inside the document
PreventAddressBarTabCyle(0,0);
// Same as above
PreventAddressBarTabCyle(1,1);
// NOTE: if min == max, the tabindex value doesn't matter
// it matches all elements with the tabindex attribute
// For all tabindex elements between 1 and 15
PreventAddressBarTabCyle(1,15);
// For tabindex elements between 1 and 15 inside
// the first element that matches the selector: .some-form
PreventAddressBarTabCyle(1,15, '.some-form');
// For all tabindex elements inside the element
// that matches the selector: .some-form2
PreventAddressBarTabCyle(1,1, '.some-form2');
Salinan solution worked for me
Put this in the start of your html page:
<span tabindex="0" id="prevent-outside-tab"></span>
and this at the end of your html page.:
<span tabindex="0" onfocus="foucsFirstElement()"></span>
foucsFirstElement() :
foucsFirstElement() {
document.getElementById("prevent-outside-tab").focus();
},

recognize multple lines on info.selectionText from Context Menu

My extension adds a context menu whenever a user selects some text on the page.
Then, using info.selectionText, I use the selected text on a function executed whenever the user selects one of the items from my context menu. (from http://code.google.com/chrome/extensions/contextMenus.html)
So far, all works ok.
Now, I got this cool request from one of the extension users, to execute that same function once per line of the selected text.
A user would select, for example, 3 lines of text, and my function would be called 3 times, once per line, with the corresponding line of text.
I haven't been able to split the info.selectionText so far, in order to recognize each line...
info.selectionText returns a single line of text, and could not find a way to split it.
Anyone knows if there's a way to do so? is there any "hidden" character to use for the split?
Thanks in advance... in case you're interested, here's the link to the extension
https://chrome.google.com/webstore/detail/aagminaekdpcfimcbhknlgjmpnnnmooo
Ok, as OnClickData's selectionText is only ever going to be text you'll never be able to do it using this approach.
What I would do then is inject a content script into each page and use something similar to the below example (as inspired by reading this SO post - get selected text's html in div)
You could still use the context menu OnClickData hook like you do now but when you receive it instead of reading selectionText you use the event notification to then trigger your context script to read the selection using x.Selector.getSelected() instead. That should give you what you want. The text stays selected in your extension after using the context menu so you should have no problem reading the selected text.
if (!window.x) {
x = {};
}
// https://stackoverflow.com/questions/5669448/get-selected-texts-html-in-div
x.Selector = {};
x.Selector.getSelected = function() {
var html = "";
if (typeof window.getSelection != "undefined") {
var sel = window.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;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
return html;
}
$(document).ready(function() {
$(document).bind("mouseup", function() {
var mytext = x.Selector.getSelected();
alert(mytext);
console.log(mytext);
});
});​
http://jsfiddle.net/richhollis/vfBGJ/4/
See also: Chrome Extension: how to capture selected text and send to a web service

Resources