Reading HTML page using Libreoffice Basic - html

I'm new to LibreOffice Basic. I'm trying to write a macro in LibreOffice Calc that will read the name of a noble House of Westeros from a cell (e.g. Stark), and output the Words of that House by looking it up on the relevant page on A Wiki of Ice and Fire. It should work like this:
Here is the pseudocode:
Read HouseName from column A
Open HtmlFile at "http://www.awoiaf.westeros.org/index.php/House_" & HouseName
Iterate through HtmlFile to find line which begins "<table class="infobox infobox-body"" // Finds the info box for the page.
Read Each Row in the table until Row begins Words
Read the contents of the next <td> tag, and return this as a string.
My problem is with the second line, I don't know how to read a HTML file. How should I do this in LibreOffice Basic?

There are two mainly issues with this.
1. Performance
Your UDF will need get the HTTP resource in every cell, in which it is stored.
2. HTML
Unfortunately there is no HTML parser in OpenOffice or LibreOffice. There is only a XML parser. Thats why we cannot parse HTML directly with the UDF.
This will work, but slow and not very universal:
Public Function FETCHHOUSE(sHouse as String) as String
sURL = "http://awoiaf.westeros.org/index.php/House_" & sHouse
oSimpleFileAccess = createUNOService ("com.sun.star.ucb.SimpleFileAccess")
oInpDataStream = createUNOService ("com.sun.star.io.TextInputStream")
on error goto falseHouseName
oInpDataStream.setInputStream(oSimpleFileAccess.openFileRead(sUrl))
on error goto 0
dim delimiters() as long
sContent = oInpDataStream.readString(delimiters(), false)
lStartPos = instr(1, sContent, "<table class=" & chr(34) & "infobox infobox-body" )
if lStartPos = 0 then
FETCHHOUSE = "no infobox on page"
exit function
end if
lEndPos = instr(lStartPos, sContent, "</table>")
sTable = mid(sContent, lStartPos, lEndPos-lStartPos + 8)
lStartPos = instr(1, sTable, "Words" )
if lStartPos = 0 then
FETCHHOUSE = "no Words on page"
exit function
end if
lEndPos = instr(lStartPos, sTable, "</tr>")
sRow = mid(sTable, lStartPos, lEndPos-lStartPos + 5)
oTextSearch = CreateUnoService("com.sun.star.util.TextSearch")
oOptions = CreateUnoStruct("com.sun.star.util.SearchOptions")
oOptions.algorithmType = com.sun.star.util.SearchAlgorithms.REGEXP
oOptions.searchString = "<td[^<]*>"
oTextSearch.setOptions(oOptions)
oFound = oTextSearch.searchForward(sRow, 0, Len(sRow))
If oFound.subRegExpressions = 0 then
FETCHHOUSE = "Words header but no Words content on page"
exit function
end if
lStartPos = oFound.endOffset(0) + 1
lEndPos = instr(lStartPos, sRow, "</td>")
sWords = mid(sRow, lStartPos, lEndPos-lStartPos)
FETCHHOUSE = sWords
exit function
falseHouseName:
FETCHHOUSE = "House name does not exist"
End Function
The better way would be, if you could get the needed informations from a Web API that would offered from the Wiki. You know the people behind the Wiki? If so, then you could place this there as a suggestion.
Greetings
Axel

Related

Create Outlook email with an #Mention for a contact in the body

