B4J: New ABMChat component for ABMaterial 2.50

abmchat

ABMChat is a new cool looking ABMaterial component to create a Web based chat program. The inner workings are very much like Erels ChatRoom, but with a slick interface. This can be a useful control if for example you want to give chat-like support within you own B4J webapp.

Some features:
– Balloon/Bubble-like chat
– Support for some ASCII emoji
– IsTyping ballon

Coding this with B4J and ABMaterial is very easy:

Code ChatShared module:

Sub Process_Globals
   Public AvoidDuplicates As Map
   Private connections As Map
   Public LastMessages As List
   Private IsTypingMap As Map
End Sub

public Sub Init
   AvoidDuplicates = Main.srvr.CreateThreadSafeMap
   connections.Initialize
   LastMessages.Initialize
   IsTypingMap.Initialize
End Sub

public Sub NewConnection(page As ChatPage, pageID As String)
   DateTime.TimeFormat = "HH:mm"

   Dim IdentifyBubble As ABMChatBubble
   IdentifyBubble.Initialize("Server", page.ChatName & " has joined the chat...", "Server " & DateTime.Time(DateTime.Now), "server", "server")
   JoinLeftMessage(pageID, IdentifyBubble)
   connections.Put(pageID, page)
   CallSubDelayed2(page, "CurrentConversation", LastMessages)
End Sub

public Sub NewMessage(pageID As String, Message As ABMChatBubble)
   LastMessages.Add(Message)
   For Each page As ChatPage In connections.Values
     CallSubDelayed2(page, "NewMessage", Message)
   Next
End Sub

public Sub JoinLeftMessage(pageID As String, Message As ABMChatBubble)
   For Each page As ChatPage In connections.Values
     CallSubDelayed2(page, "NewMessage", Message)
   Next
End Sub

public Sub IsTyping(pageID As String, Value As String)
   Dim typeBool As Boolean
   If Value = "" Then
     IsTypingMap.Remove(pageID.ToLowerCase)
   Else
     IsTypingMap.put(pageID.ToLowerCase, "true")
   End If
   If IsTypingMap.Size > 0 Then
     typeBool = True
   End If
   For Each page As ChatPage In connections.Values
     CallSubDelayed3(page, "IsTyping", pageID, typeBool)
   Next
End Sub

public Sub Disconnected(page As ChatPage, pageID As String)
   If connections.ContainsKey(pageID) = False Or connections.Get(pageID) <> page Then Return
   Dim LeaveBubble As ABMChatBubble
   LeaveBubble.Initialize("Server", page.ChatName & " has left the chat...", "Server " & DateTime.Time(DateTime.Now), "server","server")
   JoinLeftMessage(pageID, LeaveBubble)
   connections.Remove(pageID)
   AvoidDuplicates.Remove(pageID.ToLowerCase)
End Sub

Public Sub HasIdentified(pageID As String) As Boolean
   Return connections.ContainsKey(pageID)
End Sub

Relevant Code ChatPage class:

Private Sub WebSocket_Disconnected
   Log("Disconnected")
   If ABMPageId <> "" Then CallSubDelayed3(ChatShared, "Disconnected", Me, ABMPageId)
End Sub

public Sub BuildTheme()
   ' start with the base theme defined in ABMShared
   theme.Initialize("pagetheme")
   theme.AddABMTheme(ABMShared.MyTheme)

   ' add additional themes specific for this page
   theme.AddChatTheme("myChat")
   theme.Chat("myChat").AddBubble("server")
   theme.Chat("myChat").Bubble("server").Color = ABM.COLOR_BLACK
   theme.Chat("myChat").Bubble("server").TextColor = ABM.COLOR_WHITE

   theme.Chat("myChat").AddBubble("me")
   theme.Chat("myChat").Bubble("me").Color = ABM.COLOR_LIGHTBLUE
   theme.Chat("myChat").Bubble("me").TextColor = ABM.COLOR_WHITE

   theme.Chat("myChat").AddBubble("them")
   theme.Chat("myChat").Bubble("them").Color = ABM.COLOR_GREY
   theme.Chat("myChat").Bubble("them").ColorIntensity = ABM.INTENSITY_LIGHTEN2
   theme.Chat("myChat").Bubble("them").TextColor = ABM.COLOR_BLACK
