Jump to content

VScroll SMS Component


Recommended Posts

Basic example

unit Form1;

interface

uses
  SmartCL.System, SmartCL.Graphics, SmartCL.Components, SmartCL.Forms,
  uVScroll, SmartCL.Application;

type
  TForm1 = class(TW3Form)
  private
    {$I 'Form1:intf'}
    FBox: TW3VScrollControl;
  protected
    procedure InitializeForm; override;
    procedure InitializeObject; override;
    procedure Resize; override;
  end;

implementation

{ TForm1 }

procedure TForm1.InitializeForm;
begin
  inherited;
  // this is a good place to initialize components
  FBox := TW3VScrollControl.Create(self);
  FBox.SetBounds(0,0,200,300);

var LText :="<table cellpadding=|0px| style=|border-collapse: collapse| width=|100%|>";
  for var x:=1 to 400 do
  begin
    if ((x div 2) * 2) = x then
    LText += "<tr padding=|0px| style=|border: 0px solid black; background:#ECECEC|>" else
    LText += "<tr style=|border: 0px solid black; background:#FFFFFF|>";
    LText += "<td padding=|0px| height=|32px| style=|border-bottom: 1px solid #ddd|>" + x.toString + "</td>";
    LText += "<td style=|border-bottom: 1px solid #ddd|>List item #" + x.toString + "</td";
    LText += "</tr>";
  end;
  LText +="</table>";
  LText := StrReplace(LText,'|','''');

  FBox.Content.innerHTML := LText;
  FBox.Content.height := FBox.Content.ScrollInfo.ScrollHeight;


end;

procedure TForm1.InitializeObject;
begin
  inherited;
  {$I 'Form1:impl'}
end;

procedure TForm1.Resize;
begin
  inherited;
end;

initialization
  Forms.RegisterForm({$I %FILE%}, TForm1);
end.

Link to post
Share on other sites

Thanks for adapting it from JLA sample, it's quite handy. I was wondering if anybody have tried it on Edge, in my test it wasn't working.

 

On a related note, I'm having a lot of trouble of making scroll work in html5 table contained within TW3Scrollbox, problem is with Safari/Edge and IE, I would appreciate any hint of how to solve it.

Link to post
Share on other sites
Unfortunately, I could not reproduce the issue. I'll prefer render the HTML5 table within a simple DIV. 

 

I just dropped TW3ScrollBox into designer, see the container, there are child elements, maybe this is a CSS conflict, since every browser could have a different and specific property for their respective bars. 



<div id="OBJ4" class="TW3Scrollbox"
style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 16px; top: 0px; width: 360px; height: 400px;">

<div id="OBJ5" class="TW3ScrollBoxContainer"
style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 1px; top: 1px; width: 358px; height: 398px;">
</div>
<div id="OBJ6" class="TW3HorizontalScrollbar"
style="visibility: hidden; display: none; position: absolute; overflow: hidden; left: 0px; top: 0px; background-color: rgb(255, 255, 255);">

<div id="OBJ7" class="TW3ScrollbarLeftBtn"
style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 0px; top: 0px; width: 0px; height: 0px;">

<img width="100%" height="100%"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAA6klEQVQ4jcWSMU7EQAxFv0OFaLmEfQVoKBDtZE6ChLYAKRQsokAg7Uk8B4COhprGPgIF1bZImGYjLUMSQrXu/Of/J8tjYNe195chpXTJzEVE3s3srX5vpsI5546I7gAcRsTjkGcUkHPuIuKm74loNeSjIbFt23sAiy1pUUp5mAX4T/gXIKWUiKhsjX2rqt1YGKh20DTND2BErKfCQPWNZubMvAZwtpFOmXnf3Z9nAQDA3V8ryDEzH7j70yxADxGRLwAnG+lIRD7N7KX2jt6Bqi6J6LrvI+J8yDd5iaq6jIgrAB9EdDHl3V19A2PtT5Dyi9aLAAAAAElFTkSuQmCC" />
</div>
<div id="OBJ8" class="TW3ScrollbarRightBtn"
style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 0px; top: 0px; width: 0px; height: 0px;">

<img width="100%" height="100%"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAA1UlEQVQ4jc3QsWoCQRDG8f8YsbDOQ9zcK0gaMf3tE/gKSpBY2AaClU9yB9amiWibcsd3sEmTKnBpPFiOvSVWOt3szPdjd+HW9RA7dM5NVXWnqj0zO1wNqOoH8AhMVPXXzPZXAVmWDURkfGnHKSQKmNmnqv4Azw2S5/nQe7/7F3BBji1kFEM6gQAZAE8B8uW9PzU7vRQAUNf1d2reTw2dc+/Aa9OLyLwsy224I4nwGli0wpv2XvQJRVGswjCwioU7ARGZheGqqt66btr1iS/AGVimwvdRf3FdSIK1auN+AAAAAElFTkSuQmCC" />
</div>
<div id="OBJ9" class="TW3ScrollbarHandle"
style="visibility: hidden; display: none; position: absolute; overflow: hidden; left: 0px; top: 2px; height: 0px;"></div>
</div>
<div id="OBJ10" class="TW3VerticalScrollbar"
style="visibility: hidden; display: none; position: absolute; overflow: hidden; left: 0px; top: 0px; background-color: rgb(255, 255, 255);">

<div id="OBJ11" class="TW3ScrollbarDownBtn"
style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 0px; top: 0px; width: 0px; height: 0px;">

<img width="100%" height="100%"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAA/klEQVQ4je2QvUrEQBSFz40PYOHCggiCrQmWlsLiA8xkyDtoq2wharfNylY2+hKZSdJYWIngOwQFkd0n2DbMiU0CMf6gdoKnGc65d757ucCfl3xVVEodisiuiJxZaxc/AmitLwCMGzv33o+Konj6FkApNROR4168CIJglKbpYzcMPpg86X1eNu8GyVtjzNZnANFaXwE47WRjkqGItKtvknwwxmy/A8RxfATgoPV1XZ8752Z5ns8B7IvIc1MakrxJkmT1DYDkTmfySZZlk9ZYa1+893udTdarqhoAwErbFEXRHck1ANfOucv+bcqyXIZhaEkORGTqnLvv9/zrl3oFMYVbLEeX4OkAAAAASUVORK5CYII=" />
</div>
<div id="OBJ12" class="TW3ScrollbarUpBtn"
style="visibility: visible; display: inline-block; position: absolute; overflow: hidden; left: 0px; top: 0px; width: 0px; height: 0px;">

<img width="100%" height="100%"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABBUlEQVQ4je2PMU7DQBRE5y9ukBsOQGIRhCxouAUV1nrX2pJTAKKLEmq4yhr7AqahpERyChfAHRAWK38aJyIro4gyEtPN/HmzWuBfOz9NlmVHcRx3dV1//gZIKU+iKPpomuYLAMTyoLW+6LpuQUTPxpjxEKyUmgohXsIwfDLG7K4NMPM5AAIwcc5V/ohSagpg3tvTtm3H/sANgNfeTpxzlZRyNACDmWdlWS7Qv7hSkiQHQRBUAKI+apj5gYgulx0imltrZyvv/1Nrvc/MjwAO/RsRXVlr79cyvwQAUsqREKLyRq7zPL/zu8IPAKAoinchxBkzv/XR7RC8UWma7imljv8Mbpe+AQXEWbnYo3KmAAAAAElFTkSuQmCC" />
</div>
<div id="OBJ13" class="TW3ScrollbarHandle"
style="visibility: hidden; display: none; position: absolute; overflow: hidden; left: 2px; top: 0px; width: 0px;"></div>
</div>
</div>


Link to post
Share on other sites

> Unfortunately, I could not reproduce the issue. I'll prefer render the HTML5 table within a simple DIV.
 
Do you use table API to construct cells or directly add items (<TR><TD></TD>...), have you ever measured what's more efficient speedwise?
 
> I just dropped TW3ScrollBox into designer, see the container, there are child elements, this is a CSS conflict, since every
> browser could have a different and specific property for their respective bars. [/size]
 
Thanks for trying, here is a sample project that shows this issue, https://www.dropbox.com/s/1durb4a0bt8jae6/GridScrollTest.zip?dl=0

 

Table within TW3ScrollBox doesn't work on Safari (iPad), but it works if within

TW3ScrollContent. Hower within Edge or IE (default versions within win10) they do not work. I guess there must be some MS specific touch handling there...

Link to post
Share on other sites

I could not reproduce the issue := I could not test on a real device;

 

I have tested your example using TW3ScrollBox as container in the desktop browser, and is very sluggish on scrolling. 

 

I'd put the table into a single DIV (this is a custom lightweight component) container with overflow scrolling enabled. It has been said,

on mobile devices specifically ipads there seems to be a lag when scrolling through the long table.

 

In your case, if you're going to show 1000 or more elements, you definitely need to use Virtual List, if you can load them all once in JSON, it's OK, but I guess it could be pretty heavy. AFAIK, SMS don't have this widget.

 

I'd recommended you use Virtual List together with infinite scroll, where you'll add by 100 items to your virtual list, it would be like paging data. 

 

  |          | <- total table area, rows exist in data, but not on DOM
  |xxxxxxx| <- buffer of rows on DOM, but not visible
  |--------|
  |#####| <- visible screen area
  |#####|
  |--------|
  |xxxxxx |
  |          |
 
----------------------------------------------------------------------------------------
Virtual List with infinite scroll component would be very handy.
Link to post
Share on other sites

> I have tested your example using TW3ScrollBox as container in the desktop browser, and is very sluggish on scrolling. 

> I'd put the table into a single DIV (this is a custom lightweight component) container with overflow scrolling enabled. It has

> been said, on mobile devices specifically ipads there seems to be a lag when scrolling through the long table.

 

I'll probably end up creating a custom control that uses MomentumScroller from this thread, and direct html5 table (actually two tables so it would give impression of non scrollable columns header).

MomentumScroller is fine on iPad, and I'll just have to find a solution for Edge. For desktop browser instead of MomentulScroller I may choose W3ScrollBox since it gives more native kind of scrolling.

 

> I'd recommended you use Virtual List together with infinite scroll, where you'll add by 100 items to your virtual list, it would be like paging data. 

 

  |          | <- total table area, rows exist in data, but not on DOM
  |xxxxxxx| <- buffer of rows on DOM, but not visible
 
Do you mean to create rows and cells but not add it to table (delay appendChild till row comes into view)?

That could be a good idea, it should save second or two on large table.

Link to post
Share on other sites

If you want pixel perfect scrolling, use iScroll. It has been 5 years in the making and is the best.

 

The SMS adaptation can be found on my github repo: 
https://github.com/quartexNOR/smartpascal/blob/master/Casebook/qtxScrollController.pas

 

and iScroll.js (copy to $install\libraries folder:

https://github.com/quartexNOR/smartpascal/tree/master/Casebook

 

>> Do you mean to create rows and cells but not add it to table (delay appendChild till row comes into view)?

That could be a good idea, it should save second or two on large table.

 

This is what we do in the DB grid that will be in the next update :)

Link to post
Share on other sites

> If you want pixel perfect scrolling, use iScroll. It has been 5 years in the making and is the best.

 

I've tried it (official demo at http://lab.cubiq.org/iscroll5/demos/simple/) on Edge/IE on win10 tablet and it doesn't work.

I've managed to get your momentumscroller working on Edge and just need to find solution for IE, so I think I'll stick to it for now.


 

> This is what we do in the DB grid that will be in the next update :)

 

I presume it's this one: https://jonlennartaasenden.wordpress.com/2015/12/27/tw3dataset-smart-data-and-what-you-can-do?

 

I have tried it and compared to html5 table it's considerably slower, of course I may not be using it optimally, so I expect results will be better with final version but I think it will be slower compared to table.

Link to post
Share on other sites
  • Moderators

Since JLA's posts on iScroll I'm using that as the preferred scrolling solution.

Works very well. Amazing though there are problems on mobile Win10

 

Before that I implemented scrolling differently, which may or may not be of interest to you

 

1) Change the viewport meta-tag in the html-file by removing the ‘maximum-scale’ and the ‘user-scalable=no’ parameters.  (May not even be necessary in retrospect)
 
2) Enable swiping. A small javascript function on page-load to detect device type and enable swiping
 
3) Re-enable scrolling. In SMS all objects including forms are rendered with a style parameter of "overflow:hidden". This disables scrolling and needs to be changed into "overflow:auto".
  
4) for iOS / Safari only. Add a "-webkit-overflow-scrolling: touch;"  statement to the html, body section of the css file
 
Scrolling is now blindingly fast and kinetic too. 
As a bonus it does not rely on external libraries at all.
 
I've used this extensively on Android and iOS devices, however never on Win10 tablets
Soon as I have a second I'll try it out
 
code for 1) and 2) - the complete custom template file

<!DOCTYPE html>
<html manifest="app.manifest">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=yes">
<meta name="viewport" content="width=device-width, maximum-scale=1.0, initial-scale=1.0, user-scalable=no"/>
 
<title><?pas=Project.Title?></title>
 
<%stylecode%>
<?pas
for var s in Compiler.Resources("text/css") do
PrintLn('<link rel="stylesheet" href="'+s+'">');
for var s in Compiler.Resources("text/javascript") do
PrintLn('<script src="'+s+'" type="text/javascript"></script>');
for var s in Compiler.AppleTouchIcons do
PrintLn('<script src="'+s+'" type="text/javascript"></script>');
?>
 
  <script type="text/javascript">
(function(){
function isTouchDevice(){
try{
document.createEvent("TouchEvent");
return true;
}catch(e){
return false;
}
}
 
function touchScroll(id){
if(isTouchDevice()){ //if touch events exist...
var el=document.getElementById(id);
var scrollStartPos=0;
 
document.getElementById(id).addEventListener("touchstart", function(event) {
scrollStartPos=this.scrollTop+event.touches[0].pageY;
event.preventDefault();
},false);
 
document.getElementById(id).addEventListener("touchmove", function(event) {
this.scrollTop=scrollStartPos-event.touches[0].pageY;
event.preventDefault();
},false);
}
}
 
})();
  </script">
  <script type="text/javascript">
  /* This prevents the window being moved by touches,
     to give the impression of a native app */
  document.ontouchmove = function(e)
  { e.preventDefault(); }
  </script>
 
  <script type="text/javascript">
  /* Note: If you build with the CSS as an external file,
     the app may execute before the CSS has been properly loaded.
     You may want to use a different bootstrap mechanism. jQuery
     provides a good preloading mechanism for instance. */
 
    function bootstrap() { %appcode% }
    window.onload = function () { bootstrap(); touchScroll('OBJ3');  }
  </script>
</head>
 
<body></body>
</html>

 
code for 3)

procedure TForm1.FormActivated;
begin
  inherited;
 
  asm
      var e = document.getElementsByTagName('*');   //re-activate scrolling
      // Loop through elements
      for (var i=0, x=e.length; i<x; i++) {
         if (e[i].className == 'TW3CustomForm') {    //tfrmLoader
//         if (e[i].className.substring(0,3) == 'TW3') {    //all sms classes
              document.getElementById(e[i].id).style.overflow = 'auto';
           }
         }
  end;
 

 
This could have been coded more gracefully but this was early days....
Link to post
Share on other sites

No that is a TW3Dataset implementation.

Im talking about a grid

 

 

 

> If you want pixel perfect scrolling, use iScroll. It has been 5 years in the making and is the best.

 

I've tried it (official demo at http://lab.cubiq.org/iscroll5/demos/simple/) on Edge/IE on win10 tablet and it doesn't work.

I've managed to get your momentumscroller working on Edge and just need to find solution for IE, so I think I'll stick to it for now.


 

> This is what we do in the DB grid that will be in the next update :)

 

I presume it's this one: https://jonlennartaasenden.wordpress.com/2015/12/27/tw3dataset-smart-data-and-what-you-can-do?

 

I have tried it and compared to html5 table it's considerably slower, of course I may not be using it optimally, so I expect results will be better with final version but I think it will be slower compared to table.

 

Link to post
Share on other sites

In most cases you want scrolling inside a container, like a list, so doing a recursive "fixup" of scrolling content is not the best idea.

Simply create a TW3Customcontrol decendant, override StyleTagObject() and change the value there.

You should also add a style-rule for your class in the stylesheet (the CSS name should always match the classname).

 

Scrolling (or auto scrolling) works fine on android, but since iOS requires 2 fingers for momentum scrolling - which is something they added just to make it harder to use, I went for a solution that would work identical on all platforms.

 

As for Windows10, i have yet to update the codebase with their headers. I know they deal with touch differently (microsoft just cant stick to standards), so special care has to be added for that. As for IE, anything below IE9 should not be supported. But IE10 etc. have only a minor difference in scroll functionality and I will test it when that task is due. I usually test on iOS, Android and MS mobile devices.

 

Either way, all of this is being dealt with in the new RTL. Things will be easier to maintain and also to deal with differences.

As for iScroll, that is by far the best solution -- and the only reason it doesnt work on IE is a simple shim. Most notably the lack of [object].indexOf() and various other tidbits. You must always make sure you have the latest IE shims if you target that.

Officially IE is not something we support, its outdated and people still using it should be forced to update -- Spartan should be considered a bare minimum.

 

Do a debug session (if that is even possible in IE) and see what it says -- much easier to track down the culprit code.

Link to post
Share on other sites

This should do the trick for event dispatching, ill look into the scroll part.
 

if (typeof Element.prototype.addEventListener === 'undefined') {
Element.prototype.addEventListener = function (e, callback) {
e = 'on' + e;
return this.attachEvent(e, callback);
};
}

Where Firefox, webkit, opera and everyone else in the world uses "eventname" for addEventListner, Microsoft requires the "on" prefix, which in normal browsers made by sane human beings should only be used on element event attributes (e.g: Self.Handle.onSomeEvent := SomeHandler).

 

Link to post
Share on other sites

Did a quick debug session and found the error:
For some reason the Mozilla driver is created for later IE. They changed the identifier (!) -- which explains a lot.

Good that you brought this up, I actually did not know this (IE is not our primary target. Mobile devices typically run webkit, and our main API target is phonegap).

 

I'll post a quick fix soon!

 

https://www.dropbox.com/sh/e2ya5g8b0pj6s8r/AADuRo8p9f2fXUcTo7gR_7sIa?dl=0

 

As for the DB grid, that was an early prototype written in a couple of hours. The scroll code has been completely re-written and the GPU is used as much as possible everywhere.

 

It goes without saying that "low level" procedural JS and using the browsers built-in scrolling functionality is faster -- there is always a small speed penalty when using OOP based code like SMS generates, but when done right it is hardly noticable. I remember people went nuts when Delphi 1 came out, they were used to Turbo pascal speed and owning the CPU 100% -- and many hated delphi for being slightly "slower" than Turbo.
But after Delphi's RTL got more and more refactored, that speed penalty eventually was gone.
In the VCL today, only the drawing mechanism is slow (the constant allocating of a full bitmap, draw form, then dispose of the bitmap again).

There is a lot of cool stuff in the new RTL that makes scrolling, styling (skinning) and animation easier and faster. Much, much faster.
I have also re-written parts of the framework in pure JS to avoid those extra calls (every call comes with a cost).

If i have time, I might do a pascal port of iScroll, re-writing it in pure pascal just like we did with Sprite3d.

Link to post
Share on other sites

The "bug" if we can call it that was quite simple:

 

Both IE 10, 11 and Edge expose wildly different markers in the user-agent vendor information.

In short: w3_getIsInternetExplorer was unable to recognize the browser as variations of Internet Explorer.

If the browser-driver is completely unable to recognize a vendor (this is the first time that has happened in 4 years), it defaults to the Mozilla behavior.

 

Anyways, here is the fix. Simply replace the existing w3_getIsInternetExplorer() function (SmartCL.System.pas) with this one:

function  w3_getIsInternetExplorer: Boolean;
var
  ua, msie, trident, edge: variant;


  function IE_10_Or_Older: boolean;
  begin
    asm
      @ua = window.navigator.userAgent;
      @msie = (@ua).indexOf('MSIE ');
      @result = (@msie > 0);
    end;
  end;

  function IE_11: boolean;
  begin
    asm
      @ua = window.navigator.userAgent;
      @trident = (@ua).indexOf('Trident/');
      @result = (@trident > 0);
    end;
  end;

  function IE_Edge: boolean;
  begin
    asm
      @ua = window.navigator.userAgent;
      @edge = (@ua).indexOf('Edge/');
      @result = (@edge > 0);
    end;
  end;

begin
  result := IE_Edge or IE_11 or IE_10_Or_Older;
  {$IFDEF DEBUG}
  asm
    if (@result === true)
      console.log("Internet Explorer detected");
  end;
  {$ENDIF}
end;

I did however notice that the scrolling was not as smooth under IE as it is under webkit.

It's probably falling back to the requestAnimationFrame() shim, so that will have to be dealt with next.

Link to post
Share on other sites

> Good that you brought this up, I actually did not know this (IE is not our primary target. Mobile devices typically run webkit, and our

> main API target is phonegap).

 

> I'll post a quick fix soon!

 

Thanks for the quick solution, I appreciate it.

I have to target all browsers and all devices (android tablets, desktop browsers, iPad, win tablets) and so I have to support Edge and latest IE (that's 11 I think).

 

I've done some more testing and setting overflow-y to auto on IE and Edge brings their default touch scroll handling and that works quite well.

Potential solution is to use momentum scroller in all browsers except for IE/Edge and use auto scrolling there.

 

> As for the DB grid, that was an early prototype written in a couple of hours. The scroll code has been completely re-written and the

> GPU is used as much as possible everywhere.

 

I'm looking forward to it, I have to use html5 table for now but if new DB grid has comparable performance I will defintely switch to it.

> If i have time, I might do a pascal port of iScroll, re-writing it in pure pascal just like we did with Sprite3d.

 

I haven't tried iScroll for my table, since your momentum scroller works well on iPad and Win10 tablet (in Chrome), I don't see any major differences compared to official iScroll demo or other pages using iScroll, so you've done a really good job already, I don't think it's necessary to port whole iScroll.

Link to post
Share on other sites

The biggest change is basically organization. We need better separation between DOM and non-DOM code (nodejs, io and embedded), so the RTL has been divided into more distinct namespaces. Universal and abstract classes are moved into the system namespace, SmartCL should contain DOM only code, and nodeJS will naturally have its own high-level interface.

We have also set a standard when it comes to files and filesystem. So you have an abstract filesystem defined in system, which is then implemented for each target. So if you use phonegap, then the filesystem will use those functions, if you use node then you get the native versions there -- and so on.

As a user you dont have to care about that, just use the FileSystem classes and the linker will pick the correct version depending on your target.

 

So there is a lot of over-due "cleanup" of classes, concepts and units.

I am also going over every component we have, re-writing a lot of the code from scratch.

 

Then you have stuff like TAction's which we have in Delphi, that is already done. And finally a unified DB API.

 

It takes time because we are dealing with 3 platforms and multiple targets. NodeWebkit for instance is a bit of a freak since it allows both node-level access to the system AND full DOM access.

 

Up until now we have basically provided pascal access (or implementations) of JS functionality. This has caused some confusion for many people, who expect object pascal standards to be in place. So thats what I am going for now - setting some standards (like filesystem being a standard abstract class, which is implemented for each target) that delivers the same behavior regardless of platform.

 

The scroll stuff is a good example. When possible I try to write it in object pascal to avoid differences in behavior. But for best performance i will probably have to optimize for each browser type. Scrolling with CSS3 has worse performance in IE than using an animation with a matrix ease function -- so "smarting up" some of the controls is important.

 

Non visual components is also in the RTL now, which should simplify things considerably.

 

At the same time im writing a book, Smart Mobile Studio Unleashed, that teaches hard-core SMS coding - with focus on control writing, nodejs clustering and services. So its quite a job.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...