how to make famo.us recalculate sizes? - famo.us

I have a scrollview i'm inserting divs into.
however, the content of these divs are being rendered by javascript templating engine.
when famo.us initially creates this page, it seems it calculate the height of the div content to be zero, so the divs in the scrollview all end up being on top of each other. I'm guessing this is because template rendering happens a couple of ticks later..
is there a way to force famo.us to recalculate/reflow its layout?

I too owe johntraver a beer for his excellent answers on famo.us questions here. This is work derived from one of his previous answers, I use it for surfaces on scrollviews with dynamic html content (and height):
/* AutoSurface.js */
define(function(require, exports, module) {
var Surface = require('famous/core/Surface');
var Entity = require('famous/core/Entity');
function AutoSurface(options) {
Surface.apply(this, arguments);
this.hasTrueSize = false;
this._superCommit = Surface.prototype.commit;
}
AutoSurface.prototype = Object.create(Surface.prototype);
AutoSurface.prototype.constructor = AutoSurface;
AutoSurface.prototype.commit = function commit(context) {
this._superCommit(context);
if (!this.hasTrueSize) {
this.trueHeight = Entity.get(this.id)._currTarget.clientHeight;
this.setSize([undefined,this.trueHeight]);
this.hasTrueSize = true;
}
}
module.exports = AutoSurface;
});
Commit is called every tick so you should be able to wait until you have a height > 0 or what you need before you set hasTrueSize = true.

This is very similar to the answer I just posted here.
how best to create a single scrollable view in famo.us?
You can wrap your true-sized surfaces in a sized modifier. This allows for scrollview to know the actual height of the surfaces within, and thus properly scroll. I am using the _currTarget of surface in the Modifiers sizeFrom method. The ternary operation is to prevent errors pre-deploy of the surface.
Here is that example revised for multiple cells in scrollview.. Hope it helps!
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var RenderNode = require('famous/core/RenderNode');
var Modifier = require('famous/core/Modifier');
var Scrollview = require('famous/views/Scrollview');
var context = Engine.createContext();
var content = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod \
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, \
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo \
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse \
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non \
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
var scrollview = new Scrollview();
var surfaces = [];
scrollview.sequenceFrom(surfaces);
for (var i = 0; i < 10; i++) {
var surface = new Surface({
size:[undefined,true],
content: content,
properties:{
fontSize:'24px',
backgroundColor:'hsl('+(i*360/20)+',100%,50%)'
}
})
surface.pipe(scrollview);
surface.node = new RenderNode();
surface.mod = new Modifier();
surface.mod.sizeFrom(function(){
target = this._currTarget;
return target ? [undefined,target.offsetHeight] : [undefined,true];
}.bind(surface));
surface.node.add(surface.mod).add(surface);
surfaces.push(surface.node);
};
context.add(scrollview);

Instead of hacking deploy of using a sizeFrom-modifier, there is a much easier hack:
Let a surface return it's actual calculated height instead of it's specified height.
Override the default getSize function with this:
surface.getSize = function() { return this._size || this.size }
Full example in a JSFiddle
Warning: This breaks setting the Size with a modifier higher up the rendering tree. (But since you set size explictly anyway, this should not be a problem)

Related