End Sub

public Sub ConnectPage()
'   connecting the navigation bar    

'   init all your own variables (like a List, Map) and add your components
   Dim chat As ABMChat
   chat.Initialize(page, "conversation",600, 450, 255, "mychat")
   chat.AddBubble("Server", "I'm Server, what is your name?", "The Server", "server", "server")

   page.Cell(1,1).AddComponent(chat)

   Dim ChatInput As ABMInput
   ChatInput.Initialize(page, "ChatInput", ABM.INPUT_TEXT, "", False, "")
   ChatInput.PlaceHolderText = "Type your message here...."
   ChatInput.RaiseChangedEvent = True
   page.Cell(2,1).AddComponent(ChatInput)

   ' refresh the page
   page.Refresh
   ' Tell the browser we finished loading
   page.FinishedLoading
   ' restoring the navigation bar position
   page.RestoreNavigationBarPosition
End Sub

Sub ChatInput_Changed(value As String)
   If ChatShared.HasIdentified(ABMPageId) Then
     CallSubDelayed3(ChatShared, "IsTyping", ABMPageId, value)
   End If
End Sub

Sub ChatInput_EnterPressed(value As String)
   DateTime.TimeFormat = "HH:mm"

   If ChatShared.HasIdentified(ABMPageId) Then
     Dim bubble As ABMChatBubble
     bubble.Initialize(ChatName, value, ChatName & " " & DateTime.Time(DateTime.Now), "me", "them")
     CallSubDelayed3(ChatShared, "NewMessage", ABMPageId, bubble)
     CallSubDelayed3(ChatShared, "IsTyping", ABMPageId, "")
   Else
     If value = "" Then Return
     If ChatShared.AvoidDuplicates.ContainsKey(value.ToLowerCase) Then
       Dim chat As ABMChat = page.Component("conversation")
       chat.AddBubble("Server", "Sorry, but " & value & " is already taken!", "The Server", "server", "server")
       chat.Refresh
     Else
       ChatName = value
       ChatShared.AvoidDuplicates.Put(value.ToLowerCase, value)
       Dim chat As ABMChat = page.Component("conversation")
       chat.SetMyFrom(ChatName)
       chat.AddBubble("Server", "Welcome to ABMChat " & ChatName & "!", "The Server", "server", "server")
       chat.Refresh
       CallSubDelayed3(ChatShared, "NewConnection", Me, ABMPageId)
     End If
   End If

   Dim ChatInput As ABMInput = page.Component("ChatInput")
   ChatInput.Text = ""
   ChatInput.Refresh
End Sub

public Sub NewMessage(bubble As ABMChatBubble)
   Dim chat As ABMChat = page.Component("conversation")
   chat.AddBubble2(bubble)
   chat.Refresh
   chat.ScrollToBottom   ' scroll to the end of the chat
End Sub

public Sub IsTyping(pageID As String, typeBool As Boolean)
   Dim chat As ABMChat = page.Component("conversation")
   If pageID <> ABMPageId Then
     If typeBool = False Then
       chat.ThemAreTyping(False, "") 'hide the 'is typing' bubble
     Else
       chat.ThemAreTyping(True, "") ' show the 'is typing' bubble if not yet shown
     End If
     chat.Refresh
     chat.ScrollToBottom   ' scroll to the end of the chat
   End If
End Sub

public Sub CurrentConversation(LastMessages As List)
   If LastMessages.Size > 0 Then
     Dim chat As ABMChat = page.Component("conversation")
     chat.SetConversation(LastMessages)
     chat.Refresh
     chat.ScrollToBottom   ' scroll to the end of the chat
   End If
End Sub

So, there you have it. A complete modern looking multichat webapp that runs on all platforms in just about 150 lines of code!

Alwaysbusy

Click here to Donation and support ABMaterial


B4J: ABMaterial 2.20 with ABMGridBuilder

gridbuilder1

Responsive design is great, but at the same time something that is not so easy to grasp as it works over different levels.  ABMaterial has been using this concept from the very beginning using Materialize CSS and to make it easier, I created a builder for it.

A great start was the implementation done for Bootstrap: Shoelace.io.  I liked the idea of ‘drawing’ your grid.  However, for ABMaterial, I did want more.

