Is this the right way to implement my own filter in twig? (I want *minimal* whitespace between tags, not *no* whitespace between tags) - twig

I know that apply spaceless "isn't about optimisation", in Symfony's words. But dammit, I dislike extraneous whitespace from being in my served files.
So I'm keen to use it.
But... I don't like how it reduces
<span>1</span>
<span>2</span>
to
<span>1</span><span>2</span>
As that results in the browser displaying 12, rather than 1 2.
In my mind whitespace between tags should be reduced to a single space, not to nothing.
<span>1</span>
<span>2</span>
->
<span>1</span> <span>2</span>
So I thought I'd make a custom filter, minimizeWhitespace, and wrap my templates with <% apply minimizeWhitespace %>
This is what I came up with:
function minimizeWhitespace($s)
{
return new \Twig\Markup(preg_replace('/\s+/', ' ', $s->__toString()), 'UTF-8');
}
$TWIG_env->addFilter(
new \Twig\TwigFilter('minimizeWhitespace', 'minimizeWhitespace')
);
This feels messy though - I'm taking their \Twig\Markup object, converting it to a string, running my regexp on it, and then creating a new \Twig\Markup object to return.
Is there a better way?

Related

How to capitalize the first word of a sentence in Ionic 4?

I'm trying to capitalize the first word of a sentence in Ionic 4 INPUT, but doesnt work after make many tests on CSS, ion-text-capitalize.... What is the easiest way to do it? Thank you so much
Writing an ionChange event handler for ion-textarea
I have deleted my previous answer and edited it to some working code.
I personally don't think this is a good solution because these types of things end up buggy or slow.
There is an ion-event that you can write against.
I've written a simple snippet which replaces the first letter with an uppercase letter.
(template-test.page.html)
<ion-content>
<ion-title>Text Area Test</ion-title>
<ion-item>
<ion-textarea [(ngModel)]="someAutoFormattedInput" (ionChange)="onAutoFormatChanged()"></ion-textarea>
</ion-item>
</ion-content>
(template-test.page.ts)
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-textarea-test',
templateUrl: './textarea-test.page.html',
styleUrls: ['./textarea-test.page.scss'],
})
export class TextareaTestPage implements OnInit {
someAutoFormattedInput: string;
constructor() { }
ngOnInit() {
}
onAutoFormatChanged() {
this.someAutoFormattedInput = this.setFirstLetterToUppercase(this.someAutoFormattedInput);
}
private setFirstLetterToUppercase(string:string):string {
// https://dzone.com/articles/how-to-capitalize-the-first-letter-of-a-string-in
return string.charAt(0).toUpperCase() + string.slice(1);
}
}
Why I don't like this
As I said, these types of things end up buggy and laggy. This is both, but I've presented it as it might be "good enough" for your problem.
Buggy - for example, if you put a new first letter in, it will uppercase it, but the original letter in slot two will be uppercase. It's not easy to assume that you should lowercase everything else so writing rules to manage this is... buggy.
What I mean is:
Buggy
Uggy (delete the first letter, works good)
BUggy (add a new first letter, now we have a messy input)
There are probably other edge-cases lurking.
Laggy- This does string operations on the whole input, which could be very long, depending on what you're working with. This has the potential to be expensive and slow.
What I would do
I would let the user enter whatever they want and then when the data is saved I would do this formatting / cleaning up of the data.
Or I would use a custom pipe just to format the data as its being displayed and then store the original user input unaltered.
You can implement it in a different way like below:
1. Via InLine Style:
<p style="text-transform: capitalize;">This will be transformed to capitalize all words, including both parts of this hyphenated word: double-parked.</p>
2. Via CSS class:
.capitalize { text-transform: capitalize; }
<p class="capitalize">This will be transformed to capitalize all words, including both parts of this hyphenated word: double-parked.</p>
Hope this will helps!
Use autocapitalize property of textarea field, use autocapitalize="true", e.g.
<ion-textarea formControlName="description"
rows="5" autocapitalize="true">
It will auto capitalize first letter of every sentence.