I'm generating an Outlook email, which has an HTML body, via Excel VBA.
I would like to # a contact in my organisation in the body. e.g. #Bloggs, Joe
Mention is a functionality added to Outlook in the last few years and it seems it's an HTML link. How do I ensure Outlook formats this mention correctly?
Would the below suffice for Outlook to recognise this as a mention?
<a href="mailto:Joe.Bloggs#MyOrg.com">
<span style='font-family:"Arial",sans-serif;mso-no-proof:yes;text-decoration:none; text-underline:none'>#Bloggs, Joe</span>
The trick seems to be in the id of the anchor tag:
Each ID needs to start with OWAAM (I suspect it stands for "Outlook Web Access At Mention", as that's where the feature originated), followed by a 32 character hex value, followed by Z.
Each ID needs to be unique in the document, but doesn't have to be unique for the mention, so you can just generate a new one for each mention.
For instance:
<a id="OWAAM954ABFF4E42A42989C2192D3F93BB32DZ"
href="mailto:immo#example.com">Immo Landwerth</a>
This code works for me (C#):
static string GetMentionHtml(string email, string name)
{
var guid = Guid.NewGuid().ToString("N").ToUpper();
var id = $"OWAAM{guid}Z";
return = $"<a id=\"{id}\" href=\"mailto:{email}\">#{name}</a>";
}
And in case you need it in VBA:
Function GetMentionHtml(email As String, name As String) As String
Dim id As String
Dim html As String
id = "OWAAM" & NewGuid() & "Z"
html = "<a id=""" & id & """ href=""mailto:" & email & """>#" & name & "</a>"
GetMentionHtml = html
End Function
' Sadly there is no "get me a GUID" in VBA, but this is good enough.
' Source: https://www.codegrepper.com/code-examples/vb/excel+vba+generate+guid+uuid
Function NewGuid() As String
Dim k&, h$
NewGuid = Space(36)
For k = 1 To Len(NewGuid)
Randomize
Select Case k
Case 9, 14, 19, 24: h = "-"
Case 15: h = "4"
Case 20: h = Hex(Rnd * 3 + 8)
Case Else: h = Hex(Rnd * 15)
End Select
Mid$(NewGuid, k, 1) = h
Next
NewGuid = Replace(NewGuid, "-", vbNullString, Compare:=vbTextCompare)
End Function

ASP with VBScript times out while reading a file line by line [duplicate]

I can't get the following function in VBScript to work. I am trying to get all of the files in a folder and loop through them to get the highest numbered file. (file name format is log_XXX.txt) The problem that I am having is that the code never enters my For Each loop. I am new to VBScript, but I don't seem to understand why this won't work.
Function GetFileNumber(folderspec)
Dim fso, f, f1, fc, s, tempHighNum
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.GetFolder(folderspec)
WScript.Echo f.Files.Count : rem prints 3
Set fc = f.Files
WScript.Echo fc.Count : rem prints 3
Set tempHighNum = "000"
For Each f1 in fc
WScript.Echo f1.Size : rem does not print
WScript.Echo f1.Type : rem does not print
WScript.Echo f1.Name : rem does not print
s = Right(f1.name,3)
IF NOT(ISNULL(s)) THEN
IF (s > tempHighNum) THEN
tempHighNum = s
END IF
END IF
Next
GetFileNumber = tempHighNum
End Function
Change this line:
Set tempHighNum = "000"
to the following:
tempHighNum = "000"
You are attempting to set the tempHighNum variable to a string type. Therefore, you should not use the Set keyword. Set is only needed when assigning object types to variables.
I am not sure how your script works so I put this HTML application together for you. It uses a batch file called Dir.Bat located in C:\Batch which makes a file called Data.Txt located in c:\Temp. Then the script takes
over. The script reads the file Data.Txt line by line. As each line is read two split statements are used to separate out the string in the text file's name. After that I collect those strings containing numbers into the variable ListCol as I test for larger and larger numbers. I finally wind up with the largest number which I place in your original variable tempHighNum. I will post the HTA file and the Dir.Bat file. I know I did not write the script as a function using a parameter so if you really need to use a parameter, I will try to help you by changing the HTA file to make it possible to enter the path and file name in a TextBox. That should make it easy to
change and use. I added and changed a thing or two to make it run smoother.
I am not sure how your script works so I put this HTML application together for you. It uses a batch file called Dir.Bat located in C:\Batch which makes a file called Data.Txt located in c:\Temp. Then the script takes
over. The script reads the file Data.Txt line by line. As each line is read two split statements are used to separate out the string in the text file's name. After that I collect those strings containing numbers into the variable ListCol as I test for larger and larger numbers. I finally wind up with the largest number which I place in your original variable tempHighNum. I will post the HTA file and the Dir.Bat file. I know I did not write the script as a function using a parameter so if you really need to use a parameter, I will try to help you by changing the HTA file to make it possible to enter the path and file name in a TextBox. That should make it easy to
change and use. I added and changed a thing or two to make it run smoother.
<HTML><!-- C:\HTML_and_HTA_CODE_EXAMPLES\ATest.Hta -->
<HEAD>
<TITLE>ATest.Hta</TITLE>
<HTA:APPLICATION ID="HTA MyApp"
APPLICATIONNAME="Help4Saul Dolgin"
BORDER ="thick"
BORDERSTYLE ="complex"
CAPTION ="yes"
CONTEXTMENU ="no"
ICON ="http://Your URL/your icon.ico"
INNERBORDER ="yes"
MAXIMIZEBUTTON ="yes"
MINIMIZEBUTTON ="yes"
NAVIGABLE ="no"
SCROLL ="no"
SHOWINTASKBAR ="yes"
SINGLEINSTANCE ="yes"
SYSMENU ="yes"
VERSION ="1.0"
WINDOWSTATE ="Normal"/>
</HEAD>
<style>
.ExBt21 {background:"#E0E0E0";Color:"red";}/* For Exit Button */
.Spn4 {font-family:"arial";font-weight:"bold";Color:"blue"}
.Spn2 {Color:"red"}
.tAr1 {font-family:"arial";font-weight:"bold";Color:"blue"}
</style>
<SCRIPT Language="VBScript">
Sub GetFileNumber
Dim FSO, f, fc, tempHighNum, strLine, objSHO, line
Dim DataArr, Data1Arr, Data2Arr, ListCol
fc=""
ListCol=""
tempHighNum=000
Set objSHO=CreateObject("WScript.Shell")
objSHO.run "C:\Batch\Dir.bat"
Set FSO = CreateObject( "Scripting.FileSystemObject" )
Set f = FSO.OpenTextFile("c:\Temp\Data.Txt", "1")
Do Until f.AtEndOfStream
fc = fc & f.ReadLine & vbLf
Loop
tArea1.innerHTML=fc
Data1Arr = Split(fc,vbLf)
Count=UBound(Data1Arr)
For x=0 To Count
DataArr = Split(Data1Arr(x),".")
If x <= Count-1 Then
Data2Arr = Split(DataArr(0),"_")
ListCol = ListCol & Data2Arr(1) & vbLf
If Data2Arr(1) > tempHighNum Then
tempHighNum = Data2Arr(1)
End If
End If
Next
Span2.innerHTML=tempHighNum
End Sub
Sub ExtBtn:Window.close:End Sub' Exit Script For Window
</SCRIPT>
<BODY bgcolor="#D0D0D0">
<button OnClick="GetFileNumber">Button To Press</button><br/>
<span id="Span1" class="Spn1">The biggest No. is: </span><span id="Span2" class="Spn2"></span><br/><br/>
<span Id="Span3" Class="Spn3">Dir.Bat looks in: </span><span Id="Span4" Class="Spn4">C:\Temp\Log_???.Txt</span><br/>
<textarea Id="tArea1" class="tAr1" rows="10"></textarea><br/><br/>
<input id="ExBtn21" class="ExBt21" type="Button" name="ExitBtn21" OnClick="ExtBtn" value="&nbsp-Exit-&nbsp"/><!-- Exit Button -->
</BODY>
</HTML>
The Dir.Bat file is next:
#Echo Off
Dir/B c:\Temp\Log_???.Txt>c:\Temp\Data.Txt

LibreOffice Basic Macro command converting Calc cellRange to RTF/HTML

My goal is to fill a LibreOffice calc sheet, and silently send a cell range by email when the user clicks the send-off button (and once more to confirm).
So there is three part to this.
A push button with a request to confirm. (Easy and done.)
Select Cell Range and turn it into rich text format (Haven't yet found)
Send rich text email from within the sheet. (Will tackle the "silent" part later)
I tried copying the range to the clipboard with unoService but it seemed over-complicated and full of errors.
Here's what I have:
''''Send by e-mail enriched text
Sub Main
Dim Doc, Sheet, Range, Rtf, Exec as Object
End Sub
'Confirm it
Sub SendTableApproval
If MsgBox ("Ready to email?", MB_YESNO + MB_DEFBUTTON2) = IDYES Then
CopyTable()
End If
End Sub
'Copy it
Sub CopyTable
Doc = ThisComponent
View = Doc.CurrentController
Frame = View.Frame
Sheet = Doc.Sheets.getByIndex(0)
Range = Sheet.getCellrangeByName("a1:f45")
Exec = createUnoService("com.sun.star.frame.DispatchHelper")
View.Select(Range)
Cells = View.getTransferable()
Exec.executeDispatch(Frame, ".uno:Deselect", "", 0, array())
'SimpleMailTo(Cells)
End Sub
'Mail it
Sub SimpleMailTo(body)
Dim launcher as object
Dim eAddress, eSubject, eBody, aHTMLanchor as string
launcher = CreateUnoService("com.sun.star.system.SystemShellExecute")
eAddress = "tu#domo.eg"
eSubject = "Cotidie agenda futuendane"
eBody = body
aHTMLanchor = "mailto:" & eAddress & "?subject=" & eSubject & "&&body=" & eBody
launcher.execute(aHTMLanchor, "", 0)
End Sub
I still do not know after three days of research over methods, properties, uno.
My question is, simply put, How can I convert a transferable content to HTML/RTF?
Simply copying and pasting into an email produces the result you are asking for. The code on the LibreOffice side should look like this.
dispatcher.executeDispatch(document, ".uno:Copy", "", 0, Array())
It sounds like you already tried this, but something didn't work. Perhaps you could elaborate on what went wrong.
Another approach would be to write the spreadsheet to a temporary HTML or XHTML file. Then parse the temporary file to grab the part needed for the email.
AFAIK there is no such command to turn a cell range into rich text format with UNO. To do it that way, you would need to loop through each text range of each cell, read its formatting properties and then generate the HTML yourself.
EDIT:
Good idea about XTransferable. The following Java code adapted from the DevGuide gets an HTML string and then prints it. I believe this would be a good solution for your needs.
public void displayHTMLFromClipboard()
{
try
{
Object oClipboard = xMCF.createInstanceWithContext(
"com.sun.star.datatransfer.clipboard.SystemClipboard", xContext);
XClipboard xClipboard = (XClipboard)
UnoRuntime.queryInterface(XClipboard.class, oClipboard);
XTransferable xTransferable = xClipboard.getContents();
DataFlavor[] aDflvArr = xTransferable.getTransferDataFlavors();
System.out.println("Available clipboard formats:");
DataFlavor aChosenFlv = null;
for (int i=0;i<aDflvArr.length;i++)
{
System.out.println(
"MimeType: " + aDflvArr[i].MimeType +
" HumanPresentableName: " + aDflvArr[i].HumanPresentableName );
if (aDflvArr[i].MimeType.equals("text/html"))
{
aChosenFlv = aDflvArr[i];
}
}
System.out.println("");
try
{
if (aChosenFlv != null)
{
System.out.println("HTML text on the clipboard...");
Object aData = xTransferable.getTransferData(aChosenFlv);
String s = new String((byte[])aData, Charset.forName("ISO-8859-1"));
System.out.println(s);
}
}
catch (UnsupportedFlavorException exc)
{
exc.printStackTrace();
}
}
catch(com.sun.star.uno.Exception exc)
{
exc.printStackTrace();
}
}
If you plan to use Basic, it might be a good idea to do some research into the proper way to convert bytes. The code I have below seems to work but is probably unreliable and unsafe, and will not work for many languages. A few of my initial attempts crashed before this finally worked.
Sub DisplayClipboardData
oClipboard = createUnoService("com.sun.star.datatransfer.clipboard.SystemClipboard")
xTransferable = oClipboard.getContents()
aDflvArr = xTransferable.getTransferDataFlavors()
For i = LBound(aDflvArr) To UBound(aDflvArr)
If aDflvArr(i).MimeType = "text/html" Then
Dim aData() As Byte
aData = xTransferable.getTransferData(aDflvArr(i))
Dim s As String
For j = LBound(aData) to UBound(aData)
s = s & Chr(aData(j)) 'XXX: Probably a bad way to do this!
Next j
Print(s)
End If
Next
End Sub
One more suggestion: Python might be a better language choice here. In many ways, using Python with LibreOffice is easier than Java. And unlike Basic, Python is powerful enough to comfortably handle byte strings.

Google Maps geocoding API in OpenOffice Calc VBA

For my project, I need to geocode a set of locations for which I would like to know the GPS coordinates.
The amount of locations it too big to to it by hand, but not too much so that I will not have problems with Google's limitations of the use of the Geocoding API.
The most convenient way to do this for me would be to use OpenOffice Calc.
I found a VBA code that does just what I need:
Function GetGeoData(sSearch as String) as String
If Len(sSearch) = 0 Then Exit Function 'we dont need empty cells <img draggable="false" class="emoji" alt="😉" src="http://s.w.org/images/core/emoji/72x72/1f609.png">
URL = "http://maps.googleapis.com/maps/api/geocode/xml?sensor=true&address=" 'we will use the google maps api
URL = URL & sSearch 'create the searchstring
oSimpleFileAccess = createUnoService( "com.sun.star.ucb.SimpleFileAccess" ) 'this is the Sefvice in getting the data from the web
On Error GoTo ErrorResponse
oInputStream = oSimpleFileAccess.openFileRead(URL) 'use the URL
oTextStream = createUnoService("com.sun.star.io.TextInputStream") 'get the data from the web
oTextStream.InputStream = oInputStream 'this is the data
aDelimiters = Array(ASC(">"),ASC("<")) 'as the stream is segmented with ">" and "<"
sLastString = ""
Do While NOT oTextStream.isEOF 'go through the google output
sThisString = oTextStream.readString(aDelimiters,True)
Select Case sLastString 'now search for the entries
Case "lat": 'latitudes
sLat = sThisString
Case "lng": 'longitude
sLon = sThisString
End Select
sLastString = sThisString
Loop
GetGeoData = " Longitude: " & sLon & " Latitude: " &sLat 'this is our output in the new cell
oInputStream.closeInput()
Exit Function
ErrorResponse:
GetGeoData = "no values found!!!"
End Function
However, while it is fine for exact addresses, there is a problem when it comes to settlements that Google knows as polygons. In this case, the code only keeps the last set of coordinates it found in the xml info, but this corresponds to the north-east corner of the polygon. I would be happy to have the center of the polygon, which corresponds to the first set of coordinates in the xml document generated by Google maps.
Can anyone explain to me how I can select a specific node in the xml
file based on this code?
Another solution would be to keep only the first set of coordinates.
At first: Never take XML as text string only. XML has a meaningful data structure which needs to be parsed. Fortunately the Openoffice API provides a XML parser already. com.sun.star.xml.dom.DocumentBuilder https://www.openoffice.org/api/docs/common/ref/com/sun/star/xml/dom/DocumentBuilder.html
To your question: Each result has a geometry with a location. The lat, lng in that location will be either the approximate lat, lng or the geometric center. The other lat, lng are viewport or bounds ones.
Example Berlin, Germany:
<geometry>
<location>
<lat>52.5200066</lat>
<lng>13.4049540</lng>
</location>
<location_type>APPROXIMATE</location_type>
<viewport>
<southwest>
<lat>52.3396296</lat>
<lng>13.0891553</lng>
</southwest>
<northeast>
<lat>52.6754542</lat>
<lng>13.7611176</lng>
</northeast>
</viewport>
<bounds>
<southwest>
<lat>52.3396296</lat>
<lng>13.0891553</lng>
</southwest>
<northeast>
<lat>52.6754542</lat>
<lng>13.7611176</lng>
</northeast>
</bounds>
</geometry>
So only the lat, lng from the location are needed.
But there are other issues too. What if there are more than one results? Berlin, for example, is not only the capital of Germany.
So my example function returns all results:
Function GetGeoData(sSearch as String) as String
sResult = ""
if len(sSearch) > 0 and sSearch <> "0" then
sURI = "http://maps.googleapis.com/maps/api/geocode/xml?sensor=true&address="
sURI = sURI & sSearch
oDocumentBuilder = createUnoService("com.sun.star.xml.dom.DocumentBuilder")
oDOMDocument = oDocumentBuilder.parseURI(sURI)
oResults = oDOMDocument.getElementsByTagName("result")
for i = 0 to oResults.length -1
oResult = oResults.item(i)
oformattedAddress = oResult.getElementsByTagName("formatted_address").item(0)
sformattedAddress = oformattedAddress.getFirstChild().nodeValue
oGeometry = oResult.getElementsByTagName("geometry").item(0)
oLocation = oGeometry.getElementsByTagName("location").item(0)
oLat = oLocation.getElementsByTagName("lat").item(0)
sLat = oLat.getFirstChild().nodeValue
oLng = oLocation.getElementsByTagName("lng").item(0)
sLng = oLng.getFirstChild().nodeValue
if i = 0 then
sResult = sResult & sformattedAddress & ": Lat:" & sLat & " Lng:" & sLng
else
sResult = sResult & "; " & sformattedAddress & ": Lat:" & sLat & " Lng:" & sLng
end if
next
end if
GetGeoData = sResult
End Function
It sounds like you are looking for something more powerful than just matching "<" and ">" in tags. This often happens when working with XML, and there are many specialized libraries to accomplish this task.
Parsing XML can be accomplished in OpenOffice Basic using the com.sun.star.xml.sax.Parser interface. See https://wiki.openoffice.org/wiki/XML_and_Filter for details.
Alternatively, many languages have XML parsing libraries. Java and Python have XML parsing libraries and can also work with OpenOffice. The library I personally use most with OpenOffice is xml.dom.minidom.

Best way to ignore Comma (,) in CSV files if the Comma(,) is in quotation marks?

I have a small program to read CSV files to build datatable out of it. One requirement is to ignore commas (commas in names, etc) if the commas are between quotation marks. Example.
Name, Age, Location
"Henderson, David", 32, London
John Smith, 19, Belfast
The program should ignore the comma after Henderson and read Henderson, David as one field. My current code can't do this job adding extra column at the end. So How can I achieve it? The solution should not replace the comma between the quotation marks. Thanks.
My current code.
Public Function BuildDataTable() As DataTable
Dim myTable As DataTable = New DataTable("MyTable")
Dim i As Integer
Dim myRow As DataRow
Dim fieldValues As String()
Dim myReader As StreamReader = New StreamReader(_fileFullPath, Encoding.GetEncoding("iso-8859-1"))
Try
fieldValues = myReader.ReadLine().Split(_seperator)
'Create data columns accordingly
If _hasheader = False Then
For i = 0 To fieldValues.Length() - 1
myTable.Columns.Add(New DataColumn("Column(" & i & ")"))
Next
Else
'if the file has header, take the first row as header for datatable
For i = 0 To fieldValues.Length() - 1
myTable.Columns.Add(New DataColumn(fieldValues(i).Replace(" ", "")))
Next
End If
myRow = myTable.NewRow
If _hasheader = False Then
For i = 0 To fieldValues.Length() - 1
myRow.Item(i) = fieldValues(i).ToString
Next
myTable.Rows.Add(myRow)
End If
While myReader.Peek() <> -1
fieldValues = myReader.ReadLine().Split(_seperator)
myRow = myTable.NewRow
For i = 0 To fieldValues.Length() - 1
myRow.Item(i) = fieldValues(i).Trim.ToString
Next
If Not csv2xml.AreAllColumnsEmpty(myRow) = True Then
myTable.Rows.Add(myRow)
End If
End While
Catch ex As Exception
End Try
End Function
You're looking to use the double quote character as a text qualifier in your CSV. Text qualifers allow you to use your field delimiter character(s) in a field value if the field is enclosed in the text qualifier character.
You can progam this yourself but that would be a mistake. There are plenty of free and capable CSV parsers that can do this for you. Since you're using Visual Basic you can take a look at the TextFieldParser class.
You'll still need to write code that will write a CSV's contents into a DataTable.
I found the following that seems to work:
http://www.vbcode.com/asp/showsn.asp?theID=13645
Another option is the GenericParser over at codeproject.com. Don't let the fact that the code in the article is written in C# bother you; you can still reference the DLL (GenericParsing.dll) in your project and use it in VB.
The nice thing about this parser is it includes a method you can use to return a DataTable for you from a CSV. Here's an example which works with your sample data:
Using parser As New GenericParsing.GenericParserAdapter(CSV_FILE_FULLNAME)
parser.ColumnDelimiter = ","
parser.TextQualifier = """"
parser.FirstRowHasHeader = True
Dim dt As DataTable = parser.GetDataTable()
End Using
I'm not familiar with Visual Basic but I think you should not use a Split() function to split the line.
fieldValues = myReader.ReadLine().Split(_seperator) ' DO NOT do this
Instead, write your own split function, which reads each characters one by one. Then have a flag to record whether you are between the double quotation marks.
UPDATE
I'm sorry I know too little about VB or C# to write a runnable code sniplet.
Please read this pseudocode (in fact it is JavaScript)...hope it is useful.
function split_with_quote(string, delimiter, quotation) {
if (delimiter == null) delimiter = ',';
if (quotation == null) quotation = '"';
var in_quotation = false;
var result = [];
var part = '';
for (var i = 0; i < string.length; i++) {
var ch = string[i];
if (ch == quotation) in_quotation = !in_quotation;
if (ch == delimiter && !in_quotation) {
result.push(part);
part = '';
} else {
if (ch != quotation) part += ch;
}
}
return result;
}
a = 'abc,def,"ghi,jkl",123';
split_with_quote(a); // ["abc", "def", "ghi,jkl"]