Objectives:

  1. Needed to generate B4J source code, not html
  2. I wanted a way to return B4J code back to the builder
  3. Some extra functionalities to insert cells before/after another
  4. Save/Load to re-use designs.
  5. The generated code should include a ‘description’ of the grid.
  6. A ‘light’ and a ‘dark’ theme for night owls like me

Here is a little demonstration on how to work with it:

It generates code something like this:

'PHONE
'╔═══════════════════════════════════════════════════════════════════════════════════╗
'║ 1,1                                                                               ║
'╠═══════════════════════════════════════════════════════════════════════════════════╣
'║ 2,1                       | 2,2                       | 2,3                       ║
'║-----------------------------------------------------------------------------------║
'║ 2,4                       | 2,5                       | 2,6                       ║
'║-----------------------------------------------------------------------------------║
'║ 2,7                       | 2,8                       | 2,9                       ║
'║-----------------------------------------------------------------------------------║
'║ 2,10                      | 2,11                      | 2,12                      ║
'╠═══════════════════════════════════════════════════════════════════════════════════╣
'║ 3,1                                                                               ║
'╠═══════════════════════════════════════════════════════════════════════════════════╣
'║ 4,1                                                                               ║
'╚═══════════════════════════════════════════════════════════════════════════════════╝

'TABLET
'╔═══════════════════════════════════════════════════════════════════════════════════════════════════════════╗
'║ 1,1                                                                                                       ║
'╠═══════════════════════════════════════════════════════════════════════════════════════════════════════════╣
'║ 2,1                      | 2,2                      | 2,3                      | 2,4                      ║
'║-----------------------------------------------------------------------------------------------------------║
'║ 2,5                      | 2,6                      | 2,7                      | 2,8                      ║
'║-----------------------------------------------------------------------------------------------------------║
'║ 2,9                      | 2,10                     | 2,11                     | 2,12                     ║
'╠═══════════════════════════════════════════════════════════════════════════════════════════════════════════╣
'║ 3,1                                                                                                       ║
'╠═══════════════════════════════════════════════════════════════════════════════════════════════════════════╣
'║ 4,1                                                                                                       ║
'╚═══════════════════════════════════════════════════════════════════════════════════════════════════════════╝

'DESKTOP
'╔═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
'║ 1,1                                                                                                                               ║
'╠═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
'║ 2,1      | 2,2      | 2,3      | 2,4      | 2,5      | 2,6      | 2,7      | 2,8      | 2,9      | 2,10     | 2,11     | 2,12     ║
'╠═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
'║ 3,1                                                                                                                               ║
'╠═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
'║ 4,1                                                                                                                               ║
'╚═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

cont1.AddRows(1, True,"").AddCells12(1,"")
cont1.AddRows(1, True,"").AddCellsOS(12,0,0,0,4,3,1,"")
cont1.AddRows(2, True,"").AddCells12(1,"")
cont1.BuildGrid ' IMPORTANT!

This description of the grid is very handy when you start adding your components. As you can see in the video, copying the AddRows lines and parsing them back into the builder makes it easy to make changes.

The functionalities are all in the grid itself so you can work fast:

gridbuilder2

ABMGridBuilder is completely written in B4J, so it runs on all desktop platforms (Windows, OSX, Linux).

Tools like this make creating WebApps with ABMaterial and B4J even easier.  It is astonishing how versatile the B4X programming suite from Anywhere Software really is.  It is unique, fast, super stable and full of features nowhere else to be found. I’m so glad I discovered this great tool!

Donators will receive their mail with the download link shortly.

See ya!

Alwaysbusy

Click here to Donation and support ABMaterial

 


B4J: ABMaterial building a QR Code component

qrcodecomp

On the B4J forum, liulifeng77 asked the question how he could draw a QR Code in an ABMaterial web page.

I had 5 minutes to spare, so I created the QRCode component using ABMCustomComponent. It is using this javascript library: https://github.com/davidshimjs/qrcodejs

The component class CompQRCode:

'Class module
Sub Class_Globals
     Public ABMComp As ABMCustomComponent
     Public myText As String
   Public myWidth As Int
   Public myHeight As Int
   Public myColorDark As String
   Public myColorLight As String
   Public myCorrectLevel As String
