How to input decimal numbers in ag-grid cells - decimal

Using numericCellEditor in such way (without cellEditorParams):
var columnDefs = [
{
cellEditor: 'numericCellEditor'
},
]
Decumal numbers (1.25, 3.675 etc.) input is not possible. What cell editor to choose (or, maybe, how to tune numericCellEditor) to work with decimals, i.e. so I could edit values, and in opened editing fields I could'n print letters or other non-numeric symbols, but I could input dot or comma as decimal separator.

Include this
function isCharDecimal(charStr) {
return '.'.indexOf(charStr) === 0;
}
And then isKeyPressedNumeric to
function isKeyPressedNumeric(event) {
const charCode = getCharCodeFromEvent(event);
const charStr = String.fromCharCode(charCode);
return isCharNumeric(charStr) || isCharDecimal(charStr);
}

Check numeric editor example here in the official docs.
You can modify the isKeyPressedNumeric function to something like -
private isKeyPressedNumeric(event): boolean {
const charCode = this.getCharCodeFromEvent(event);
const charAllowed = [".","Backspace"];
const charStr = event.key ? event.key : String.fromCharCode(charCode);
return this.isCharNumeric(charStr) || charAllowed.includes(charStr);
}
Note that this is just an example to allow decimal numbers, you can modify the implementation to suit your requirement.