Regex pattern to find matches that start with [vc or [/vc and end with "] or ]

As the title suggests I am looking to build a regex (JS flavour for VSCode find in file) that finds matches that start with [vc_ or [/vc and end with "] or ]. I am attempting to clean up data coming from WP Bakery so it can be imported into the WordPress TinyMCE editor. And example of the exported content is as follows (apologies for the length, but its to give an idea of the strings I am working with):
<content:encoded>
<![CDATA[[vc_row type="in_container" full_screen_row_position="middle" column_margin="default" column_direction="default" column_direction_tablet="default" column_direction_phone="default" scene_position="center" text_color="dark" text_align="left" row_border_radius="none" row_border_radius_applies="bg" overlay_strength="0.3" gradient_direction="left_to_right" shape_divider_position="bottom" bg_image_animation="none"][vc_column column_padding="no-extra-padding" column_padding_tablet="inherit" column_padding_phone="inherit" column_padding_position="all" background_color_opacity="1" background_hover_color_opacity="1" column_shadow="none" column_border_radius="none" column_link_target="_self" gradient_direction="left_to_right" overlay_strength="0.3" width="1/1" tablet_width_inherit="default" tablet_text_alignment="default" phone_text_alignment="default" column_border_width="none" column_border_style="solid" bg_image_animation="none"][vc_column_text]Duis posuere diam eu eleifend tristique. Morbi tincidunt leo sit amet diam ultricies elementum.[/vc_column_text][/vc_column][/vc_row][vc_row type="in_container" full_screen_row_position="middle" column_margin="default" column_direction="default" column_direction_tablet="default" column_direction_phone="default" scene_position="center" text_color="dark" text_align="left" row_border_radius="none" row_border_radius_applies="bg" overlay_strength="0.3" gradient_direction="left_to_right" shape_divider_position="bottom" bg_image_animation="none"][vc_column column_padding="no-extra-padding" column_padding_tablet="inherit" column_padding_phone="inherit" column_padding_position="all" background_color_opacity="1" background_hover_color_opacity="1" column_shadow="none" column_border_radius="none" column_link_target="_self" gradient_direction="left_to_right" overlay_strength="0.3" width="1/1" tablet_width_inherit="default" tablet_text_alignment="default" phone_text_alignment="default" column_border_width="none" column_border_style="solid" bg_image_animation="none"][vc_column_text]<strong>Duis posuere diam eu eleifend tristique. Morbi tincidunt leo sit amet diam ultricies elementum.</strong>
Duis posuere diam eu eleifend tristique. Morbi tincidunt leo sit amet diam ultricies elementum.[/vc_column_text][/vc_column][/vc_row][vc_row type="in_container" full_screen_row_position="middle" column_margin="default" column_direction="default" column_direction_tablet="default" column_direction_phone="default" scene_position="center" text_color="dark" text_align="left" row_border_radius="none" row_border_radius_applies="bg" overlay_strength="0.3" gradient_direction="left_to_right" shape_divider_position="bottom" bg_image_animation="none"][vc_column column_padding="no-extra-padding" column_padding_tablet="inherit" column_padding_phone="inherit" column_padding_position="all" background_color_opacity="1" background_hover_color_opacity="1" column_shadow="none" column_border_radius="none" column_link_target="_self" gradient_direction="left_to_right" overlay_strength="0.3" width="1/1" tablet_width_inherit="default" tablet_text_alignment="default" phone_text_alignment="default" column_border_width="none" column_border_style="solid" bg_image_animation="none"][divider line_type="No Line"][/vc_column][/vc_row][vc_row type="in_container" full_screen_row_position="middle" column_margin="default" column_direction="default" column_direction_tablet="default" column_direction_phone="default" scene_position="center" text_color="dark" text_align="left" row_border_radius="none" row_border_radius_applies="bg" overlay_strength="0.3" gradient_direction="left_to_right" shape_divider_position="bottom" bg_image_animation="none"][vc_column column_padding="no-extra-padding" column_padding_tablet="inherit" column_padding_phone="inherit" column_padding_position="all" background_color_opacity="1" background_hover_color_opacity="1" column_shadow="none" column_border_radius="none" column_link_target="_self" gradient_direction="left_to_right" overlay_strength="0.3" width="1/1" tablet_width_inherit="default" tablet_text_alignment="default" phone_text_alignment="default" column_border_width="none" column_border_style="solid" bg_image_animation="none"][vc_column_text]<strong>Duis posuere diam eu eleifend tristique. Morbi tincidunt leo sit amet diam ultricies elementum.</strong>[/vc_column_text][/vc_column][/vc_row][vc_row type="in_container" full_screen_row_position="middle" column_margin="default" column_direction="default" column_direction_tablet="default" column_direction_phone="default" scene_position="center" text_color="dark" text_align="left" row_border_radius="none" row_border_radius_applies="bg" overlay_strength="0.3" gradient_direction="left_to_right" shape_divider_position="bottom" bg_image_animation="none"][vc_column column_padding="no-extra-padding" column_padding_tablet="inherit" column_padding_phone="inherit" column_padding_position="all" background_color_opacity="1" background_hover_color_opacity="1" column_shadow="none" column_border_radius="none" column_link_target="_self" gradient_direction="left_to_right" overlay_strength="0.3" width="1/1" tablet_width_inherit="default" tablet_text_alignment="default" phone_text_alignment="default" column_border_width="none" column_border_style="solid" bg_image_animation="none"][divider line_type="No Line"][/vc_column][/vc_row][vc_row type="in_container" full_screen_row_position="middle" column_margin="default" column_direction="default" column_direction_tablet="default" column_direction_phone="default" scene_position="center" text_color="dark" text_align="left" row_border_radius="none" row_border_radius_applies="bg" overlay_strength="0.3" gradient_direction="left_to_right" shape_divider_position="bottom" bg_image_animation="none"][vc_column column_padding="no-extra-padding" column_padding_tablet="inherit" column_padding_phone="inherit" column_padding_position="all" background_color_opacity="1" background_hover_color_opacity="1" column_shadow="none" column_border_radius="none" column_link_target="_self" gradient_direction="left_to_right" overlay_strength="0.3" width="1/1" tablet_width_inherit="default" tablet_text_alignment="default" phone_text_alignment="default" column_border_width="none" column_border_style="solid" bg_image_animation="none"][vc_column_text]Duis posuere diam eu eleifend tristique. Morbi tincidunt leo sit amet diam ultricies elementum.[/vc_column_text][/vc_column][/vc_row]]]></content:encoded>
So, I have been attempting various regex to find these 'vc' tags, but what I have been using does not find the end of each tag, it finds the end of the tag for that particular string.
The expression I have been using so far - (\[vc|\[\/vc).*(\"]|\]).
Any help with this would be appreciated.
You may use this regex:
/\[\/?(vc[^\]]*)"?\]/g
RegEx Demo
RegEx Breakup:
\[: Match a [
\/?: Match optional /
(: Start capture group
vc: Match vc
[^\]]*: Match 0 or more of any char that is not ]
): End capture group
"?: Match optional "
\]: Match a ]

Nested wxSizers; stacking panel vertically wrapping content

This is a reference layout (colors only for highlighting the bounds) I created using HTML and CSS. Here is the code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.message {
padding: 5px;
background-color: red;
}
.text-field {
margin: 0;
padding: 5px;
background-color: gray;
color: black;
display: inline-block;
}
.max-size {
max-width: 60%;
}
.align-right {
text-align: right;
}
.align-right p {
text-align: left;
}
</style>
</head>
<body>
<div class="message">
<div class="msg-header">
<p class="text-field max-size">Sender</p>
</div>
<div class="msg-content">
<p class="text-field max-size">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
</div>
</div>
<div class="message">
<div class="msg-header align-right">
<p class="text-field max-size">You</p>
</div>
<div class="msg-content align-right">
<p class="text-field max-size">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
</div>
</div>
</body>
</html>
I try to achieve the same layout using c++ and wxWidgets. Ignoring the 60% max size I tried the following.
MainFrame::MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, wxID_ANY, title, pos, size) {
this->InitialiseComponents();
}
void MainFrame::InitialiseComponents() {
...
this->panelChat = new wxScrolledWindow(this->splitterChatLog, MFID_LIST_BOX_CHAT, wxDefaultPosition, wxSize(850, 100));
this->panelChat->SetBackgroundColour(wxColour(255, 255, 255));
this->panelChat->SetScrollRate(5, 5);
this->sizerChat = new wxBoxSizer(wxVERTICAL);
this->panelChat->SetSizer(this->sizerChat);
...
}
void MainFrame::AddChatMessage(std::string addr, std::string str, bool sent) {
wxPanel *panelMsg = new wxPanel(this->panelChat, wxID_ANY);
panelMsg->SetBackgroundColour(wxColour(255, 0, 0));
wxStaticText *textAddr = new wxStaticText(panelMsg, wxID_ANY, addr);
wxStaticText *textMsg = new wxStaticText(panelMsg, wxID_ANY, str);
wxBoxSizer *sizerMsgHeader = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *sizerMsgContent = new wxBoxSizer(wxVERTICAL);
sizerMsgHeader->Add(textAddr, 0, (sent ? wxALIGN_RIGHT : 0));
sizerMsgContent->Add(textMsg, 0, (sent ? wxALIGN_RIGHT : 0));
wxBoxSizer *sizerMsg = new wxBoxSizer(wxVERTICAL);
sizerMsg->Add(sizerMsgHeader, 0, wxEXPAND);
sizerMsg->Add(sizerMsgContent, 0, wxEXPAND | wxBOTTOM, 5);
panelMsg->SetSizer(sizerMsg);
this->sizerChat->Add(panelMsg, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 5);
}
The problems are:
Calling AddChatMessage multiple times does not stack the messages they lay on top of each other.
Each message panel is a 20x20 red square. It's neither expanding horizontally nor wrapping its content.
I think you just need to add a call to Layout(); or panelChat->Layout(); at the end of the MainFrame::AddChatMessage method. This will force the panelChat window to rearrange all its children according to the sizer flags given to it when the children were added.
However, wxWidgets does offer the wxWebView control. If you already have a chatlog in html format, using that control might be the best way to display it.
You can restrict the text area to 60% on the right or left of the chat window. One way to do this is to use a flexible spacer set to take up 40% of the width and set the message panel to take up the remaining 60%. I think this version of the AddChatMessage should do that.
void MainFrame::AddChatMessage(std::string addr, std::string str, bool sent) {
wxPanel *panelMsg = new wxPanel(this->panelChat, wxID_ANY);
panelMsg->SetBackgroundColour(wxColour(255, 0, 0));
wxStaticText *textAddr = new wxStaticText(panelMsg, wxID_ANY, addr);
wxStaticText *textMsg = new wxStaticText(panelMsg, wxID_ANY, str);
textMsg->Wrap(60 * GetClientSize().GetWidth() / 100);
wxBoxSizer *sizerMsg = new wxBoxSizer(wxVERTICAL);
sizerMsg->Add(textAddr, 0, (sent ? wxALIGN_RIGHT : 0));
sizerMsg->Add(textMsg, 0, (sent ? wxALIGN_RIGHT : 0));
panelMsg->SetSizer(sizerMsg);
// Create a new horizontal sizer and add a spacer either before or after
// taking up 40% of the horizontal space.
wxBoxSizer* newChatSizer = new wxBoxSizer(wxHORIZONTAL);
if ( sent )
{
newChatSizer->AddStretchSpacer(2);
}
newChatSizer->Add(panelMsg, 3, wxEXPAND);
if ( !sent )
{
newChatSizer->AddStretchSpacer(2);
}
this->sizerChat->Add(newChatSizer, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 5);
panelChat->Layout();
}
In this case this adds a space (either on the left or right) with a proportion of 2. The the message panel is added with a proportion of 3. Since the total proportion is 5, the spacer is given 2/5=40% of the space leaving the remainging 60% of the space for the message.
Note: I also removed the textAddr and textMsg sizers and instead just added the static texts to the sizerMsg. Usually a sizer containing only one item isn't necessary, and I didn't see what those two sizers were doing.

How to filter out list elements that are indexed after an element that fullfills a condition?

I have this list:
var ls = List("Should be ignored", "X", "test", "test1", "X", "test2")
I was wondering if someone knows how I could filter out the elements in the list which are right after the "X" so that the result is:
List("test", "test2")
So far I have tried:
ls.filter(x => x._2.equals("X"))
However that simply bring back this result:
List((0,This line should be ignored, because it is before the first predicate), (2,Lorem ipsum dolor sit amet, consectetur adipiscing elit.), (3,Suspendisse aliquet quis ligula nec tristique.), (5,Donec augue ipsum, mattis et elit vel, convallis convallis dui.), (7,In hac habitasse platea dictums.))
You can use sliding to get an Iterator of every tuple of consecutive elements, and then select the second elements from the tuples in which "X" is the first element:
val items = List("Should be ignored", "X", "test", "test1", "X", "test2")
items
.sliding(2)
.collect { case List("X", item) => item }
.toList
// res0: List[String] = List(test, test2)
You might create the new list by unfolding over the given list.
var ls = List("Should be ignored", "X", "test", "test1", "X", "test2")
List.unfold(ls){state =>
val ns = state.dropWhile(_ != "X").drop(1)
Option.when(ns.nonEmpty)(ns.head->ns.tail)
}
//res0: List[String] = List(test, test2)
Should work no matter how many "X" elements there are.
But what if the input is List("A","X","X","X","B")? Should the result be List("X","B") or just List("B")? Right now my suggested solution produces the former but it can produce the latter after a very simple modification.
I believe the simples solution would be this:
def filterAfterX(data: List[String]): List[String] =
(data lazyZip data.tail).collect {
case ("X", word) => word
}.toList

Swift - Add attributed string to items in parenthesis

I'm trying to display text to a view from a set of definitions and in cases where that text/string has parenthesis, I would like to display that parenthesis element in a different way - let's say bolded vs non bolded.
"This string a"
"This string b (has parenthesis)" - parenthesis show in lower
weight font
Now I'm aware that the solution is found by combining regular expressions - \(\w*\) - with attributed strings, but I haven't been able to combine it meaningfully.
This is my function that prints the words
func setWord(_ index:Int) {
if (index < 0) { return }
let word:[String:AnyObject] = self.definitions[index]
if let wordLabelText = word[self.store.sourceLanguage.lowercased()] as? String {
self.wordLabel.attributedText = NSMutableAttributedString(string: wordLabelText, attributes: [NSKernAttributeName: 1.0])
}
print(word)
self.definitionView.word = word
}
What I've done so far is added some more definitions for the attributed strings output, but then I'm not sure how to continue:
func setWord(_ index:Int) {
if (index < 0) { return }
let font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)
let fontSize = font.pointSize * 3
let plainFont = UIFont(name: "X-BoldItalic", size: fontSize)
let boldFont = UIFont(name: "X-Italic", size: fontSize)
let word:[String:AnyObject] = self.definitions[index]
if let wordLabelText = word[self.store.sourceLanguage.lowercased()] as? String {
self.wordLabel.attributedText = NSMutableAttributedString(string: wordLabelText, attributes: [NSKernAttributeName: 1.0, NSFontAttributeName: boldFont])
Here I'd need to loop through the words looking for the (elements), but I'm not sure how to do that and properly return the words. Am I on the right path? Thanks a bunch :)
A solution in Objective-C, with explained logic that should be easily translated in Swift.
NSDictionary *defaultAttributes = #{NSFontAttributeName: [UIFont boldSystemFontOfSize:15]};
NSDictionary *otherAttributes = #{NSFontAttributeName: [UIFont systemFontOfSize:12]};
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"\\(.*?\\)" options:0 error:nil];
NSString *initialString = #"This string a (has parenthesis), This string b (has parenthesis too), This string C hasn't.";
NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:initialString attributes:defaultAttributes];
NSLog(#"Attr: %#", attr);
NSArray *allMatches = [regex matchesInString:[attr string] options:0 range:NSMakeRange(0, [attr length])];
for (NSTextCheckingResult *aResult in allMatches)
{
[attr addAttributes:otherAttributes range:[aResult range]];
}
NSLog(#"Attr: %#", attr);
Logs:
$> Attr: This string a (has parenthesis), This string b (has parenthesis too), This string C hasn't.{
NSFont = "<UICTFont: 0x14663df0> font-family: \".SFUIText-Semibold\"; font-weight: bold; font-style: normal; font-size: 15.00pt";
}
$> Attr: This string a {
NSFont = "<UICTFont: 0x14663df0> font-family: \".SFUIText-Semibold\"; font-weight: bold; font-style: normal; font-size: 15.00pt";
}(has parenthesis){
NSFont = "<UICTFont: 0x14666260> font-family: \".SFUIText\"; font-weight: normal; font-style: normal; font-size: 12.00pt";
}, This string b {
NSFont = "<UICTFont: 0x14663df0> font-family: \".SFUIText-Semibold\"; font-weight: bold; font-style: normal; font-size: 15.00pt";
}(has parenthesis too){
NSFont = "<UICTFont: 0x14666260> font-family: \".SFUIText\"; font-weight: normal; font-style: normal; font-size: 12.00pt";
}, This string C hasn't.{
NSFont = "<UICTFont: 0x14663df0> font-family: \".SFUIText-Semibold\"; font-weight: bold; font-style: normal; font-size: 15.00pt";
}
What is the idea:
Create a NSMutableAttributedString with the "default" attributes (in our case bold font with "big size").
Then create a NSRegularExpression and find all occurrences.
You'll add the attributes (in our case small and normal font) at that occurrence range.
In our case, it works simply, because since you can only have one attribute per kind maximum at a specific range, the NSFontAttributeName attribute will be replaced for that range.
If you added more attributes, and want to remove them, you may need to not call addAttributes:range:, but replaceCharactersInRange:withAttributedString: instead:
NSAttributedString *replacement = [[NSAttributedString alloc] initWithString:[[attr string] substringWithRange:[aResult range]]
attributes:otherAttributes];
[attr replaceCharactersInRange:[aResult range] withAttributedString:replacement];
Edit: Swift 3 Version
Nota Bene: I'm clearly not a Swift Developer, this code seems to work, but clearly, I "write" Swift like I write Objective-C, and many things since I don't use them daily and didn't read the doc are wrongly done (like the conversion/cast/explicit type/class, the "as", the "!", the "?", etc.), but it could be a start for you.
If you are a Swift developer and spots issues, feel free to comment the post and suggest your modifications. If you're just here because you have the same issue, don't forget to read in the comment if there are more Swifty things to fix in my pseudo code.
let defaultAttributes:[String:Any] = [NSFontAttributeName:UIFont.boldSystemFont(ofSize:15)];
let otherAttributes:[String:Any] = [NSFontAttributeName:UIFont.systemFont(ofSize:12)];
do {
let regex = try NSRegularExpression.init(pattern: "\\(.*?\\)", options: [])
let initialString:String = "This string a (has parenthesis), This string b (has parenthesis too), This string C hasn't."
let attr = NSMutableAttributedString.init(string: initialString, attributes: defaultAttributes)
print("Attr\(attr)");
let allMatches:[NSTextCheckingResult] = regex.matches(in: attr.string, options:[], range: NSRange(location: 0, length: attr.string.characters.count))
for aResult in allMatches
{
let occurrence = (attr.string as NSString).substring(with: aResult.range)
let replacement = NSAttributedString.init(string: occurrence , attributes: otherAttributes)
attr.replaceCharacters(in: aResult.range, with: replacement)
//attr.addAttributes(otherAttributes, range: aResult.range)
}
print("Attr\(attr)");
} catch let regexError {
print(regexError)
}

Using regex to extract a string

I am using Google Apps Script to return a text string from simple comments entity in a database into Google Sheets. I would like to identify certain comments that contain my own 'bbCode' such as [Status: Complete]. But I am not sure how to extract the 'Complete' text and how to remove the entire '[Status: Complete]' bbCode from the comment text. This is where I have got to so far - thank you for any suggestions:
Example 1 comment text: '[Status: Complete] Lorem ipsum bla bla'
Desired output: Col1: 'Lorem ipsum bla bla' Col2: 'Complete'
Example 2 comment text: 'Lorem [Status: Proposed] ipsum bla bla'
Desired output: Col1: 'Lorem ipsum bla bla' Col2: 'Proposed'
Example 3 comment text: 'Lorem ipsum bla bla'
Desired output: Col1: 'Lorem ipsum bla bla' Col2:
var bbCode = '[Status: ";
//get data from db and for next into val
var val = rs.getString(col+1);
if (val.indexOf(bbCode) > -1) {
// Item with Status, place the status in the Status Column
//but this next line is not right - I would like var Status = 'Complete' or 'Proposed' etc...
var Status = RegExp(.*\[Status: (.*)\].*', 'g');
cell.offset(row, col+2).setValue(Status);
// Place the remaining comment in the Comment column
//This next line is not right I would like val = the comment string without the '[Status: Completed]' or '[Status: Proposed]' etc...
cell.offset(row, col+2).setValue(val);
} else {
// Enter comment in Comment column as normal
cell.offset(row, col+1).setValue(val);
}
Try this sample:
function RegexGetTwoStrings() {
Logger.log('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^');
var sample = ['[Status: Complete] Lorem ipsum bla bla',
'Lorem [Status: Proposed] ipsum bla bla',
'Lorem ipsum bla bla'];
var RegEx = /(.*)\[.*: (.*)\] (.*)/;
var Replace = "$1$3,$2";
var str, newStr, Col1, Col2;
var strArr = [];
for (var i = 0; i < sample.length; i++) {
str = sample[i];
newStr = str.replace(RegEx, Replace) + ",";
strArr = newStr.split(',');
Col1 = strArr[0];
Col2 = strArr[1];
Logger.log("Sample1 = '" + str + "'");
Logger.log('Col1 = ' + Col1);
Logger.log('Col2 = ' + Col2);
}
Logger.log('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^');
}