End Sub
'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(InternalPage As ABMPage, ID As String, text As String, width As Int, height As Int, colorDark As String, colorLight As String, correctLevel As String)
     ABMComp.Initialize("ABMComp", Me, InternalPage, ID)
     myText = text
   myWidth = width
   myHeight = height
   myColorDark = colorDark
   myColorLight = colorLight
   myCorrectLevel = correctLevel
End Sub
Sub ABMComp_Build(internalID As String) As String
  Return $"
<div id="${internalID}svg"/></div>
<script>var qrcode${internalID};</script>"$
End Sub
' Is useful to run some initalisation script.
Sub ABMComp_FirstRun(InternalPage As ABMPage, internalID As String)
   Dim script As String = $"qrcode${internalID} = new QRCode(document.getElementById("${internalID}svg"), {
  text: "${myText}",
  width: ${myWidth},
  height: ${myHeight},
  colorDark : "${myColorDark}",
  colorLight : "${myColorLight}",
  correctLevel : ${myCorrectLevel}
});"$
     InternalPage.ws.Eval(script, Array As Object(ABMComp.ID))
     ' flush not needed, it's done in the refresh method in the lib
End Sub
' runs when a refresh is called
Sub ABMComp_Refresh(InternalPage As ABMPage, internalID As String)
   Dim script As String = $"qrcode${internalID}.clear();
   qrcode${internalID}.makeCode("${myText}");"$
     InternalPage.ws.Eval(script, Array As Object(ABMComp.ID))
End Sub
' do the stuff needed when the object is removed
Sub ABMComp_CleanUp(InternalPage As ABMPage, internalID As String)
End Sub

Usage (if you want to change its text at run time, put the declaration in Class_globals):

Dim QRCode As CompQRCode

In BuildPage() load the javascript library:

page.AddExtraJavaScriptFile("custom/qrcode.min.js")

In ConnectPage():

QRCode.Initialize(page, "qrcode", "Alwaysbusy's ABMaterial", 128,128, "#000000", "transparent", "QRCode.CorrectLevel.H")
page.Cell(3,1).AddComponent(QRCode.ABMComp)

Et voila, another new component!

Alwaysbusy

Click here to Donation and support ABMaterial


B4J: ABMaterial Public 1.22/Donators 2.00 now released

It has been a long night to get everything ready, but I’m happy to release the new version(s) of ABMaterial for B4J!

2.00 is a major release.  Lots of new stuff and some changes that were really needed in preparation of the upcoming (ABMaterial Abstract Desinger) ABMAD tool.

Highlights of the releases:

1.22: New component ABMPatternLock

ABMPatternLock is yet another authentication method you can use based on the popular Pattern Lock you see in e.g. Android.

patternlock

1.22: ABMContainer can collapse

ABMContainer can be initialized as a collapsable component.  As a collapsable has a header and a body and clicking on the header will collapse/expand the body.  This can be used as an alternative for an ABMCard so you can build your own.

1.22: Support for Font-Awesome icons

If you set: page.UseFontAwesome = true then you can, next to the material icons, also use the Font Awesome Icon library (634 icons in used version Font Awesome 4.6.3)
See fontawesome.io for a list of icons.

fontawesome

2.00: New component ABMChronologylist

The ABMChronologyList is a vertical timeline component. Useful to give an overview of a limited period. It is device aware so e.g. on a phone, all items will be one under each other.

chronology

2.00: New helper ABMSideBar

In ABMaterial 2.00, you will be able to define multiple side bars (sliding in from the right). I’ve tried to mimic the usage of ABMSideBar as much of the existing Side Navigation menu. The tricky part was making it work well with the NavigationBar, on all screensizes.

Check out the online demo abmaterial.com or download your free copy from the B4J website.  Make sure you read the included README.TXT files.  It contains some important info and shows some tips and tricks.

That’s it for now.  Next big release will be all about ABMAD. Until next time!

Alwaysbusy

Click here to Donation if you like my work


B4J: Future steps for ABMaterial

evolution

As ABMaterial has grown to a production ready framework for WebApps using B4J, I recently started a poll on the B4X forum to find out what could be the next steps in its evolution.

I came up with 3 things to do. They would require about the same develop time, but it could be useful to see what the actual users of ABMaterial would like me to work on next.

The possible choices were:

1. ABMXPlay
This is the component you have seen some demos of and will be more for game developers. It still needs a lot of attention before it can be released.

