how to properly parse paired html tags? - rebol

the question is about parsing an html stream obtained by load/markup in a way you can get html tags constituent parts, i.e. when you find
<div id="one">my text</div>
you should end with something like <div id="one">, {my text} and </div> in the same container, something like
[<div id="one"> {my text} </div>]
or even better
[<div> [id {one}] {my text} </div>]
the parsing problem is matching paired html tags, in html a tag may be an empty tag with maybe attributes but without content and thus without an ending tag or a normal tag maybe with attributes and content and so an ending tag, but both types of tag are just a tag
I mean when you find a sequence like <p>some words</p> you have a P tag just the same you get whit a sequence like <p /> just a P tag, in first case you have associated text and ending tag and in the latter you don't, that's all
In other words, html attributes and content are properties of tag element in html, so representing this in json you will get someting like:
tag: { name: "div" attributes: { id: "one } content: "my text" }
this means you have to identify content of a tag in order to assign it to properly tag, which in terms of rebol parse means identifing matching tags (opening tag and ending tag)
In rebol you can easy parse an html sequence like:
<div id="yo">yeah!</div><br/>
with the rule:
[ some [ tag! string! tag! | tag! ]]
but with this rule you will match the html
<div id="yo">yeah!</div><br/>
and also
<div id="yo">yeah!</p><br/>
as being the same
so you need a way to match the same opening tag when appearing in ending position
sadly rebol tags cannot (AFAIK) be parametrized with tag name, so you cannot say something like:
[ some [ set t1 tag! set s string! set t2 tag!#t1/1 | tag! ] ]
the t1/1 notation is due to a (bad) feature of rebol including all tag atributes at same level of tag name (another bad feature is not reckognizing matching tags as being the same tag)
Of course you can achieve the goal using code such as:
tags: copy []
html: {<div id="yo">yeah!</p><br/>}
parse html [ some [ set t1 tag! set s string! set t2 tag! (tag: first make block! t1 if none <> find t2 tag [append/only tags reduce [t1 s] ]) | tag! (append/only tags reduce [t1])]]
but the idea is to use a more elegant and naive approach using parse dialect only

There's a way to parse pairs of items in rebol parse dialect, simply using a word to store the expected pair:
parse ["a" "a"] [some [set s string! s ]]
parse ["a" "a" "b" "b"] [some [set s string! s ]]
But this doesn't work well with tags due to tags carry attributes and special ending marks (/) and thus it's not easy to find the ending pair from initial one:
parse [<p> "some text" </p>] [some [ set t tag! set s string! t ]
parse [<div id="d1"> "some text" </div>] [some [ set t tag! set s string! t ]
don't work cause </p> is not equal to <p> and neither </div> is equal to <div id="d1">
Again you can fix it with code:
parse load/markup "<p>preug</p>something<br />" [
some [
set t tag! (
b: copy t remove/part find b " " tail b
insert b "/"
)
set s string!
b (print [t s b])
|
tag!
|
string!
]
]
but this is not simple and zen code anymore, so question's still alive ;-)

Related

Vuetify remove brackets when there is no data in JSON v-data-table

I have a list of data and for some of the objects in the data the tag key is empty. When plotting the data in the table they data which has tag key is plotted correctly but for the object which doesn't have the tag key it is being displayed as []. How do I remove that ?
<template v-slot:[`item.tags`]="item">
{{item.tags}}
</template>
Data Sample for a data with tag
{"_id": {"$oid": "60c057823e2a3a534cb1b412"},
"tags": "MMM",
"DateModified": "2021-06-09 13:54:57"}
Data Sample for a data without tag
{"_id": {"$oid": "60c057823e2a3a534cb1b412"},
"tags": [],
"DateModified": "2021-06-09 13:54:57"}
Since you different type of data for each of case when you have data and when you don't have data.
Better go with this
<template v-slot:[`item.tags`]="item">
{{item.tags.length === 0 ? '' : item.tags}}
</template>
If your tags field is an array, you do not want to directly call {{tags}} in your template. You should probably have something like
<span v-for="(tag, i) in tags" :key="i">{{tag}}</span>
You have a problem with your default tags
If i can contain at most 1 tags it should be an empty String instead of an empty Array
your data Sample for a data without tag
{
"_id": {"$oid": "60c057823e2a3a534cb1b412"},
"tags": '',
"DateModified": "2021-06-09 13:54:57"
}

How can i break line html text in Elm

I have a text like this
Special menu:\nFrenchfire 1ea\nHamborger 2ea\nMeatball 1ea
and i want to breakline it in Elm with Html tag such as pre or span
and it should show like this
Special menu:
Frenchfire 1ea
Hamborger 2ea
Meatball 1ea
i have no idea to concat br[][] in string if it is an Elm
but if javascript i can replace string like replace(/\n/g,'<br/>') and it done.
You mentioned a <pre> tag, so let's try that first:
import Html exposing (pre, text)
pre []
[ text "Special menu:\nFrenchfire 1ea\nHamborger 2ea\nMeatball 1ea" ]
Renders as:
Special menu:
Frenchfire 1ea
Hamborger 2ea
Meatball 1ea
Or you could split the string into individual lines. String.split accomplishes this:
String.split "\n" "Special menu:\nFrenchfire 1ea\nHamborger 2ea\nMeatball 1ea"
Even better, as kaskelotti aptly suggested:
String.lines "Special menu:\nFrenchfire 1ea\nHamborger 2ea\nMeatball 1ea"
Use List.map to transform the Strings into Html and List.intersperse to insert <br /> tags between them:
import Html exposing (br, text)
List.intersperse (br [] [])
(List.map text
(String.lines "Special menu:\nFrenchfire 1ea\nHamborger 2ea\nMeatball 1ea")
)
You end up with a list of Html elements. Done!
See it rendered here.
Or, if you prefer, use an itemized list:
import Html exposing (Html, div, h3, li, text, ul)
listItem : String -> Html msg
listItem content =
li [] [ text content ]
main : Html msg
main =
let
split =
String.lines "Special menu:\nFrenchfire 1ea\nHamborger 2ea\nMeatball 1ea"
headline =
Maybe.withDefault "Items:" (List.head split)
items =
Maybe.withDefault [] (List.tail split)
in
div []
[ h3 [] [ text headline ]
, ul [] (List.map listItem items)
]
See it rendered here.
String.replace takes input of type String and returns a String. List.map requires an input as List of Strings i.e. List String as the error states.
You can solve this by using String.split or String.lines. Both will take a String as input and return a List of String, which is what List.map requires.
The difference is that String.lines is a built-in function meant for splitting the String with newline chars (\n) and String.split is a general purpose utility for splitting a String by any delimiter.
For this particular case, I'd go with String.lines, since the name documents its purpose already. Presence of String.split is good to know for future cases.

escape backslashes in dataweave

I have a string array in properties file, and I want to read its value in dataweave in JSON format.
The array in properties file is-
Countries = ["USA","England","Australia"]
in dataweave, I am using this-
%output application/json
---
{
countries: p('Countries')
}
Output I am getting is-
"countries": "[\"USA\",\"England\",\"Australia\"]",
Output I want is-
"countries": [
"USA",
"England",
"Australia"
]
I have tried with replace but no luck.
I also tried countries map $ as String after changing country array to Countries = ['USA','England','Australia'] but it says Invalid input 'S', expected :type or enclosedExpr
How to achieve this?
The problem is that properties file values are strings and not arrays so your expression is not interpreted. But don't worry you can use the read function
read(p('Countries'), "application/json"))

Transform Html DOM

I am new to Elm and I really love it so far, but I've run into a problem that I cannot seem to wrap my head around.
I have an Html DOM, for example
div []
[ h1 [] [text "Headline 1"]
, p [] [text "Some text"]
, h2 [] [text "Headline 2"]
]
I would like to add a-links inside each h[1-6] element and so transform it to something like (keeping it simple)
div []
[ h1 [] [ text "Headline 1"
, [a [name "headline"] [text "#"]
]
, p [] [text "Some text"]
, h2 [] [text "Headline 2"
, [a [name "headline"] [text "#"]
]
]
This is conceptually not very hard. Look through the DOM, if element is h[1-6] add an a-link as child element. However my understanding of Elm is not well enough to get it to work.
Here is what I've been trying so far.
transform : Html a -> Html a
transform node =
-- check if the tag is h1-h6
case node.tag of
-- add a-link to h1 children
"h1" -> { node | children = (a [name "headline"] [text "#") :: node.children }
"h2" -> { node | children = (a [name "headline"] [text "#") :: node.children }
-- do this for all nodes in the tree
_ -> { node | children = List.map transform node.children }
This doesn't work.
The type annotation for `transform` does not match its definition.
40| transform : Html a -> Html a
^^^^^^^^^^^^^^^^
The type annotation is saying:
VirtualDom.Node a -> VirtualDom.Node a
But I am inferring that the definition has this type:
{ b | tag : String, children : List (Html a) }
-> { b | children : List (Html a), tag : String }
I understand that I can't do node.tag because the generic type a might not have that field. It wouldn't be type safe. For example the text node doesn't have a tag field, but is still an instance of Html.Html a.
> text "Hello World"
{ type = "text", text = "Hello World" } : Html.Html a
My question is, how can I do this? Can I do this? or shouldn't I be doing this?
It is not possible to modify existing values of Html msg type.
They are final internal structures, which are rendered by Virtual DOM in to actual HTML Nodes as an output of your program.
Html msg is an alias for VirtualDom.Node a
You are attempting to use them as Records, but that's just a JavaScript object.
Elm REPL outputs String presentation of an abstract data structure here:
> text "Hello World"
{ type = "text", text = "Hello World" } : Html.Html a -- not a record
Instead of attempting to transform Html msg -> Html msg, you should try something like:
-- Input example: [ "#", "http://google.com/", "http://package.elm-lang.org/" ]
linksView : List String -> Html msg
linksView links =
links
|> List.map (\link -> a [ href link ] [ text link ])
|> div [] -- Expected output: <div> with thre links
In Elm, Html a is really only useful as output. You're never going to use it as input in the way that your transform function is attempting.
You will be better served by creating a model to describe your domain, then passing that to a view function to render html.
type alias Article =
{ priority : Priority
, headline : String
, body : String
}
type alias Model =
List Article
type Priority = First | Second
Your view could then look something like this:
view : Model -> Html msg
view =
div [] << List.map viewArticle
viewArticle : Article -> Html msg
viewArticle article =
let
priorityTag =
case article.priority of
First -> h1
Second -> h2
in
div []
[ priorityTag []
[ text article.headline
, a [ name "headline" ] [ text "#" ]
]
, p [] [ text article.body ]
]

How do I get the values of all the fields of a form?

I have a HTML form like this in a client side Amber solution
<form id="myForm1">
Creator: <input type="text" name="creator" />
<br>
Title: <input type="text" name="title" />
<br>
Description: <input type="text" name="description" />
<br>
Doctype: <input type="text" name="doctype" />
<br>
Tags: <input type="text" name="tags" />
</form>
Question
How do I iterate through all of the fields of the form in order to put the content of the fields into a Amber dictionary with the field name as key and the text content as value?
New version of the question after answer by Stephen-Eggermont and MKroenert
How do I get the values of all the fields of the form in order to put them into an Amber dictionary with the field name as key and the text content as value?
Or is there an idiomatic way to create a form and retrieve the values?
Note: The form may be constructed with Amber code if this makes things more readable.
References
https://github.com/amber-smalltalk/amber/wiki/FAQ#how-do-i-get-the-text-value-of-an-input-field
http://api.jquery.com/each/
Edit after answer: FileIn code
The answer provided by MKroenert works fine
Below is his code I tested. It may be filed in directly in a workspace
Widget subclass: #AmberFormExample
instanceVariableNames: 'dictionary inputs'
package: 'TodoList'!
!AmberFormExample methodsFor: 'not yet classified'!
collectValues
inputs do: [ :each |
dictionary at: (each asJQuery attr: 'name')
put: (each asJQuery val).
].
Transcript show: dictionary printString
!
initialize
dictionary := Dictionary new.
inputs := Array new.
!
renderInput: inputName on: html
html p: [
html label with: inputName.
inputs add: (html input id: inputName;
name: inputName;
yourself)]
!
renderOn: html
inputs removeAll.
html form id: 'myForm1'; with: [
#('Creator' 'Title' 'Description' 'Doctype' 'Tags') do: [ :each |
self renderInput: each on: html]].
html button
with: 'Collect Inputfield Values';
onClick: [
self collectValues.
]
! !
I reused the code from this SO question and rewrote it in Amber to address the first part of your question.
Here is how you iterate over all input fields:
(('#myForm1 *' asJQuery)
filter: ':input')
each: [ :thisArg :index |
console log: thisArg ] currySelf
This Amber recipe is required to get access to the JavaScript this.
Printing both name and value of the input fields to the JavaScript console can be done like this:
(('#myForm1 *' asJQuery)
filter: ':input')
each: [ :thisArg :index |
console log: (thisArg asJQuery attr: 'name').
console log: (thisArg asJQuery val)] currySelf
Putting the values into a dictionary:
| dict |
dict := Dictionary new.
(('#myForm1 *' asJQuery)
filter: ':input')
each: [ :thisArg :index |
dict at: (thisArg asJQuery attr: 'name')
put: (thisArg asJQuery val)] currySelf
As for the second part of your question, there is the Web package in Amber which contains Classes for generating HTML pages.
What you do is to create a subclass of Widget and implement the renderOn: html method.
The object passed in as html parameter is of type HTMLCanvas and can be used to create an HTML form like this:
renderOn: html
html form with: [
html input id: 'creator'.
html input id: 'title'.]
Here is a complete example.
Take it as a starting point and be aware that it may not be the most efficient way of doing things
Widget subclass: #AmberFormExample
instanceVariableNames: 'dictionary inputs'
package: 'Examples'
AmberFormExample>>initialize
dictionary := Dictionary new.
inputs := Array new.
AmberFormExample>>renderOn: html
inputs removeAll.
html form id: 'myForm1'; with: [
#('Creator' 'Title' 'Description' 'Doctype' 'Tags') do: [ :each |
self renderInput: each on: html]].
html button
with: 'Collect Inputfield Values';
onClick: [
self collectValues.
]
AmberFormExample>>renderInput: inputName on: html
html p: [
html label with: inputName.
inputs add: (html input id: inputName;
name: inputName;
yourself)]
AmberFormExample>>collectValues
inputs do: [ :each |
dictionary at: (each asJQuery attr: 'name')
put: (each asJQuery val).
].
After implementing this class in a running Amber instance the following code can be used to execute it:
AmberFormExample new appendToJQuery: 'body' asJQuery
There is a lot of duplication in your form. You might want to take a look at HTMLCanvas and how it is used in IDE.
You could add a method renderField: aFieldName on: aCanvasand reuse that 5 times. Did you take a look at Seaside?
The end result should be something like
renderOn: html
html form with: [
#('Creator' 'Title' 'Description' 'Doctype' 'Tags') do: [:each |
self renderField: each on: html]