Jump to content

lynkfs

Moderators
  • Content Count

    743
  • Joined

  • Last visited

  • Days Won

    146

Reputation Activity

  1. Like
    lynkfs got a reaction from Dany in Including a neural network   
    Hi Christian
    I like the way your brain works (pun intended
     
    What I like about your approach is that it avoids any asm code, that it works through all of the js library code (‘code is truth’), that it encourages type safe operation and that it explains the class external feature.
     
    Personally I tend to treat any js library as much as a black box as possible. That suits me fine as I’m usually too impatient to try and grasp all of someone elses js code intricacies. 
     
    So far I've approached this by looking at the calling conventions. If for instance the library documentation states that the syntax to put some text into a pdf document is ‘pdfdoc.Text(left, top, ‘text’) then I can be pretty confident that soon as I have established a handle to the js class I can write in pascal ‘PDF1.Text(left, top, ‘text’);
     
    The downside of this is that I need a single asm code-line in the wrapper unit. 
     asm (@self.FHandle) = new jsPDF(); end; I’ve convinced myself that I can live with that though.
     
    Thanks for your reply. I’ve been hanging out for a discussion or documentation on how to best wrap external js libraries for a long time. I’ve only done a couple so far but pieces are falling into place.
  2. Like
    lynkfs reacted to warleyalex in My showcase area   
    These projects were tested with Smart Mobile Studio version 2.2.0.4093
     
     

      |||||||||||| W E B  A P P ||||||||||||
    SmartMSFW7
    SmartMS web app with Framework7  
     
    |||||||||||| F O R M S ||||||||||||
    HelloWorld
    Develop a little greeting app using SMS 
     
    TabBar
    TabBar visual component 
     
    ScrollList
    This is a scroll-able control uses iScroll 
     
    EditBoxSpinEdit
    Use an EditBox as a SpinEdit Control
     
    RadioButtonDemo
    Radio button Control adapted from CheckBox
     
    HttpDemo
    Loading remote JSON and local data
     
    ToggleSwitch
    Using a TW3ToggleSwitch to toggle the value of a property 
     
    SelectionComboBox
    How to use a combo box on a form 
     
    MemoSaveLoad
    How to use a memo on a form 
     
    TW3Slider
    Using a TW3Slider to control the value of a property 
     
    TW3Chart
    Using bar charts and pie charts to display data 
     
    TW3Grid
    Using a TW3Grid to display the contents of records 
     
    MultipleForms
    How to use several forms 
     
    SwitchForm
    Mini Wizard like application 
     
    CladView
    It shows some NESTED Layouts 
     
    Semaforo
    It shows a transit semaphore 
     
    |||||||||||| GAMES / GRAPHICS ||||||||||||
    PlatformDemo
    User presses keys to control players' jumps between platforms  
     
    BallTrajectory
    Ball moves under the effects of gravity, friction and restitution 
     
    BlendingEllipses
    Draws randomly positioned, sized and coloured ellipses with alpha blending 
     
    Cool
    User shoots and collides with enemies 
     
    MaxCircles
    Draws coloured circles that fit exactly into available space 
     
    MazeOnCube
    [bUser navigates maze on cube faces and seeks target[/b] 
     
    MovingBallWithParticles
    Ball emits stars of different colours as it bounces round a container 
     
    ObjectMovingBalls
    Draws coloured circles moving at different speeds around a square 
     
    RandomPlatformScroller
    User jumps and falls to reach door to next level 
     
    RetroKeeper3D
    Keeper tries to save shots directed at corners of goal 
     
    RoamingBlockBuster
    User busts the randomly moving blocks by firing at them 
     
    SpaceInvaders
    User avoids invaders and shoots them 
     
    TowerOfArcher
    User aims arrows at enemies that move along the ground or through the air 
     
    HorrorGame
    Demonstrates CSS3 Animation in SMS 
     
    AnimatedTitle
    Rotating a title, bouncing title  
     
    Knowledge
    Keep the taxi on the road 
     
    Snake
    Snake that grows when it eats 
     
    MazePlus
    User navigates maze on cube faces and seeks target  
     
    Invader
    Including audio, mouse input, the drawing of circles and text and alpha blending 
     
    ForegroundMove
    This little demo shows how to use an image (in this case a wall) to show a moving foreground.
     
    BitmappedText
    Showing a scrolling message and stationary text in a bitmapped font
     
    SpriteCanvas
    How to use a sprite in a Smart Pascal canvas project
     
    Isabella
    Displays 3,000 individually animated Sprites
  3. Like
    lynkfs got a reaction from jorn in Including a neural network   
    If you want to include a neural network into your app, you can use the unit below which basically is a wrapper around the brain.js library
     
    The unit exposes 'AddExample', 'Train' and 'Run' as the main procs, which speak for themselves. 
     
    As an example the following neural network is defined with 3 inputs (r, g, b and 3 possible outputs (orange, green and purple).
    To train the network 4 examples are provided which should be enough to get reasonable results.
    Running the network on inputs r,g,b = 1,1,0 gives as outcome 'orange' as the best result (approx 80% certainty)
    procedure TForm1.W3Button1Click(Sender: TObject); var   TrainingInputs: Array of float;   RunInputs: Array of float;   outcome : array of float;   MinVal, MaxVal : Float;   HighInt : Integer; begin // //Define inputs and possible outcomes   Brain1.InputArray  := ['r', 'g', 'b'];   Brain1.OutputArray := ['orange', 'green', 'purple'];   // //Give some examples // //training example 1   TrainingInputs := [1, 0.65, 0];   Brain1.AddExample(TrainingInputs,'orange'); // better : Brain1.OutputArray[0]);   //training example 2   TrainingInputs.Clear;   TrainingInputs := [0, 0.54, 0];   Brain1.AddExample(TrainingInputs,Brain1.OutputArray[1]); //output : green   //training example 3   TrainingInputs.Clear;   TrainingInputs := [0.6, 1, 0.5];   Brain1.AddExample(TrainingInputs,Brain1.OutputArray[1]); //output : green   //training example 4   TrainingInputs.Clear;   TrainingInputs := [0.67, 0, 1];   Brain1.AddExample(TrainingInputs,Brain1.OutputArray[2]); //output : purple   //Train the network   Brain1.Train;   //Run the network on a specific set of input values   RunInputs := [1, 1, 0];     outcome := Brain1.Run(RunInputs);   //Get the highest score   minval := outcome[0];   maxval := outcome[0];   highint := 0;   for var i := 1 to outcome.Count-1 do   begin   if outcome[i]<minval then     minval := outcome[i]   else if outcome[i]>maxval then     begin       maxval := outcome[i];       highint := i;     end;   end;   ShowMessage ('r=1, g=1 and b=0 gives ''' + Brain1.OutputArray[highint] +                ''' with certainty of ' + IntToStr(Integer(maxval * 100)) + ' %');   end;   procedure TForm1.InitializeForm; begin   inherited;   // this is a good place to initialize components   Brain1 := TBrain.Create; end; The brain unit
    Note the use of the 'namepair' unit which at present is not part of the latest Smart release, but was posted by Jon Aasenden a while ago. It enables the writing of records / namepairs with a variable nr of fields. Cool.
      unit Brain;   interface   uses    SmartCL.System, system.types, namepair;   type     TBrainHandle = THandle;     EBrain = class(EW3Exception);   TBrain = class(TObject)   private     FHandle:      TBrainHandle;     exin, exout, example:  Variant;     NrofTrainingExamples : Integer := 0;   protected     Procedure     CreateBrain;     procedure     ReleaseBrain;   public     Property      Handle:TBrainHandle read FHandle;       Procedure     InitialiseAPIData;     Procedure     AddExample (TrainingInputs: Array of float; TrainingOutput: String);     Procedure     Train;     Function      Run(RunInputs : Array of float) : Array of Float;       Constructor   Create;virtual;     Destructor    Destroy;Override;     HiddenLayer:  Integer := 4;     InputArray, OutputArray: Array of String;   end;   Procedure BrainInitialize;overload; Procedure BrainInitialize(DriverURL:String);overload; function  BrainReady:Boolean;   implementation   uses SmartCL.fileutils;     {$R 'brain-0.6.3.js'}   var   __BrainREADY:  Boolean := False;     Procedure TBrain.CreateBrain; begin   if (FHandle) then     ReleaseBrain;     asm     (@self.FHandle) = new brain.NeuralNetwork({        hiddenLayers: [@HiddenLayer]     });   end;     InitialiseAPIData;   end;   procedure TBrain.ReleaseBrain; begin   if FHandle then   begin     FHandle:=TVariant.CreateObject;     FHandle:=NULL;   end; end;   Constructor TBrain.Create; begin   inherited Create;     try     CreateBrain;   except     on e: exception do     Raise Exception.Create(e.message);   end; end;   Destructor TBrain.Destroy; begin   if (FHandle) then     ReleaseBrain;   inherited; end;   Procedure TBrain.AddExample (TrainingInputs: Array of float; TrainingOutput: String); begin   var Structure := TW3JsonStructure.Create(null);   var JSONData:String;     Structure.Clear;   For var i := 0 to TrainingInputs.Count-1 do begin     Structure.WriteFloat(InputArray[i], TrainingInputs[i]);   end;   Structure.SaveToJSON(JSONData);   exin[NrofTrainingExamples] := JSon.Parse(JSonData);     Structure.Clear;   Structure.WriteInt(TrainingOutput,1);   Structure.SaveToJSON(JSONData);   exout[NrofTrainingExamples] := JSon.Parse(JSonData);     Inc(NrofTrainingExamples); end;   Procedure TBrain.Train; var   ExampleArray: Array of variant; begin   if (FHandle) then begin     For var i := 0 to NrofTrainingExamples-1 do begin       example[i] := TVariant.CreateObject;       example[i].input := exin[i];       example[i].output := exout[i];     end;       ExampleArray.Clear;     For var j := 0 to NrofTrainingExamples-1 do begin       ExampleArray[j] := example[j];     end; //    asm alert(JSON.stringify(@ExampleArray)); end;     FHandle.train(ExampleArray);     end; end;   Function TBrain.Run(RunInputs : Array of float) : Array of Float; var   MyOutputArray : Array of float;   outcome: variant;   RunValues : Variant; begin   if (FHandle) then begin       var Structure := TW3JsonStructure.Create(null);     var JSONData:String;     Structure.Clear;     For var i := 0 to RunInputs.Count-1 do begin       Structure.WriteFloat(InputArray[i], RunInputs[i]);     end;     Structure.SaveToJSON(JSONData);     RunValues := JSon.Parse(JSonData); //    asm alert(JSON.stringify(@RunValues)); end;       outcome := FHandle.run(RunValues);     asm       var j = 0;       for (var i in @outcome)  {       @MyOutputArray[j] = @outcome[i];       j=j+1;  }     end;     Result := MyOutputArray;   end; end;   Procedure TBrain.InitialiseAPIData; begin   NrofTrainingExamples := 0;     if not TVariant.IsNull(exin)   and not exin.IsUnassigned     then exin.Destroy;   exin  := TVariant.CreateObject;     if not TVariant.IsNull(exout)   and not exout.IsUnassigned     then exout.Destroy;   exout := TVariant.CreateObject;     if not TVariant.IsNull(example)   and not example.IsUnassigned     then example.Destroy;   example := TVariant.CreateObject; end;   Procedure BrainInitialize(DriverURL:String);overload; begin   if not __BrainREADY then   begin     try       writeln("Loading Brain driver");       TW3Storage.LoadScript(DriverURL,         procedure ()         begin           writeln("Brain Driver loaded and ready");           w3_setTimeout( procedure ()             begin               __BrainREADY := true;             end, 20);         end);     except       on e: exception do       Raise EBrain.CreateFmt       ("Failed to load Brain driver, system threw exception %s with [%s]",       [e.classname,e.message]);     end;   end; end;   Procedure BrainInitialize; begin   BrainInitialize("/lib/brain-0.6.3.js"); end;   function  BrainReady:Boolean; begin   result:=__BrainREADY; end;   initialization begin   __BrainReady:=False;   BrainInitialize; end;   end.   The namepair code can be found on http://smartmobilestudio.com/author/lennart/ posted by Jon Aasenden on 26 sept 2015
     
     
     
     
  4. Like
    lynkfs got a reaction from Czar in Cloudinary image management   
    A while ago I got the request to make an image gallery.
    Not the most challenging of tasks maybe, but after some discussion and thinking about it the list of requirements did become rather large.
      What about having to provide an easy management system of uploaded images, making it fit for all mobile and desktop devices - not only screen sizes but also resolution, building an easy upload facility of images, include a one-step upload process using the device camera if there is one, automatic client-side re-sampling to reduce file size etc.   Fortunately there are a number of mature image management systems available, so not everything needs to be developed from scratch. The choice for this project was to have a deeper look at Cloudinary (www.cloudinary.com).   Cloudinary provides image (and video) management for web developers which includes image upload services, cloud storage, administration management and especially image manipulation, transformation services and fast delivery. And it offers all of this as a freemium service : free up to a (rather large) number of uploaded images, with a subscription mechanism for larger quantities.   Accessing uploaded images is as simple as    //  AString := 'res/myimage01.png';      AString := 'http://res.cloudinary.com/your-account/image/upload/' +              'w_' + inttostr(W3Image.Width) +              ',h_' + inttostr(W3Image.Height) +              ',dpr_' + GetDPR +              ',c_fill/v1438317145/myimage01_abcdef.png';       W3Image1.LoadFromURL(AString);   The good thing is that the image is on the fly server-side re-sized to the right dimensions (w_ and h_ parameters), that it fills this dimension (c_fill parameter) and that it fits the correct device pixel ratio, which covers anything from low-res to retina displays. The GetDPR function is included at the end of this post.     To get the available images I used a small server-side php file to serve this content and make it available in array-format.   //  cloudinary.php :   <?php   header("Access-Control-Allow-Origin: *");   $url_cloudinary = $_POST['url_cloudinary'];   $arr = file_get_contents($url_cloudinary);   echo $arr;   ?>   The first header line is superfluous if this file is located on the same server location as our project. The second line reads the Cloudinary account information as a parameter while the third and fourth line populate and serve the info of available images. Obviously this approach has an upper limit in what the functions used (file_get_contents, echo) can handle but for a reasonable number of images this works fine.   The gallery-page has a scrollable panel and initialises as :   procedure TForm1.FormActivated; begin   inherited;   If not assigned(FHttp) then begin     FHttp := TW3HttpRequest.Create;     ReadImages;   end; end;   procedure TForm1.ReadImages; var   cloudinaryurl: string; begin                                                //retrieve list     FHttp.OnDataReady := RetrieveImages;     cloudinaryurl := 'https://123456789012345:abcdefghijklmnopqrstuvwxyz@api.cloudinary.com/v1_1/your-account/resources/image/upload/?max_results=500';   FHttp.open("POST","http://..wherever../cloudinary.php");   //where the php file resides   FHttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");   FHttp.send('url_cloudinary=' + cloudinaryurl);   FullScreenSpinner.Enter;   end;   This is actually a bad design. The cloudinaryurl account parameter contains access and password information and should not be send from the client. Better to hard-code it server-side in the php file rather than sending it as a parameter.   Once the image info is available the ondataready callback is invoked. This procedure sets up the scroll-environment and populates it with all images as 70 x 70 thumbnails. It also sets up a larger image at the end, which will be populated with a larger version of the thumbnail when pressed/clicked.   procedure TForm1.RetrieveImages(Sender: TW3HttpRequest); begin //showmessage(sender.responsetext);                     //callback 'ondataready'   cursor := JSON.parse(FHttp.ResponseText);   MaxRows := cursor.resources.length;   FullScreenSpinner.Leave;     Panel1 := TW3Panel.Create(self);   ScrollElement1 := TW3ScrollWindow.Create(Panel1);   Panel1.SetBounds(0,20,ClientWidth, ClientHeight-40);   ScrollElement1.SetBounds(0,0,panel1.width-40,panel1.height);   dy := 5;   dx := 25;     For var i := 0 to MaxRows-1 do begin     PopuLateScrollList(i);   end;     inc(dy,90);   BigImage:=TW3Image.Create(ScrollElement1.Content);     BigImage.SetBounds(trunc(Panel1.Width * 0.15),                      dy+35,                      trunc(Panel1.Width * 0.7),                      trunc(Panel1.Width * 0.7));   AString := 'http://res.cloudinary.com/your-account/image/upload/' +              'w_' + inttostr(BigImage.Width) +              ',h_' + inttostr(BigImage.Height) +              ',c_pad/v' +              cursor.resources[0].version + '/' +              cursor.resources[0].public_id + '.' +              cursor.resources[0].format;   BigImage.LoadFromURL(AString);     ScrollElement1.Content.Height:=dy + trunc(Panel1.Width * 0.7)+70; //Size content to all images   ScrollElement1.ScrollAPI.Refresh; //Update IScroll   scrollelement1.ScrollApi.Options.VerticalScrollbar := true;   ScrollElement1.SetBounds(0,0,panel1.width,panel1.height); end;   Procedure TForm1.PopulateScrollList(row:integer); var   mImage: TW3Image;   mLabel: TW3Label; Begin   mImage := TW3Image.Create(ScrollElement1.Content);   mImage.SetBounds(dx,dy,70,70);   AString := 'http://res.cloudinary.com/your-account/image/upload/' +              'w_70,h_70,c_thumb/v' +              cursor.resources[row].version + '/' +              cursor.resources[row].public_id + '.' +              cursor.resources[row].format;   mImage.LoadFromURL(AString);   w3_setStyle(mImage.Handle,'cursor','pointer');   mImage.OnClick := procedure(Sender:TObject)   begin     FullScreenSpinner.Enter;     AString := 'http://res.cloudinary.com/your-account/image/upload/' +                'w_' + inttostr(BigImage.Width) +                ',h_' + inttostr(BigImage.Height) +                ',c_pad/v' +                cursor.resources[row].version + '/' +                cursor.resources[row].public_id + '.' +                cursor.resources[row].format;     AIndex := row;     BigImage.LoadFromURL(AString);     ScrollElement1.ScrollApi.ScrollTo(0,-BigImage.Top,1,false);     FullScreenSpinner.Leave;   end; //   mLabel:=TW3Label.Create(ScrollElement1.Content);   mLabel.setBounds(dx,dy+75,70,16);   mLabel.Caption := 'Image' + inttostr(row+1);   mLabel.OnClick := procedure (sender: TObject)   begin     AIndex := row;   end;     inc(dx,75);   if dx > Panel1.width - 85 then   begin     dx := 25;     inc(dy,95);   end;   end;   The last piece of the puzzle is the use of the device camera. Cloudinary has a simple widget which takes care of that.      W3Button1.OnClick :=     procedure (Sender: TObject)     begin       asm         cloudinary.openUploadWidget({ cloud_name: 'your-account',                                   upload_preset: 'your-account',                                   cropping: 'server'});       end;     end;   Select the camera, take a picture and upload in one go.   Function TForm1.GetDPR:String; var   DPRString: String;   DPR : Float;   DPR_Supported_F : Array of Float;   DPR_Supported_S : Array of String; begin   DPRString := '1';   DPR := BrowserApi.DevicePixelRatio;   DPR_Supported_F := [0.75, 1.0, 1.3, 1.5, 2.0, 3.0];   DPR_Supported_S := ['0.75', '1.0', '1.3', '1.5', '2.0', '3.0'];   for var x := Low(DPR_Supported_F) to High(DPR_Supported_F) do begin     if DPR_Supported_F[x] >= DPR then begin       DPR := DPR_Supported_F[x];       DPRString := DPR_Supported_S[x];       Break;     end;   end;   Result := DPRString; //  ShowMessage(Result); end;  

  5. Like
    lynkfs got a reaction from Dany in Cloudinary image management   
    A while ago I got the request to make an image gallery.
    Not the most challenging of tasks maybe, but after some discussion and thinking about it the list of requirements did become rather large.
      What about having to provide an easy management system of uploaded images, making it fit for all mobile and desktop devices - not only screen sizes but also resolution, building an easy upload facility of images, include a one-step upload process using the device camera if there is one, automatic client-side re-sampling to reduce file size etc.   Fortunately there are a number of mature image management systems available, so not everything needs to be developed from scratch. The choice for this project was to have a deeper look at Cloudinary (www.cloudinary.com).   Cloudinary provides image (and video) management for web developers which includes image upload services, cloud storage, administration management and especially image manipulation, transformation services and fast delivery. And it offers all of this as a freemium service : free up to a (rather large) number of uploaded images, with a subscription mechanism for larger quantities.   Accessing uploaded images is as simple as    //  AString := 'res/myimage01.png';      AString := 'http://res.cloudinary.com/your-account/image/upload/' +              'w_' + inttostr(W3Image.Width) +              ',h_' + inttostr(W3Image.Height) +              ',dpr_' + GetDPR +              ',c_fill/v1438317145/myimage01_abcdef.png';       W3Image1.LoadFromURL(AString);   The good thing is that the image is on the fly server-side re-sized to the right dimensions (w_ and h_ parameters), that it fills this dimension (c_fill parameter) and that it fits the correct device pixel ratio, which covers anything from low-res to retina displays. The GetDPR function is included at the end of this post.     To get the available images I used a small server-side php file to serve this content and make it available in array-format.   //  cloudinary.php :   <?php   header("Access-Control-Allow-Origin: *");   $url_cloudinary = $_POST['url_cloudinary'];   $arr = file_get_contents($url_cloudinary);   echo $arr;   ?>   The first header line is superfluous if this file is located on the same server location as our project. The second line reads the Cloudinary account information as a parameter while the third and fourth line populate and serve the info of available images. Obviously this approach has an upper limit in what the functions used (file_get_contents, echo) can handle but for a reasonable number of images this works fine.   The gallery-page has a scrollable panel and initialises as :   procedure TForm1.FormActivated; begin   inherited;   If not assigned(FHttp) then begin     FHttp := TW3HttpRequest.Create;     ReadImages;   end; end;   procedure TForm1.ReadImages; var   cloudinaryurl: string; begin                                                //retrieve list     FHttp.OnDataReady := RetrieveImages;     cloudinaryurl := 'https://123456789012345:abcdefghijklmnopqrstuvwxyz@api.cloudinary.com/v1_1/your-account/resources/image/upload/?max_results=500';   FHttp.open("POST","http://..wherever../cloudinary.php");   //where the php file resides   FHttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");   FHttp.send('url_cloudinary=' + cloudinaryurl);   FullScreenSpinner.Enter;   end;   This is actually a bad design. The cloudinaryurl account parameter contains access and password information and should not be send from the client. Better to hard-code it server-side in the php file rather than sending it as a parameter.   Once the image info is available the ondataready callback is invoked. This procedure sets up the scroll-environment and populates it with all images as 70 x 70 thumbnails. It also sets up a larger image at the end, which will be populated with a larger version of the thumbnail when pressed/clicked.   procedure TForm1.RetrieveImages(Sender: TW3HttpRequest); begin //showmessage(sender.responsetext);                     //callback 'ondataready'   cursor := JSON.parse(FHttp.ResponseText);   MaxRows := cursor.resources.length;   FullScreenSpinner.Leave;     Panel1 := TW3Panel.Create(self);   ScrollElement1 := TW3ScrollWindow.Create(Panel1);   Panel1.SetBounds(0,20,ClientWidth, ClientHeight-40);   ScrollElement1.SetBounds(0,0,panel1.width-40,panel1.height);   dy := 5;   dx := 25;     For var i := 0 to MaxRows-1 do begin     PopuLateScrollList(i);   end;     inc(dy,90);   BigImage:=TW3Image.Create(ScrollElement1.Content);     BigImage.SetBounds(trunc(Panel1.Width * 0.15),                      dy+35,                      trunc(Panel1.Width * 0.7),                      trunc(Panel1.Width * 0.7));   AString := 'http://res.cloudinary.com/your-account/image/upload/' +              'w_' + inttostr(BigImage.Width) +              ',h_' + inttostr(BigImage.Height) +              ',c_pad/v' +              cursor.resources[0].version + '/' +              cursor.resources[0].public_id + '.' +              cursor.resources[0].format;   BigImage.LoadFromURL(AString);     ScrollElement1.Content.Height:=dy + trunc(Panel1.Width * 0.7)+70; //Size content to all images   ScrollElement1.ScrollAPI.Refresh; //Update IScroll   scrollelement1.ScrollApi.Options.VerticalScrollbar := true;   ScrollElement1.SetBounds(0,0,panel1.width,panel1.height); end;   Procedure TForm1.PopulateScrollList(row:integer); var   mImage: TW3Image;   mLabel: TW3Label; Begin   mImage := TW3Image.Create(ScrollElement1.Content);   mImage.SetBounds(dx,dy,70,70);   AString := 'http://res.cloudinary.com/your-account/image/upload/' +              'w_70,h_70,c_thumb/v' +              cursor.resources[row].version + '/' +              cursor.resources[row].public_id + '.' +              cursor.resources[row].format;   mImage.LoadFromURL(AString);   w3_setStyle(mImage.Handle,'cursor','pointer');   mImage.OnClick := procedure(Sender:TObject)   begin     FullScreenSpinner.Enter;     AString := 'http://res.cloudinary.com/your-account/image/upload/' +                'w_' + inttostr(BigImage.Width) +                ',h_' + inttostr(BigImage.Height) +                ',c_pad/v' +                cursor.resources[row].version + '/' +                cursor.resources[row].public_id + '.' +                cursor.resources[row].format;     AIndex := row;     BigImage.LoadFromURL(AString);     ScrollElement1.ScrollApi.ScrollTo(0,-BigImage.Top,1,false);     FullScreenSpinner.Leave;   end; //   mLabel:=TW3Label.Create(ScrollElement1.Content);   mLabel.setBounds(dx,dy+75,70,16);   mLabel.Caption := 'Image' + inttostr(row+1);   mLabel.OnClick := procedure (sender: TObject)   begin     AIndex := row;   end;     inc(dx,75);   if dx > Panel1.width - 85 then   begin     dx := 25;     inc(dy,95);   end;   end;   The last piece of the puzzle is the use of the device camera. Cloudinary has a simple widget which takes care of that.      W3Button1.OnClick :=     procedure (Sender: TObject)     begin       asm         cloudinary.openUploadWidget({ cloud_name: 'your-account',                                   upload_preset: 'your-account',                                   cropping: 'server'});       end;     end;   Select the camera, take a picture and upload in one go.   Function TForm1.GetDPR:String; var   DPRString: String;   DPR : Float;   DPR_Supported_F : Array of Float;   DPR_Supported_S : Array of String; begin   DPRString := '1';   DPR := BrowserApi.DevicePixelRatio;   DPR_Supported_F := [0.75, 1.0, 1.3, 1.5, 2.0, 3.0];   DPR_Supported_S := ['0.75', '1.0', '1.3', '1.5', '2.0', '3.0'];   for var x := Low(DPR_Supported_F) to High(DPR_Supported_F) do begin     if DPR_Supported_F[x] >= DPR then begin       DPR := DPR_Supported_F[x];       DPRString := DPR_Supported_S[x];       Break;     end;   end;   Result := DPRString; //  ShowMessage(Result); end;  

  6. Like
    lynkfs got a reaction from jorn in Cloudinary image management   
    A while ago I got the request to make an image gallery.
    Not the most challenging of tasks maybe, but after some discussion and thinking about it the list of requirements did become rather large.
      What about having to provide an easy management system of uploaded images, making it fit for all mobile and desktop devices - not only screen sizes but also resolution, building an easy upload facility of images, include a one-step upload process using the device camera if there is one, automatic client-side re-sampling to reduce file size etc.   Fortunately there are a number of mature image management systems available, so not everything needs to be developed from scratch. The choice for this project was to have a deeper look at Cloudinary (www.cloudinary.com).   Cloudinary provides image (and video) management for web developers which includes image upload services, cloud storage, administration management and especially image manipulation, transformation services and fast delivery. And it offers all of this as a freemium service : free up to a (rather large) number of uploaded images, with a subscription mechanism for larger quantities.   Accessing uploaded images is as simple as    //  AString := 'res/myimage01.png';      AString := 'http://res.cloudinary.com/your-account/image/upload/' +              'w_' + inttostr(W3Image.Width) +              ',h_' + inttostr(W3Image.Height) +              ',dpr_' + GetDPR +              ',c_fill/v1438317145/myimage01_abcdef.png';       W3Image1.LoadFromURL(AString);   The good thing is that the image is on the fly server-side re-sized to the right dimensions (w_ and h_ parameters), that it fills this dimension (c_fill parameter) and that it fits the correct device pixel ratio, which covers anything from low-res to retina displays. The GetDPR function is included at the end of this post.     To get the available images I used a small server-side php file to serve this content and make it available in array-format.   //  cloudinary.php :   <?php   header("Access-Control-Allow-Origin: *");   $url_cloudinary = $_POST['url_cloudinary'];   $arr = file_get_contents($url_cloudinary);   echo $arr;   ?>   The first header line is superfluous if this file is located on the same server location as our project. The second line reads the Cloudinary account information as a parameter while the third and fourth line populate and serve the info of available images. Obviously this approach has an upper limit in what the functions used (file_get_contents, echo) can handle but for a reasonable number of images this works fine.   The gallery-page has a scrollable panel and initialises as :   procedure TForm1.FormActivated; begin   inherited;   If not assigned(FHttp) then begin     FHttp := TW3HttpRequest.Create;     ReadImages;   end; end;   procedure TForm1.ReadImages; var   cloudinaryurl: string; begin                                                //retrieve list     FHttp.OnDataReady := RetrieveImages;     cloudinaryurl := 'https://123456789012345:abcdefghijklmnopqrstuvwxyz@api.cloudinary.com/v1_1/your-account/resources/image/upload/?max_results=500';   FHttp.open("POST","http://..wherever../cloudinary.php");   //where the php file resides   FHttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");   FHttp.send('url_cloudinary=' + cloudinaryurl);   FullScreenSpinner.Enter;   end;   This is actually a bad design. The cloudinaryurl account parameter contains access and password information and should not be send from the client. Better to hard-code it server-side in the php file rather than sending it as a parameter.   Once the image info is available the ondataready callback is invoked. This procedure sets up the scroll-environment and populates it with all images as 70 x 70 thumbnails. It also sets up a larger image at the end, which will be populated with a larger version of the thumbnail when pressed/clicked.   procedure TForm1.RetrieveImages(Sender: TW3HttpRequest); begin //showmessage(sender.responsetext);                     //callback 'ondataready'   cursor := JSON.parse(FHttp.ResponseText);   MaxRows := cursor.resources.length;   FullScreenSpinner.Leave;     Panel1 := TW3Panel.Create(self);   ScrollElement1 := TW3ScrollWindow.Create(Panel1);   Panel1.SetBounds(0,20,ClientWidth, ClientHeight-40);   ScrollElement1.SetBounds(0,0,panel1.width-40,panel1.height);   dy := 5;   dx := 25;     For var i := 0 to MaxRows-1 do begin     PopuLateScrollList(i);   end;     inc(dy,90);   BigImage:=TW3Image.Create(ScrollElement1.Content);     BigImage.SetBounds(trunc(Panel1.Width * 0.15),                      dy+35,                      trunc(Panel1.Width * 0.7),                      trunc(Panel1.Width * 0.7));   AString := 'http://res.cloudinary.com/your-account/image/upload/' +              'w_' + inttostr(BigImage.Width) +              ',h_' + inttostr(BigImage.Height) +              ',c_pad/v' +              cursor.resources[0].version + '/' +              cursor.resources[0].public_id + '.' +              cursor.resources[0].format;   BigImage.LoadFromURL(AString);     ScrollElement1.Content.Height:=dy + trunc(Panel1.Width * 0.7)+70; //Size content to all images   ScrollElement1.ScrollAPI.Refresh; //Update IScroll   scrollelement1.ScrollApi.Options.VerticalScrollbar := true;   ScrollElement1.SetBounds(0,0,panel1.width,panel1.height); end;   Procedure TForm1.PopulateScrollList(row:integer); var   mImage: TW3Image;   mLabel: TW3Label; Begin   mImage := TW3Image.Create(ScrollElement1.Content);   mImage.SetBounds(dx,dy,70,70);   AString := 'http://res.cloudinary.com/your-account/image/upload/' +              'w_70,h_70,c_thumb/v' +              cursor.resources[row].version + '/' +              cursor.resources[row].public_id + '.' +              cursor.resources[row].format;   mImage.LoadFromURL(AString);   w3_setStyle(mImage.Handle,'cursor','pointer');   mImage.OnClick := procedure(Sender:TObject)   begin     FullScreenSpinner.Enter;     AString := 'http://res.cloudinary.com/your-account/image/upload/' +                'w_' + inttostr(BigImage.Width) +                ',h_' + inttostr(BigImage.Height) +                ',c_pad/v' +                cursor.resources[row].version + '/' +                cursor.resources[row].public_id + '.' +                cursor.resources[row].format;     AIndex := row;     BigImage.LoadFromURL(AString);     ScrollElement1.ScrollApi.ScrollTo(0,-BigImage.Top,1,false);     FullScreenSpinner.Leave;   end; //   mLabel:=TW3Label.Create(ScrollElement1.Content);   mLabel.setBounds(dx,dy+75,70,16);   mLabel.Caption := 'Image' + inttostr(row+1);   mLabel.OnClick := procedure (sender: TObject)   begin     AIndex := row;   end;     inc(dx,75);   if dx > Panel1.width - 85 then   begin     dx := 25;     inc(dy,95);   end;   end;   The last piece of the puzzle is the use of the device camera. Cloudinary has a simple widget which takes care of that.      W3Button1.OnClick :=     procedure (Sender: TObject)     begin       asm         cloudinary.openUploadWidget({ cloud_name: 'your-account',                                   upload_preset: 'your-account',                                   cropping: 'server'});       end;     end;   Select the camera, take a picture and upload in one go.   Function TForm1.GetDPR:String; var   DPRString: String;   DPR : Float;   DPR_Supported_F : Array of Float;   DPR_Supported_S : Array of String; begin   DPRString := '1';   DPR := BrowserApi.DevicePixelRatio;   DPR_Supported_F := [0.75, 1.0, 1.3, 1.5, 2.0, 3.0];   DPR_Supported_S := ['0.75', '1.0', '1.3', '1.5', '2.0', '3.0'];   for var x := Low(DPR_Supported_F) to High(DPR_Supported_F) do begin     if DPR_Supported_F[x] >= DPR then begin       DPR := DPR_Supported_F[x];       DPRString := DPR_Supported_S[x];       Break;     end;   end;   Result := DPRString; //  ShowMessage(Result); end;  

  7. Like
    lynkfs got a reaction from AB in Some more simple components   
    A couple of more links for people interested in some bare-bones but functional SMS components :
     
    1) www.lynkfs.com/components/Carousel/Carousel.pas
    An image carousel with choice of effects
    Example of use in ..../Carousel/Carousel.sproj
    Preview : ..../Carousel/www/index.html 
     
    2) www.lynkfs.com/components/PageControl/PageControl.pas
    A tabbed page component.

    Example of use in ..../PageControl/PageControl.sproj
    Preview : ..../PageControl/www/index.html 


     

    3) www.lynkfs.com/components/PopUpMenu/PopUpMenu.pas
    A simple pop-up menu
    Example of use of this component in ..../PopUpMenu/PopUpMenu.sproj
    Preview : ..../PopUpMenu/www/index.html
     
    4) www.lynkfs.com/components/TreeView/TreeView.pas
    A treeview

    Example of use in ..../TreeView/TreeView.sproj
    Preview : ..../TreeView/www/index.html



     
  8. Like
    lynkfs got a reaction from Mablecymn in Browsers back button   
    - the browsers back-button in Smart Mobile Studio
     
    Most users find it frustrating they cannot use their browsers back/next buttons to navigate between forms. And especially so since they will typically leave the application altogether if they use them.
     
    One solution to this problem is manipulating the browsers history stack. Basically if we insert entries in the browsers history whenever there is a form change, and have a notification happening when the user uses the back/next buttons, then we can hook any history changes to form changes.
     
    Html5 introduced the history object. See https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history
     
    First thing to do is to hook up the popstate event. A popstate event is dispatched to the window every time the active history entry changes.
     
      w3_bind2(BrowserAPI.Body, 'onpopstate', CBOnPopState);
      w3_bind2(BrowserAPI.Body, 'popstate', CBOnPopState);  
     
    Next is to inject fake url's in the browsers history whenever there is a form change. The pushState method accepts a state object, a description and a url. The first 2 parameters are not really important here, the last one is. The following code injects a <your domain>/2.html entry in the history stack whenever the user navigates to Form2.
     
      asm
        var str = '{"hello":"world"}';
        var stateObj = JSON.parse(str);
        history.pushState(stateObj, "page 2", "2.html");
      end;
      Application.GotoForm('Form2');
     
    and then in the CBOnPopState procedure the history entries are synchronised with the application forms
     
      Procedure CBOnPopState;
      var
        F : String;
      Begin
        F := 'Form1';
        asm
          if (window.location.href == '<your domain>/') {@F = 'Form1';};
          if (window.location.href == '<your domain>/1.html') {@F = 'Form1';};
          if (window.location.href == '<your domain>/2.html') {@F = 'Form2';};
          if (window.location.href == '<your domain>/3.html') {@F = 'Form3';};
        end;
        Application.GoToForm(F);
      end;
     
    So now the browsers back/next buttons navigate between Forms !!
    Works in all modern browsers, desktop and mobile.
     
    There is a history.js project on github if you need Html4 browser support
     
    Nico
     
  9. Like
    lynkfs got a reaction from JosephMut in Browsers back button   
    - the browsers back-button in Smart Mobile Studio
     
    Most users find it frustrating they cannot use their browsers back/next buttons to navigate between forms. And especially so since they will typically leave the application altogether if they use them.
     
    One solution to this problem is manipulating the browsers history stack. Basically if we insert entries in the browsers history whenever there is a form change, and have a notification happening when the user uses the back/next buttons, then we can hook any history changes to form changes.
     
    Html5 introduced the history object. See https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history
     
    First thing to do is to hook up the popstate event. A popstate event is dispatched to the window every time the active history entry changes.
     
      w3_bind2(BrowserAPI.Body, 'onpopstate', CBOnPopState);
      w3_bind2(BrowserAPI.Body, 'popstate', CBOnPopState);  
     
    Next is to inject fake url's in the browsers history whenever there is a form change. The pushState method accepts a state object, a description and a url. The first 2 parameters are not really important here, the last one is. The following code injects a <your domain>/2.html entry in the history stack whenever the user navigates to Form2.
     
      asm
        var str = '{"hello":"world"}';
        var stateObj = JSON.parse(str);
        history.pushState(stateObj, "page 2", "2.html");
      end;
      Application.GotoForm('Form2');
     
    and then in the CBOnPopState procedure the history entries are synchronised with the application forms
     
      Procedure CBOnPopState;
      var
        F : String;
      Begin
        F := 'Form1';
        asm
          if (window.location.href == '<your domain>/') {@F = 'Form1';};
          if (window.location.href == '<your domain>/1.html') {@F = 'Form1';};
          if (window.location.href == '<your domain>/2.html') {@F = 'Form2';};
          if (window.location.href == '<your domain>/3.html') {@F = 'Form3';};
        end;
        Application.GoToForm(F);
      end;
     
    So now the browsers back/next buttons navigate between Forms !!
    Works in all modern browsers, desktop and mobile.
     
    There is a history.js project on github if you need Html4 browser support
     
    Nico
     
  10. Like
    lynkfs got a reaction from Mablecymn in iScroll under FireFox   
    The iScroll unit by Jon Aasenden has a problem with FireFox - Firefox renders a width of 0 on scrollboxes
     
    Add the third line from the bottom (the "width 100%" line) to fix that issue
     
    //############################################################################ // TW3ScrollWindow //############################################################################   procedure TW3ScrollWindow.InitializeObject; Begin   inherited;   FContent:=getScrollContentClass.Create(self);   FContent.Height:=0;     w3_setStyle(FContent.Handle,'postion','relative');   w3_setStyle(FContent.Handle,'width','100%');   w3_setStyle(FContent.Handle,'min-width','100%');   w3_setStyle(FContent.Handle,'min-height','0px');  
  11. Like
    lynkfs got a reaction from Richardmam in iScroll under FireFox   
    The iScroll unit by Jon Aasenden has a problem with FireFox - Firefox renders a width of 0 on scrollboxes
     
    Add the third line from the bottom (the "width 100%" line) to fix that issue
     
    //############################################################################ // TW3ScrollWindow //############################################################################   procedure TW3ScrollWindow.InitializeObject; Begin   inherited;   FContent:=getScrollContentClass.Create(self);   FContent.Height:=0;     w3_setStyle(FContent.Handle,'postion','relative');   w3_setStyle(FContent.Handle,'width','100%');   w3_setStyle(FContent.Handle,'min-width','100%');   w3_setStyle(FContent.Handle,'min-height','0px');  
  12. Like
    lynkfs got a reaction from HowardtorY in iScroll under FireFox   
    The iScroll unit by Jon Aasenden has a problem with FireFox - Firefox renders a width of 0 on scrollboxes
     
    Add the third line from the bottom (the "width 100%" line) to fix that issue
     
    //############################################################################ // TW3ScrollWindow //############################################################################   procedure TW3ScrollWindow.InitializeObject; Begin   inherited;   FContent:=getScrollContentClass.Create(self);   FContent.Height:=0;     w3_setStyle(FContent.Handle,'postion','relative');   w3_setStyle(FContent.Handle,'width','100%');   w3_setStyle(FContent.Handle,'min-width','100%');   w3_setStyle(FContent.Handle,'min-height','0px');  
  13. Like
    lynkfs got a reaction from thvedel in Components and how to make them data-aware   
    Understood. I'm sure there are better / more suave ways to deal with this than the above. Something is cooking though, see fb posts in Delphi developer
     
    By the way
    In retrospect you can do away with all the messaging and make the previous example a lot simpler :
     
    procedure TForm1.ObjectReady; var   Tag : String; begin   inherited;     Tag := EditBox1.Tagid;   // 'OBJ4'     Variant1.a := "initial value of attr a";   Variant1.b := "initial value of attr b";     asm        //watch for any change of attribute a     watch(@Variant1, "a", function(){       window[@Tag].value = @Variant1['a'];     });   end;       Button1.OnClick := procedure(Sender: TObject)     begin     //when changing the objects attribute it's watcher will be invoked       Variant1['a'] := 'a new object value';     end;     EditBox1.OnChanged := procedure(Sender: TObject)     begin       Variant1['a'] := EditBox1.Text;       ShowMessage('Object value changed to ' + Variant1['a']);     end;   end;
  14. Like
    lynkfs got a reaction from thvedel in Components and how to make them data-aware   
    A couple of links for whoever is interested in some simple SMS components :
     
    1) www.lynkfs.com/components/MySQLDB/MySQLDB.pas
    A cross domain component which takes a SQL query on any existing MySQL database and returns the cursor in both a local Variant and an on the fly generated TDataSet. Superfluous to have both ways implemented, but just for fun
    Example of use of this component in ..../MySQLDB/TestMySQLDB.sproj
     
    How to make a standard memo data-aware : just drag this component and a Memo on a form and connect them as in .../MemoDB/TestMemoDB.sproj
    Preview in .../MemoDB/www/index.html
     
    2) www.lynkfs.com/components/Accordeon/Accordion.pas
    A collapsible panel component. Example of use in .../Accordeon/TestAccordeon.sproj
    Preview in .../Accordeon/www/index.html
     
    How to make this component data aware : put this component and the previous one on a form as in .../AccordeonDB/TestAccordeonDB.sproj
    Preview in .../AccordeonDB/www/index.html
     
    Components have been built with the latest beta.
    The MySQL test database used has just 1 table with some data of just 5 customers.
     
    3) For a next post  : TTreeView, TCarousel, TPageControl and TPopUpMenu
  15. Like
    lynkfs got a reaction from Richardmam in Components and how to make them data-aware   
    A couple of links for whoever is interested in some simple SMS components :
     
    1) www.lynkfs.com/components/MySQLDB/MySQLDB.pas
    A cross domain component which takes a SQL query on any existing MySQL database and returns the cursor in both a local Variant and an on the fly generated TDataSet. Superfluous to have both ways implemented, but just for fun
    Example of use of this component in ..../MySQLDB/TestMySQLDB.sproj
     
    How to make a standard memo data-aware : just drag this component and a Memo on a form and connect them as in .../MemoDB/TestMemoDB.sproj
    Preview in .../MemoDB/www/index.html
     
    2) www.lynkfs.com/components/Accordeon/Accordion.pas
    A collapsible panel component. Example of use in .../Accordeon/TestAccordeon.sproj
    Preview in .../Accordeon/www/index.html
     
    How to make this component data aware : put this component and the previous one on a form as in .../AccordeonDB/TestAccordeonDB.sproj
    Preview in .../AccordeonDB/www/index.html
     
    Components have been built with the latest beta.
    The MySQL test database used has just 1 table with some data of just 5 customers.
     
    3) For a next post  : TTreeView, TCarousel, TPageControl and TPopUpMenu
  16. Like
    lynkfs got a reaction from IElite in Some more simple components   
    A couple of more links for people interested in some bare-bones but functional SMS components :
     
    1) www.lynkfs.com/components/Carousel/Carousel.pas
    An image carousel with choice of effects
    Example of use in ..../Carousel/Carousel.sproj
    Preview : ..../Carousel/www/index.html 
     
    2) www.lynkfs.com/components/PageControl/PageControl.pas
    A tabbed page component.

    Example of use in ..../PageControl/PageControl.sproj
    Preview : ..../PageControl/www/index.html 


     

    3) www.lynkfs.com/components/PopUpMenu/PopUpMenu.pas
    A simple pop-up menu
    Example of use of this component in ..../PopUpMenu/PopUpMenu.sproj
    Preview : ..../PopUpMenu/www/index.html
     
    4) www.lynkfs.com/components/TreeView/TreeView.pas
    A treeview

    Example of use in ..../TreeView/TreeView.sproj
    Preview : ..../TreeView/www/index.html



     
  17. Like
    lynkfs got a reaction from HowardtorY in Components and how to make them data-aware   
    A couple of links for whoever is interested in some simple SMS components :
     
    1) www.lynkfs.com/components/MySQLDB/MySQLDB.pas
    A cross domain component which takes a SQL query on any existing MySQL database and returns the cursor in both a local Variant and an on the fly generated TDataSet. Superfluous to have both ways implemented, but just for fun
    Example of use of this component in ..../MySQLDB/TestMySQLDB.sproj
     
    How to make a standard memo data-aware : just drag this component and a Memo on a form and connect them as in .../MemoDB/TestMemoDB.sproj
    Preview in .../MemoDB/www/index.html
     
    2) www.lynkfs.com/components/Accordeon/Accordion.pas
    A collapsible panel component. Example of use in .../Accordeon/TestAccordeon.sproj
    Preview in .../Accordeon/www/index.html
     
    How to make this component data aware : put this component and the previous one on a form as in .../AccordeonDB/TestAccordeonDB.sproj
    Preview in .../AccordeonDB/www/index.html
     
    Components have been built with the latest beta.
    The MySQL test database used has just 1 table with some data of just 5 customers.
     
    3) For a next post  : TTreeView, TCarousel, TPageControl and TPopUpMenu
  18. Like
    lynkfs got a reaction from jorn in Some more simple components   
    A couple of more links for people interested in some bare-bones but functional SMS components :
     
    1) www.lynkfs.com/components/Carousel/Carousel.pas
    An image carousel with choice of effects
    Example of use in ..../Carousel/Carousel.sproj
    Preview : ..../Carousel/www/index.html 
     
    2) www.lynkfs.com/components/PageControl/PageControl.pas
    A tabbed page component.

    Example of use in ..../PageControl/PageControl.sproj
    Preview : ..../PageControl/www/index.html 


     

    3) www.lynkfs.com/components/PopUpMenu/PopUpMenu.pas
    A simple pop-up menu
    Example of use of this component in ..../PopUpMenu/PopUpMenu.sproj
    Preview : ..../PopUpMenu/www/index.html
     
    4) www.lynkfs.com/components/TreeView/TreeView.pas
    A treeview

    Example of use in ..../TreeView/TreeView.sproj
    Preview : ..../TreeView/www/index.html



     
  19. Like
    lynkfs got a reaction from Ekbergket in Browsers back button   
    - the browsers back-button in Smart Mobile Studio
     
    Most users find it frustrating they cannot use their browsers back/next buttons to navigate between forms. And especially so since they will typically leave the application altogether if they use them.
     
    One solution to this problem is manipulating the browsers history stack. Basically if we insert entries in the browsers history whenever there is a form change, and have a notification happening when the user uses the back/next buttons, then we can hook any history changes to form changes.
     
    Html5 introduced the history object. See https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history
     
    First thing to do is to hook up the popstate event. A popstate event is dispatched to the window every time the active history entry changes.
     
      w3_bind2(BrowserAPI.Body, 'onpopstate', CBOnPopState);
      w3_bind2(BrowserAPI.Body, 'popstate', CBOnPopState);  
     
    Next is to inject fake url's in the browsers history whenever there is a form change. The pushState method accepts a state object, a description and a url. The first 2 parameters are not really important here, the last one is. The following code injects a <your domain>/2.html entry in the history stack whenever the user navigates to Form2.
     
      asm
        var str = '{"hello":"world"}';
        var stateObj = JSON.parse(str);
        history.pushState(stateObj, "page 2", "2.html");
      end;
      Application.GotoForm('Form2');
     
    and then in the CBOnPopState procedure the history entries are synchronised with the application forms
     
      Procedure CBOnPopState;
      var
        F : String;
      Begin
        F := 'Form1';
        asm
          if (window.location.href == '<your domain>/') {@F = 'Form1';};
          if (window.location.href == '<your domain>/1.html') {@F = 'Form1';};
          if (window.location.href == '<your domain>/2.html') {@F = 'Form2';};
          if (window.location.href == '<your domain>/3.html') {@F = 'Form3';};
        end;
        Application.GoToForm(F);
      end;
     
    So now the browsers back/next buttons navigate between Forms !!
    Works in all modern browsers, desktop and mobile.
     
    There is a history.js project on github if you need Html4 browser support
     
    Nico
     
  20. Like
    lynkfs got a reaction from RudyAttemia in Browsers back button   
    - the browsers back-button in Smart Mobile Studio
     
    Most users find it frustrating they cannot use their browsers back/next buttons to navigate between forms. And especially so since they will typically leave the application altogether if they use them.
     
    One solution to this problem is manipulating the browsers history stack. Basically if we insert entries in the browsers history whenever there is a form change, and have a notification happening when the user uses the back/next buttons, then we can hook any history changes to form changes.
     
    Html5 introduced the history object. See https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history
     
    First thing to do is to hook up the popstate event. A popstate event is dispatched to the window every time the active history entry changes.
     
      w3_bind2(BrowserAPI.Body, 'onpopstate', CBOnPopState);
      w3_bind2(BrowserAPI.Body, 'popstate', CBOnPopState);  
     
    Next is to inject fake url's in the browsers history whenever there is a form change. The pushState method accepts a state object, a description and a url. The first 2 parameters are not really important here, the last one is. The following code injects a <your domain>/2.html entry in the history stack whenever the user navigates to Form2.
     
      asm
        var str = '{"hello":"world"}';
        var stateObj = JSON.parse(str);
        history.pushState(stateObj, "page 2", "2.html");
      end;
      Application.GotoForm('Form2');
     
    and then in the CBOnPopState procedure the history entries are synchronised with the application forms
     
      Procedure CBOnPopState;
      var
        F : String;
      Begin
        F := 'Form1';
        asm
          if (window.location.href == '<your domain>/') {@F = 'Form1';};
          if (window.location.href == '<your domain>/1.html') {@F = 'Form1';};
          if (window.location.href == '<your domain>/2.html') {@F = 'Form2';};
          if (window.location.href == '<your domain>/3.html') {@F = 'Form3';};
        end;
        Application.GoToForm(F);
      end;
     
    So now the browsers back/next buttons navigate between Forms !!
    Works in all modern browsers, desktop and mobile.
     
    There is a history.js project on github if you need Html4 browser support
     
    Nico
     
×
×
  • Create New...