2. B4JS
The Basic to Javascript transpiler is now in its very early stages. It is a tough one to write without finishing ABMXplay first, as the ABMXPlay component would be the first to really benefit from it. The B4J core functions have been covered, but big parts of all ABMComponents need to be refactored to start using B4JS.

3. ABMaterialAbstractDesigner
A full blown Abstract Visual Designer like the one in B4J. This is NOT a code generator but the real deal. Code generators may look nice at first sight, but  have a huge disadvantage: they go one way. ABMAbstractDesigner would work like the B4X ones: you create a layout, develop, change the layout, continue to develop and so on without losing what you have written in code. I’ll use the grid designer I’ve shown as a proof of concept in another post + the possibility to put ABMComponents on them. I’ve done some tests this weekend and it would work very similar as the other B4X tools.

4. Other (please specify)

The poll is now nearly ended and besides some interesting messages with excellent pointers, it was quite obvious from the beginning that the ABMaterialAbstractDesigner (will need a working title for this project, lets call it ABMAD for now…) would be the next part I’ll be working on. (Almost 75% in the poll at the time of writing).  This was also my preferred choice as one user did not really have the patience to wait for me and started to write some kind of code generator.  However, his limited knowledge of the ABMaterial framework has the potential of starting to reflect bad on ABMaterial itself so it is time for me to jump in.

Making ABMAD will happen in a couple of logical steps:

  1. Making the ABMaterial components ready for a designer.  This will ask for some (minor) changes to them so they can be serialized and expose their properties to the designer.
  2. Making a grid builder.  This is the biggest step new users have to overcome nowadays.
  3. Making a properties view for the components.
  4. Drag/Drop facilities to add components to specific cells
  5. Let the ABMaterial library communicate with the Designer output in a similar way as B4X does now.

This will not happen overnight, but the benefits for ABMaterial programmers will be substantial.  This will NOT be a simple code generator. ABMAD will work bidirectional.  And you’ll be able to make layouts in ABMAD and mix them with manually written code like you can do in B4X/ABMaterial now.

Note: To avoid future misunderstandings, the license of ABMaterial 2.0 will include the following statement:

4. YOU AGREE NOT TO DISTRIBUTE, FOR A FEE, AN APPLICATION USING THE LIBRARY THAT, AS ITS PRIMARY PURPOSE, IS DESIGNED TO BE AN AID IN THE DEVELOPMENT OF SOFTWARE FOR YOUR APPLICATION’S END USER. SUCH APPLICATION INCLUDES, BUT IS NOT LIMITED TO, A DEVELOPMENT IDE OR A B4J SOURCE CODE GENERATOR.

Let me explain. The philosophy of ABMaterial (and myself) is giving anyone the chance to easily build great WebApps without having to spend any money. One of the reasons I picked B4J was because it is free. Those who can spare something, donate to stimulate my continuation on the project and in so help giving other not so fortunate people an equal chance to grow. Any derived development tool or generator that uses ABMaterial must follow this philosophy so the tool must be available with 100% of its features for free (not even a ‘limited’ free version is acceptable).

This has no impact on whatever WebApp you make with ABMaterial, but is solely a protection against some ‘money-grubber’ who sees the potential of the ABMaterial framework and thinks of making some easy money using my engine by simply writing a layer above it. Just to be clear, ‘FEE’ here means ANY form of payment or compensation, donations included.

So now that this is out of the way, I think it’s time to step up to the drawing board and start to make some decisions on how ABMAD will look like.

Fun times ahead!

Alwaysbusy

Click here to Donation and support ABMaterial

 

 

 

 

 

 

 


B4J: Creating your own components in ABMaterial

abmcustgauge

Although ABMaterial has already a lot of components build-in, sometimes you just need a specific one.  I had a question on the B4J forum from CGP who needed a Gauge component.

So let’s build one! CGP had found a very nice library JustGage that would allow him to get the Gauge he wanted. Its value had to be updateable from within B4J, and this was a bit tricky. It looked like we have to be a bit creative because this javascript library did not connect its parent tag with the created JustGage.

We will have to keep track of the JustGage variable ourselves by adding a script tag in the build:

Sub ABMComp_Build(internalID As String) As String
  Return $"<div id="${internalID}"></div><script>var _${internalID};</script>"$