node.js - Regex: Put space between triple mustaches ( {{{ => { {{ )

I have a String which contains minified CSS and handlebars.js notations (if statements, variables and so on). Because that the String is minified, it also contains stuff like '{{{' or '}}}' which I need to replace with '{ {{' or '}} }' (put one space between) in order to compile it correctly through handlebars.
Trouble is that I cannot manage to put the correct Regex together for this simple task. I guess the { symbols make the whole thing difficult since its a Regex-specific character.
String:
.class1{{{#if style.textColor}}color:{{style.textColor}};{{/if}}}item-price{{{#if style.showPrice}}display:block;{{else}}display:none;{{/if}}{{#if style.fontSizeItemPrice}}font-size:{{style.fontSizeItemPrice}}px;{{/if}}}
Expected output:
.class1{ {{#if style.textColor}}color:{{style.textColor}};{{/if}} }item-price{ {{#if style.showPrice}}display:block;{{else}}display:none;{{/if}}{{#if style.fontSizeItemPrice}}font-size:{{style.fontSizeItemPrice}}px;{{/if}} }
Simply substituting triple mustaches works, but only for the first occurance:
css = css.replace("{{{", "{ {{");
css = css.replace("}}}", "}} }")
String.replace function automatically converts the first string param to regex (without the global option set). That's why only the first occurrence is replaced. If you want to replace all occurrences of the pattern, you can create a regex with global option set. Try the following snippet.
css = css.replace(/{{{/g, '{ {{').replace(/}}}/g, '}} }')

Manipulate Rebex.MailMessage Html String

I am currently working on a message watcher service we have and my task is to simply embed message details into the message body.
I have tried using a string builder for this however i have found that my message body is a html based string.
I'm wanting to know if there is a way i can add the values i want to add at a certain point in this html string?
Below is a section of the html string i want to manipulate. My text needs to be inserted directly after the body tag.
<body lang=EN-GB link=blue vlink=purple>
<div class=WordSection1>
<p class=MsoNormal>
<span style='font-size:10.0pt;font-family:"Century Gothic","sans-serif";color:black'>Another test for AppendLine();<o:p></o:p>
</span>
</p>
Here is how i was trying to do it:
StringBuilder sb = new StringBuilder();
sb.Append("From: ");
sb.Append(message.From.ToString());
sb.AppendLine();
sb.Append("Sent: ");
sb.Append(message.Date.ToString());
sb.AppendLine();
sb.Append("To: ");
sb.Append(message.To.ToString());
sb.AppendLine();
sb.Append("Subject: ");
sb.Append(message.Subject);
sb.AppendLine();
sb.Append(message.BodyHtml);
Unfortunately this just printed my From, Sent, To, Subject values onto one line and then output the html section.
If any more information is needed please let me know and i will provide it.
Instead of appending the body HTML, try this regex replace:
StringBuilder sb = new StringBuilder();
sb.Append("From: ");
sb.Append(message.From.ToString());
...
sb.Append("Subject: ");
sb.Append(message.Subject);
sb.AppendLine();
// not guaranteed to work with arbitrary HTML strings
Regex regex = new Regex(#"(<body[^>]*>)", RegexOptions.IgnoreCase);
message.BodyHtml = regex.Replace(message.BodyHtml, "$1\r\n" + sb.ToString());
Disclaimer: Please be advised that processing HTML with regular expressions is generally regarded as a bad idea. Although the code above might work in 98% of cases, regular expressions are not up to the task of parsing arbitrary HTML. HTML is too sophisticated for regex. If you need to process arbitrary HTML bodies (not just the one above), I strongly recommend using a proper HTML parser such as HTML Agility Pack - even for seemingly-simple operations such as inserting a text after the body tag.

BBcode parsing problem

I use this function for BBcode parsing:
function bbcode ($message) {
$search = array(
'#\[(?i)b\](.*?)\[/(?i)b\]#si',
'#\[(?i)i\](.*?)\[/(?i)i\]#si',
'#\[(?i)u\](.*?)\[/(?i)u\]#si',
'#\[color=rgb(.*?)\](.*?)\[\/color\]#si',
'#\[quote](.*?)\[\/quote\]#si',
'#\[li](.*?)\[\/li\]#si',
'#\[ul](.*?)\[\/ul\]#si',
);
$replace = array(
'<b>\\1</b>',
'<i>\\1</i>',
'<u>\\1</u>',
'<span style=\"color:rgb\\1\">\\2</span>',
'<span class=\"quote">\\1</span>',
'<li>\\1</li>',
'<ul>\\1</ul>',
);
return preg_replace($search , $replace, $message);
}
In most cases it works ok, but not always.
For example:
[color=rgb(102, 0, 102)]H[color=rgb(204, 0, 0)]e[/color]llo[/color]
The result is:
<span style="color:rgb(102, 0, 102)">H[color=rgb(204, 0, 0)]e</span>llo[/color]
As you can see, only the first [color=...][/color] has been converted to html. The second stays as it is. Any ideas?
It's working correctly as you specified it. The problem is with embedded sequences.
I suggest you perform two replaces. One for the starting tags and one for the ending tags.
You might also be able to get away with specifying all of the starting tags first and
all of the ending tags last in the array of replacements.
That makes the search-replace values simpler anyway and in most cases you don't
need to use back-references, especially for simple tags like [b].
That should fix your problem.

Detect a change in a rich text field's value in SPItemEventReceiver?

I currently have an Event Receiver that is attached to a custom list. My current requirement is to implement column level security for a Rich Text field (Multiple lines of text with enhanced rich text).
According to this post[webarchive], I can get the field's before and after values like so:
object oBefore = properties.ListItem[f.InternalName];
object oAfter = properties.AfterProperties[f.InternalName];
The problem is that I'm running to issues comparing these two values, which lead to false positives (code is detecting a change when there wasn't one).
Exhibit A: Using ToString on both objects
oBefore.ToString()
<div class=ExternalClass271E860C95FF42C6902BE21043F01572>
<p class=MsoNormal style="margin:0in 0in 0pt">Text.
</div>
oAfter.ToString()
<DIV class=ExternalClass271E860C95FF42C6902BE21043F01572>
<P class=MsoNormal style="MARGIN: 0in 0in 0pt">Text.
</DIV>
Problems?
HTML tags are capitalized
Random spaces (see the additional space after margin:)
Using GetFieldValueForEdit or GetFieldValueAsHTML seem to result in the same values.
"OK," you say, so lets just compare the plain text values.
Exhibit B: Using GetFieldValueAsText
Fortunately, this method strips all of the HTML tags out of the value and only plain text is displayed. However, using this method led me to discover additional issues with whitespace characters:
In the before value:
Sometimes there are additional newline characters.
Sometimes spaces are displayed as non-breaking spaces (ASCII char code 160)
Question:
How can I detect if the user changed a rich text field in an event receiver?
[Ideal] Detect any change to HTML or text or white space
[Acceptable] Detect changes to text or white space
[Not so good] Detect changes to text characters only (strip all non-alphanumeric characters)
What happens if you set the ListItem field with the new value and read it back out? Does that give the same formatting?
object oBefore = properties.ListItem[f.InternalName];
properties.ListItem[f.InternalName] = properties.AfterProperties[f.InternalName]
object oAfter = properties.ListItem[f.InternalName];
//dont update
properties.ListItem[f.InternalName] = oBefore;
I would probably try something between choices 2 and 3:
bool changed =
valueAsTextBefore != valueAsTextAfter ||
0 != string.Compare(
oBefore.ToString().Replace(" ", ""),
oAfter.ToString().Replace(" ", ""),
true);
The left half checks if the text (including case) has changed while the right half checks if the tags or attributes have changed. Very kludgy, but should fit your case.
The only other thing I can think of is to run an XML transform on the HTML in order to standardize on case and spacing. But not only does that seem like overkill, but it assumes the HTML will always be well formed.
I'm currently testing a combination approach: GetFieldValueAsText and then stripping out all characters except alphanumeric/punctuation:
static string GetRichTextValue(string value)
{
if (null == value)
{
return string.Empty;
}
StringBuilder sb = new StringBuilder(value.Length);
foreach (char c in value)
{
if (char.IsLetterOrDigit(c) || char.IsPunctuation(c))
{
sb.Append(c);
}
}
return sb.ToString();
}
This only detects changes to the text of a rich text field but seems to work consistently.

Resources