Add the following function
function isCharDecimal(charStr) {
return !!/\./.test(charStr);
}
And change isKeyPressedNumeric() as following
function isKeyPressedNumeric(event) {
const charCode = getCharCodeFromEvent(event);
const charStr = String.fromCharCode(charCode);
return isCharNumeric(charStr) || isCharDecimal(charStr);
}
Note: This will accept more than one decimal point. :(

As an improvement on the solution offered by Pavel, you could use the following to allow up to one decimal point (but not more than one).
Add this function (where this.eInput is your html input):
function isCharTheFirstDecimal(charStr) {
return !!/\./.test(charStr) && !/\./.test(this.eInput.value);
}
And then change isKeyPressedNumeric to:
function isKeyPressedNumeric(event) {
var charCode = this.getCharCodeFromEvent(event);
var charStr = String.fromCharCode(charCode);
return this.isCharNumeric(charStr) ||
this.isCharTheFirstDecimal(charStr);
}
The other functions used (which are all from the example in the ag-grid documentation here: https://www.ag-grid.com/javascript-grid-cell-editing) are below:
getCharCodeFromEvent(event) {
event = event || window.event;
return typeof event.which == "undefined" ? event.keyCode : event.which;
}
isCharNumeric(charStr) {
return !!/\d/.test(charStr);
}

Related

Filter text layers using selectedLayers property

I am trying to put a check on type of layer to ensure my function call only applies to a text layer in selected layers (number of selected layers are several hundreds). It seems I am doing some mistake using the typeOf method. Can someone please help?
var myComp = app.project.activeItem;
var selectedLayers = myComp.selectedLayers;
var numLayers = selectedLayers.length;
for(var i=0; i < numLayers; i++){
var mySourceText = selectedLayers[i].property("ADBE Text Properties").property("ADBE Text Document");
var myTextDoc = mySourceText.value;
if (typeOf(selectedLayers[i]) == "TextLayer") {
mySourceText.setValue(trim(myTextDoc));
}
}
function trim(strValue){
var str = new String(strValue);
return str.replace(/(^\s*)|(\s*$)/g,"");
}
The correct boolean test you want is
if (selectedLayers[i] instanceof TextLayer) {
instanceof, and no quotes for TextLayer.

Swift - Replacing emojis in a string with whitespace

I have a method that detects urls in a string and returns me both the urls and the ranges where they can be found. Everything works perfectly until there are emojis on the string. For example:
"I'm gonna do this callenge as soon as I can swing again 😂😂😂\n http://youtu.be/SW_d3fGz1hk"
Because of the emojis, the url extracted from the text is http://youtu.be/SW_d3fGz1 instead of http://youtu.be/SW_d3fGz1hk. I figured that the easiest solution was to just replace the emojis on the string with whitespace characters (cause I need the range to be correct for some text styling stuff). Problem is, this is extremely hard to accomplish with Swift (most likely my abilities with the Swift String API is lacking).
I've been trying to do it like this but it seems that I cannot create a string from an array of unicode points:
var emojilessStringWithSubstitution: String {
let emojiRanges = [0x1F601...0x1F64F, 0x2702...0x27B0]
let emojiSet = Set(emojiRanges.flatten())
let codePoints: [UnicodeScalar] = self.unicodeScalars.map {
if emojiSet.contains(Int($0.value)) {
return UnicodeScalar(32)
}
return $0
}
return String(codePoints)
}
Am I approaching this problem the wrong way? Is replacing emojis the best solution here? If so, how can I do it?
Swift 5
Don't use this hardcoded way to detect emojis. In Swift 5 you can do it easily
let inputText = "Some 🖐string 😂😂😂 with 👹👹 👹 emoji 🖐"
let textWithoutEmoij = inputText.unicodeScalars
.filter { !$0.properties.isEmojiPresentation }
.reduce("") { $0 + String($1) }
print(textWithoutEmoij) // Some string with emoji
You can use pattern matching (for emoji patterns) to filter out emoji characters from your String.
extension String {
var emojilessStringWithSubstitution: String {
let emojiPatterns = [UnicodeScalar(0x1F601)...UnicodeScalar(0x1F64F),
UnicodeScalar(0x2702)...UnicodeScalar(0x27B0)]
return self.unicodeScalars
.filter { ucScalar in !(emojiPatterns.contains{ $0 ~= ucScalar }) }
.reduce("") { $0 + String($1) }
}
}
/* example usage */
let str = "I'm gonna do this callenge as soon as I can swing again 😂😂😂\n http://youtu.be/SW_d3fGz1hk"
print(str.emojilessStringWithSubstitution)
/* I'm gonna do this callenge as soon as I can swing again
http://youtu.be/SW_d3fGz1hk */
Note that the above only makes use of the emoji intervals as presented in your question, and is in no way representative for all emojis, but the method is general and can swiftly be extended by including additional emoji intervals to the emojiPatterns array.
I realize reading your question again that you'd prefer substituting emojis with whitespace characters, rather than removing them (which the above filtering solution does). We can achieve this by replacing the .filter operation above with a conditional return .map operation instead, much like in your question
extension String {
var emojilessStringWithSubstitution: String {
let emojiPatterns = [UnicodeScalar(0x1F600)...UnicodeScalar(0x1F64F),
UnicodeScalar(0x1F300)...UnicodeScalar(0x1F5FF),
UnicodeScalar(0x1F680)...UnicodeScalar(0x1F6FF),
UnicodeScalar(0x2600)...UnicodeScalar(0x26FF),
UnicodeScalar(0x2700)...UnicodeScalar(0x27BF),
UnicodeScalar(0xFE00)...UnicodeScalar(0xFE0F)]
return self.unicodeScalars
.map { ucScalar in
emojiPatterns.contains{ $0 ~= ucScalar } ? UnicodeScalar(32) : ucScalar }
.reduce("") { $0 + String($1) }
}
}
I the above, the existing emoji intervals has been extended, as per your comment to this post (listing these intervals), such that the emoji check is now possibly exhaustive.
Swift 4:
extension String {
func stringByRemovingEmoji() -> String {
return String(self.filter { !$0.isEmoji() })
}
}
extension Character {
fileprivate func isEmoji() -> Bool {
return Character(UnicodeScalar(UInt32(0x1d000))!) <= self && self <= Character(UnicodeScalar(UInt32(0x1f77f))!)
|| Character(UnicodeScalar(UInt32(0x2100))!) <= self && self <= Character(UnicodeScalar(UInt32(0x26ff))!)
}
}
Emojis are classified as symbols by Unicode. Character sets are typically used in searching operations. So we will use Character sets a property that is symbols.
var emojiString = "Hey there 🖐, welcome"
emojiString = emojiString.components(separatedBy: CharacterSet.symbols).joined()
print(emojiString)
Output is
Hey there , welcome
Now observe the emoji is replaced by a white space so there is two white space and we replace it by the following way
emojiString.replacingOccurrences(of: " ", with: " ")
The above method replace parameter of: "two white space" to with: "single white space"
Getting all emoji is more complicated than you would think. For more info on how to figure out which characters are emoji, check out this stackoverflow post or this article.
Building on that information, I would propose to use the extension on Character to more easily let us understand which characters are emoji. Then add a String extension to easily replace found emoji with another character.
extension Character {
var isSimpleEmoji: Bool {
guard let firstProperties = unicodeScalars.first?.properties else {
return false
}
return unicodeScalars.count == 1 &&
(firstProperties.isEmojiPresentation ||
firstProperties.generalCategory == .otherSymbol)
}
var isCombinedIntoEmoji: Bool {
return unicodeScalars.count > 1 &&
unicodeScalars.contains {
$0.properties.isJoinControl ||
$0.properties.isVariationSelector
}
}
var isEmoji: Bool {
return isSimpleEmoji || isCombinedIntoEmoji
}
}
extension String {
func replaceEmoji(with character: Character) -> String {
return String(map { $0.isEmoji ? character : $0 })
}
}
Using it would simply become:
"Some string 😂😂😂 with emoji".replaceEmoji(with: " ")
I found that the solutions given above did not work for certain characters such as 🏋️🏻‍♂️ and 🧰.
To find the emoji ranges, using regex I converted the full list of emoji characters to a file with just hex values. Then I converted them to decimal format and sorted them. Finally, I wrote a script to find the ranges.
Here is the final Swift extension for isEmoji().
extension Character {
func isEmoji() -> Bool {
let emojiRanges = [
(8205, 11093),
(12336, 12953),
(65039, 65039),
(126980, 129685)
]
let codePoint = self.unicodeScalars[self.unicodeScalars.startIndex].value
for emojiRange in emojiRanges {
if codePoint >= emojiRange.0 && codePoint <= emojiRange.1 {
return true
}
}
return false
}
}
For reference, here are the python scripts I wrote to parse the hex strings to integers and then find the ranges.
convert-hex-to-decimal.py
decimals = []
with open('hex.txt') as hexfile:
for line in hexfile:
num = int(line, 16)
if num < 256:
continue
decimals.append(num)
decimals = list(set(decimals))
decimals.sort()
with open('decimal.txt', 'w') as decimalfile:
for decimal in decimals:
decimalfile.write(str(decimal) + "\n")
make-ranges.py
first_line = True
range_start = 0
prev = 0
with open('decimal.txt') as hexfile:
for line in hexfile:
if first_line:
prev = int(line)
range_start = prev
first_line = False
continue
curr = int(line)
if prev + 1000 < curr: # 100 is abitrary to reduce number of ranges
print("(" + str(range_start) + ", " + str(prev) + ")")
range_start = curr
prev = curr
Don't hard-code the range of emojis, use this instead.
func 去除表情符号(字符串:String) -> String {
let 转换为Unicode = 字符串.unicodeScalars//https://developer.apple.com/documentation/swift/string
let 去除表情后的结果 = 转换为Unicode.filter { (item) -> Bool in
let 判断是否表情 = item.properties.isEmoji
return !判断是否表情//是表情就不保留
}
return String(去除表情后的结果)
}

Sub Grid Total In Crm

I have a primary Entity (Self-Insurance) and a secondary entity (Compensation). They have a 1:N relationship. So in my main form of Self Insurance I have a sub-grid with the name 'Worker_Compensation' where i am adding up some payroll values.
I have 2 questions. . .
1: The thing I want is that when I add some values in the sub-grid. I need to show a sum of all payrolls in the text below of my main form named as 'TOTAL'.
2: Where should i call this java script(On which event) Onload or Onsave of form ? or else where because I can seems to locate the events on Subgrid.
I am using a java script for this purpose.
enter code here
function setupGridRefresh() {
var targetgrid = document.getElementById("Worker_Compensation");
// If already loaded
if (targetgrid.readyState == 'complete') {
targetgrid.attachEvent("onrefresh", subGridOnload);
}
else {
targetgrid.onreadystatechange = function applyRefreshEvent() {
var targetgrid = document.getElementById("Worker_Compensation");
if (targetgrid.readyState == 'complete') {
targetgrid.attachEvent("onrefresh", subGridOnload);
}
}
}
subGridOnload();
}
function subGridOnload() {
//debugger;
var grid = Xrm.Page.ui.controls.get('Worker_Compensation')._control;
var sum = 0.00;
if (grid.get_innerControl() == null) {
setTimeout(subGridOnload, 1000);
return;
}
else if (grid.get_innerControl()._element.innerText.search("Loading") != -1) {
setTimeout(subGridOnload, 1000);
return;
}
var ids = grid.get_innerControl().get_allRecordIds();
var cellValue;
for (i = 0; i < ids.length; i++) {
if (grid.get_innerControl().getCellValue('new_estannualpayroll', ids[i]) != "") {
cellValue = grid.get_innerControl().getCellValue('new_estannualpayroll', ids[i]);
cellValue = cellValue.substring(2);
cellValue = parseFloat(cellValue);
sum = sum + cellValue;
}
}
var currentSum = Xrm.Page.getAttribute('new_payrolltotal').getValue();
if (sum > 0 || (currentSum != sum && currentSum != null)) {
Xrm.Page.getAttribute('new_payrolltotal').setValue(sum);
}
}
This piece of code is not working. after i add values in the grid my textbox remains empty!
Thanks in advance
If you are upgrading to Microsoft CRM 2015 soon or are already on Microsoft CRM 2015, you can do this without any JavaScript by simply creating a new calculated rollup field and placing that underneath the sub grid, or wherever you wish to place it on the form. Note that this field is calculated ever 12 hours, but if you wish to, it could be calculated on form load via JavaScript. You can see details about that at https://msdn.microsoft.com/en-us/library/dn817863.aspx -"Calculated and Rollup Attributes". The TechNet document, "Define rollup fields" at https://technet.microsoft.com/library/dn832162.aspx has some good examples, scenarios, and discussion about the limitations of the rollup fields.
You can do it with subgrid's onRefresh. This is also unsupportted way but it works. You must add this functions to your javascript
function AddEventToGridRefresh(gridName, functionToCall) {
// retrieve the subgrid
var grid = document.getElementById(gridName);
// if the subgrid still not available we try again after 1 second
if (grid == null) {
setTimeout(function () {AddEventToGridRefresh(gridName, functionToCall);}, 1000);
return;
}
// add the function to the onRefresh event
grid.control.add_onRefresh(functionToCall);
}
// function used in this example
function AdviseUser() {
alert("Sub-Grid refreshed");
}
For more information, here is the link

Comparing String.Index values

Is it possible to compare two String.Index values in Swift? I'm trying to process a string character by character, and several times I need to check if I am at the end of the string. I've tried just doing
while (currentIndex < string.endIndex) {
//do things...
currentIndex = currentIndex.successor()
}
Which complained about type conversions. Then, I tried defining and overload for < as such:
#infix func <(lhs: String.Index, rhs: String.Index) -> Bool {
var ret = true //what goes here?
return ret
}
Which gets rid of compilation errors, but I have no clue what to do in order to compare lhs and rhs properly. Is this the way I should go about using String.Index, or is there a better way to compare them?
The simplest option is the distance() function:
var string = "Hello World"
var currentIndex = string.startIndex
while (distance(currentIndex, string.endIndex) >= 0) {
println("currentIndex: \(currentIndex)")
currentIndex = currentIndex.successor()
}
Beware distance() has O(N) performance, so avoid it for large strings. However, the entire String class doesn't currently handle large strings anyway — you should probably switch to CFString if performance is critical.
Using an operator overload is a bad idea, but just as a learning exercise this is how you'd do it:
var string = "Hello World"
var currentIndex = string.startIndex
#infix func <(lhs: String.Index, rhs: String.Index) -> Bool {
return distance(lhs, rhs) > 0
}
while (currentIndex < string.endIndex) {
currentIndex = currentIndex.successor()
}
String indexes support = and !=. String indexes are an opaque type, not integers and can not be compared like integers.
Use: if (currentIndex != string.endIndex)
var currentIndex = string.startIndex
while (currentIndex != string.endIndex) {
println("currentIndex: \(currentIndex)")
currentIndex = currentIndex.successor()
}
I believe this REPL/Playground example should illuminate what you (and others) need to know about working with the String.Index concept.
// This will be our working example
let exampleString = "this is a string"
// And here we'll call successor a few times to get an index partway through the example
var someIndexInTheMiddle = exampleString.startIndex
for _ in 1...5 {
someIndexInTheMiddle = someIndexInTheMiddle.successor()
}
// And here we will iterate that string and detect when our current index is relative in one of three different possible ways to the character selected previously
println("\n\nsomeIndexInTheMiddle = \(exampleString[someIndexInTheMiddle])")
for var index: String.Index = exampleString.startIndex; index != exampleString.endIndex; index = index.successor() {
println(" - \(exampleString[index])")
if index != exampleString.startIndex && index.predecessor() == someIndexInTheMiddle {
println("current character comes after someIndexInTheMiddle")
} else if index == someIndexInTheMiddle {
println("current character is the one indicated by someIndexInTheMiddle")
} else if index != exampleString.endIndex && index.successor() == someIndexInTheMiddle {
println("Current character comes before someIndexinTheMiddle")
}
}
Hopefully that provides the necessary information.
Whatever way you decide to iterator over a String, you will immediately want to capture the iteration in a function that can be repeatedly invoked while using a closure applied to each string character. As in:
extension String {
func each (f: (Character) -> Void) {
for var index = self.startIndex;
index < self.endIndex;
index = index.successor() {
f (string[index])
}
}
}
Apple already provides these for C-Strings and will for general strings as soon as they get character access solidified.

Dropdown field - first item should be blank

Using sharepoint build in lookup column and it set to required field. SharePoint automatically selects the first item in the dropdown box (kinda misleading for the end users).
Is there a way to display blank or Null for the first row of this drop down box?
(I am open to any solution. I prefer javascript type solution)
For Choice fields, the default value is configured in the column settings. If the "Default value" input box is populated, delete the value in order to use no default value.
Edit
For Lookup fields, the field seems to change dramatically if it is required. Fields that are NOT required have a "(None)" value by default. However, toggling the field to required will remove the "(None)" value and the first value is selected automatically.
One thing I found, is that if you use JavaScript to add the null value to the dropdown and then try to press OK you get an error page: "An unexpected error has occurred." As a workaround, I wrote some more code to do a quick validation that the field has a value before the form is submitted. If the field has no value, then it will prompt the user and cancel the submit. (Note: this code is only attached to the OK buttons so you may get errors while editing EditForm.aspx.. just choose a value for your lookup field and you'll be able to edit like normal)
Anyways, onto the code... I think the only line you'll need to change is var fieldTitle = 'Large Lookup Field'; to update it to the name of your field.
<script type="text/javascript">
function GetDropdownByTitle(title) {
var dropdowns = document.getElementsByTagName('select');
for (var i = 0; i < dropdowns.length; i++) {
if (dropdowns[i].title === title) {
return dropdowns[i];
}
}
return null;
}
function GetOKButtons() {
var inputs = document.getElementsByTagName('input');
var len = inputs.length;
var okButtons = [];
for (var i = 0; i < len; i++) {
if (inputs[i].type && inputs[i].type.toLowerCase() === 'button' &&
inputs[i].id && inputs[i].id.indexOf('diidIOSaveItem') >= 0) {
okButtons.push(inputs[i]);
}
}
return okButtons;
}
function AddValueToDropdown(oDropdown, text, value, optionnumber){
var options = oDropdown.options;
var option = document.createElement('OPTION');
option.appendChild(document.createTextNode(text));
option.setAttribute('value',value);
if (typeof(optionnumber) == 'number' && options[optionnumber]) {
oDropdown.insertBefore(option,options[optionnumber]);
}
else {
oDropdown.appendChild(option);
}
oDropdown.options.selectedIndex = 0;
}
function WrapClickEvent(element, newFunction) {
var clickFunc = element.onclick;
element.onclick = function(event){
if (newFunction()) {
clickFunc();
}
};
}
function MyCustomExecuteFunction() {
// find the dropdown
var fieldTitle = 'Large Lookup Field';
var dropdown = GetDropdownByTitle(fieldTitle);
if (null === dropdown) {
alert('Unable to get dropdown');
return;
}
AddValueToDropdown(dropdown, '', '', 0);
// add a custom validate function to the page
var funcValidate = function() {
if (0 === dropdown.selectedIndex) {
alert("Please choose a value for " + fieldTitle + ".");
// require a selection other than the first item (our blank value)
return false;
}
return true;
};
var okButtons = GetOKButtons();
for (var b = 0; b < okButtons.length; b++) {
WrapClickEvent(okButtons[b], funcValidate);
}
}
_spBodyOnLoadFunctionNames.push("MyCustomExecuteFunction");
</script>
In response Kit Menke, I've made a few changes to the code so it will persist previous value of the dropdown. I have added the following lines of code to AddValueToDropdown()....
function AddValueToDropdown(oDropdown, text, value, optionnumber){
var selectedIndex
if (oDropdown.options.selectedIndex)
selectedIndex = oDropdown.options.selectedIndex;
else
selectedIndex = -1;
// original code goes here
// changed last line of code (added "selectedIndex+1")
oDropdown.options.selectedIndex = selectedIndex+1;
}
To improve on top of Aaronster's answer: AddValueToDropdown can be done that way:
var injectedBlankValue = false;
function AddValueToDropdown(oDropdown, text, value, optionnumber) {
for (i = 0; i < oDropdown.options.length; i++) {
option = oDropdown.options[i];
if(option.getAttribute('selected')) // If one is already explicitely selected: we skip the whole process
return;
}
var options = oDropdown.options;
var option = document.createElement('OPTION');
option.appendChild(document.createTextNode(text));
option.setAttribute('value', value);
if (typeof (optionnumber) == 'number' && options[optionnumber]) {
oDropdown.insertBefore(option, options[optionnumber]);
}
else {
oDropdown.appendChild(option);
}
// changed last line of code (added 'selectedIndex+1')
oDropdown.options.selectedIndex = 0;
injectedBlankValue = true;
}
This is needed for document libraries where "add" and "set properties" are two distinct pages.
And funcValidate starts with:
var funcValidate = function () {
if (!injectedBlankValue)
return true;
All these changes is to make the whole thing work with document libraries.

Resources