End Sub

The rest of the code is straight forward and we can just follow the javascript instructions and add them in our relative events.

An ABMaterial custom component has 4 important events:

ABMComp_Build(): This is where we have to add the html tag (anchor) for the new component.  In this particular case we also have to add a <script> tag that will hold our justGage variable.  We can then later use this variable to update the Gauge chart.

ABMComp_FirstRun(): Here we can add a script that will run, well, at first run. In most cases, this is where we add the libraries javascript initialization code. The B4J $””$ Smartstrings are in methods like this particulary handy. Use them!

ABMComp_Refresh(): The place where we can adjust properties of the javascript component.  Like for this component, we’ll adjust the value of the gauge.

ABMComp_CleanUp(): An event that is raised when the component is removed.  Could be useful to do some cleanup in the html of set javascript variables to null.

Here is the complete B4J code for our customGauge component:

Sub Class_Globals
   Public ABMComp As ABMCustomComponent
  Public myValue As Int
  Public myLabel As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(InternalPage As ABMPage, ID As String, value As Int, name As String)
  ABMComp.Initialize("ABMComp", Me, InternalPage, ID)
  myValue = value
  myLabel = name
End Sub

Sub ABMComp_Build(internalID As String) As String
  Return $"<div id="${internalID}"></div><script>var _${internalID};</script>"$
End Sub

' Is useful to run some initalisation script.
Sub ABMComp_FirstRun(InternalPage As ABMPage, internalID As String)
  Dim script As String = $"_${internalID} = new JustGage({
  id: "${internalID}",
  value: ${myValue},
  Min: 0,
  Max: 100,
  relativeGaugeSize: true,
  title: "${myLabel}"
  });"$

  InternalPage.ws.Eval(script, Array As Object(ABMComp.ID))
  ' flush not needed, it's done in the refresh method in the lib
End Sub

' runs when a refresh is called
Sub ABMComp_Refresh(InternalPage As ABMPage, internalID As String)
  Dim script As String = $"_${internalID}.refresh(${myValue});"$
  InternalPage.ws.Eval(script, Null)
End Sub

' do the stuff needed when the object is removed
Sub ABMComp_CleanUp(InternalPage As ABMPage, internalID As String)

End Sub

Now, how do we use our new component in our B4J ABMaterial WebApp?

First, we’ll need to make our custGauge a global variable on the page:

Sub Class_Globals
  Dim custGauge As CustomGauge
  ...

Next, we add the justGauge libraries in the BuildPage().  We’ve placed them in \www\js\custom folder so we can add:

Sub BuildPage()
  ...
  page.AddExtraJavaScriptFile("custom/justgage.js")
  page.AddExtraJavaScriptFile("custom/raphael-2.1.4.min.js")
  ...

In the ConnectPage, we’ll create the actual Gauge and we add a button so we can change its value:

Sub ConnectPage()
  ...
  custGauge.Initialize(page, "custGauge", 25, "Test")
  page.Cell(7,1).AddComponent(custGauge.ABMComp)

  Dim custGaugebtn1 As ABMButton
  custGaugebtn1.InitializeRaised(page, "custGaugebtn1", "", "", "BUTTON", "")
   page.Cell(7,1).AddComponent(custGaugebtn1)
   ...

And finally we add out button code to adjust the value of the Gauge:

Sub custGaugebtn1_Clicked(Target As String)
   custGauge.myValue = Rnd(10,100)
   custGauge.ABMComp.Refresh
End Sub

And just like that, it looks like a fun new component we’ve got in B4J ABMaterial!

Until next time,

Alwaysbusy

Click here to Donation and support ABMaterial

 


B4J: ABMXPlay first online demo

abmxplay-demo

This is the first public demo of the new ABMaterial 2.0 component ABMXPlay.  It contains several basic demos of what this new component can do. Use the ‘hamburger’ menu on the top left to see another demo.

Although the current version of the demo still uses pure B4J server code (e.g. when you click, it does a round-trip to the server), I’m quite happy with the responiveness.  But as I mentioned in a previous post, even this round-trip will be eliminated when it starts using the Basic to JavaScript transpiler B4JS.

I’m making good progress on B4JS.  At the time of this writing, I’m able to transpile a lot of the B4J core functions. See here for an overview.

An example of some B4J core function tests and their translations to Javascript.

B4J code:

Sub B4JSCoreFunctions
   Log("------- Start B4JSCoreFunctions")
   Log("(True And False): false <=> " & (True And False)) 
   Log("(True Or False): true <=> " & (True Or False)) 
   Log("Rnd(0,10): Number between 0 and 10 <=> " & Rnd(0,10))
   Log("Abs(-2): 2 <=> " & Abs(-2))
   Log("Not(False): true <=> " & Not(False))
   Log("CosD(45):  0.70710678... <=> " & CosD(45))
   Log("Cos(0.5):  0.87758256... <=> " & Cos(0.5))
   Log("SinD(45):  0.70710678... <=> " & SinD(45))
   Log("Sin(0.5):  0.47942553... <=> " & Sin(0.5))
   Log("ACosD(-1):  180... <=> " & ACosD(-1))
   Log("ACos(0.5):  1.04719755... <=> " & ACos(0.5))
   Log("ASinD(-1):  -90... <=> " & ASinD(-1))
   Log("ASin(0.5):  0.52359877... <=> " & ASin(0.5))
   Log("TanD(45):  0.99999999... <=> " & TanD(45))
   Log("Tan(0.5):  0.54630248... <=> " & Tan(0.5))
   Log("ATanD(45):  88.7269699... <=> " & ATanD(45))
   Log("ATan(0.5):  0.46364760... <=> " & ATan(0.5))
   Log("ATan2D(90,45): 3.4349488... <=> " & ATan2D(90,45))
   Log("ATan2(8,4):  1.10714871... <=> " & ATan2(8,4))

   Log("try/Catch example, only visible in B4JS.")
   Try
     Dim i As Int = 1/0 'ignore
   Catch
     Log(LastException.Message)
   End Try

   Log("Chr(65): A <=> " & Chr(65))
   Log("Asc(""A""): 65 <=> " & Asc("A"))
   Log("cE: 2.71828182... <=> " & cE)
   Log("Ceil(1.65): 2 <=> " & Ceil(1.65))
   Log("cPI: 3.14159265... <=> " & cPI)
   Log("Floor(1.65): 1 <=> " & Floor(1.65))
   Log("IsNumber(""alain""): false <=> " & IsNumber("alain"))
   Log("IsNumber(""15.2""): true <=> " & IsNumber("15.2"))
   Log("Logarithm(10, 1000): 0.33333333.... <=> " & Logarithm(10, 1000))
   Log("Max(10,5): 10 <=> " & Max(10,5))
   Log("Min(10,5): 5 <=> " & Min(10,5))
   Log("Power(2,3): 8 <=> " & Power(2,3))
   Log("Round(1.12345): 1 <=> " & Round(1.12345))
   Log("Round2(1.12345, 2): 1.12 <=> " & Round2(1.12345, 2))
   Log("Sqrt(9): 3 <=> " & Sqrt(9))

   Dim str1 As String = "Test €100 is £84.73"
   Dim theBytes() As Byte
   theBytes = str1.GetBytes("UTF8")
   Log("BytesToString(theBytes,0,theBytes.Length, ""UTF-8""): Test €100 is £84.73 <=> " & BytesToString(theBytes,0,theBytes.Length, "UTF-8"))
End Sub

The JavaScript transpiled code:

this.b4jscorefunctions=b4jscorefunctions=function(){
     try {
       console.log("------- Start B4JSCoreFunctions");
       console.log("(True And False): false <=> "+(true && false));
       console.log("(True Or False): true <=> "+(true || false));
       console.log("Rnd(0,10): Number between 0 and 10 <=> "+(Math.floor(Math.random() * (10 - 0) + 0)));
       console.log("Abs(-2): 2 <=> "+(Math.abs(-2)));
       console.log("Not(False): true <=> "+!(false));
       console.log("CosD(45):  0.70710678... <=> "+(Math.cos((45)/(180/Math.PI))));
       console.log("Cos(0.5):  0.87758256... <=> "+(Math.cos(0.5)));
       console.log("SinD(45):  0.70710678... <=> "+(Math.sin((45)/(180/Math.PI))));
       console.log("Sin(0.5):  0.47942553... <=> "+(Math.sin(0.5)));
       console.log("ACosD(-1):  180... <=> "+(Math.acos(-1)*(180/Math.PI)));
       console.log("ACos(0.5):  1.04719755... <=> "+(Math.acos(0.5)));
       console.log("ASinD(-1):  -90... <=> "+(Math.asin(-1)*(180/Math.PI)));
       console.log("ASin(0.5):  0.52359877... <=> "+(Math.asin(0.5)));
       console.log("TanD(45):  0.99999999... <=> "+(Math.tan((45)/(180/Math.PI))));
       console.log("Tan(0.5):  0.54630248... <=> "+(Math.tan(0.5)));
       console.log("ATanD(45):  88.7269699... <=> "+(Math.atan(45)*(180/Math.PI)));
       console.log("ATan(0.5):  0.46364760... <=> "+(Math.atan(0.5)));
       console.log("ATan2D(90,45): 3.4349488... <=> "+(Math.atan2(90,45)*(180/Math.PI)));
       console.log("ATan2(8,4):  1.10714871... <=> "+(Math.atan2(8,4)));
       console.log("try/Catch example, only visible in B4JS.");
       try {
         var _i=1/0;
       } catch(err) {
         console.log(lastexception.message);
       }
       console.log("Chr(65): A <=> "+String.fromCharCode(65));
       console.log("Asc(\"A\"): 65 <=> "+"A".charCodeAt(0));
       console.log("cE: 2.71828182... <=> "+2.7182818284590452353602874713527);
       console.log("Ceil(1.65): 2 <=> "+(Math.ceil(1.65)));
       console.log("cPI: 3.14159265... <=> "+Math.PI);
       console.log("Floor(1.65): 1 <=> "+(Math.floor(1.65)));
       console.log("IsNumber(\"alain\"): false <=> "+(!isNaN(parseFloat("alain")) && isFinite("alain")));
       console.log("IsNumber(\"15.2\"): true <=> "+(!isNaN(parseFloat("15.2")) && isFinite("15.2")));
       console.log("Logarithm(10, 1000): 0.33333333.... <=> "+(Math.log(10)/Math.log(1000)));
       console.log("Max(10,5): 10 <=> "+Math.max(10,5));
       console.log("Min(10,5): 5 <=> "+Math.min(10,5));
       console.log("Power(2,3): 8 <=> "+Math.pow(2,3));
       console.log("Round(1.12345): 1 <=> "+Math.round(1.12345));
       console.log("Round2(1.12345, 2): 1.12 <=> "+(+(Math.round(1.12345+"e+"+2)+"e-"+2)));
       console.log("Sqrt(9): 3 <=> "+(Math.sqrt(9)));
       var _str1="Test €100 is £84.73";
       var _thebytes=[];
       _thebytes.length=0;
       _thebytes = _str1.getBytes();
       console.log("BytesToString(theBytes,0,theBytes.Length, \"UTF-8\"): Test €100 is £84.73 <=> "+bytesToString(_thebytes.slice(0,_thebytes.length)));
     }
     catch(err) {
       console.log(err.message + ' ' + err.stack);
     }
   };

Or a more challenging transpiling of the B4J ‘String.GetBytes()’ command:

String.prototype.getBytes = function() {
     var utf8 = [];
     var str = this;
     for (var i=0; i < str.length; i++) {
       var charcode = str.charCodeAt(i);
       if (charcode < 0x80) utf8.push(charcode);
       else if (charcode < 0x800) {
         utf8.push(0xffffffc0 | (charcode >> 6), 0xffffff80 | (charcode & 0x3f));
       } else if (charcode < 0xd800 || charcode >= 0xe000) {
         utf8.push(0xffffffe0 | (charcode >> 12), 0xffffff80 | ((charcode>>6) & 0x3f), 0xffffff80 | (charcode & 0x3f));
       } else {
         utf8.push(0xef, 0xbf, 0xbd);
       }
     }
     return utf8;
   };

Now that I can write real B4J code and translate it to JavaScript, I can start introducing it into ABMaterial. Once I’ve done this for the ABMXplay component, I’ll put up a new demo (using B4JS) and then we can compare the results. My first tests look very promising!

WARNING: the demo contains audio fragments and the South Park characters can be rather offensive!

It runs on my personal server. Please be gentle: ABMXPlay demo

Happy programming!

Alwaysbusy


%d bloggers like this: