Jump to content

lynkfs@gmail.com

Moderators
  • Content count

    312
  • Joined

  • Last visited

  • Days Won

    68

lynkfs@gmail.com last won the day on December 13 2017

lynkfs@gmail.com had the most liked content!

About lynkfs@gmail.com

  • Rank

Profile Information

  • Gender
    Male
  • Location
    Australia

Recent Profile Visitors

187 profile views
  1. Smart Test Framework

    Thank you, great (System.diagnostics ==> smartcl.diagnostics)
  2. shoestring framework

    The documentation for the native framework can be found here
  3. Vibration API

    try this browserapi.window.navigator.vibrate(200); // vibrate for 200ms browserapi.window.navigator.vibrate([100,30,100,30,100,200,200,30,200,30,200,200,100,30,100,30,100]); // Vibrate 'SOS' in Morse. The vibration api does not work on ios safari, opera and edge
  4. SQLite Loadfromstream

    This code snippet saves a SQLite database to local storage and reloads it again. All goes fine up till the end (LoadFromStream) but leaves the db in an inactive state Any suggestions ? var MyStream : TStringStream; MyStream := TStringStream.Create; db.SaveToStream(MyStream,true); var textdata3 := MyStream.DataString; //save textdata3 to local storage var LocalStorage2 := TW3LocalStorage.Create; LocalStorage2.Open('sqlite'); LocalStorage2.SetKeyStr('stream',textdata3); LocalStorage2.Close; //read textdata3 from local storage LocalStorage2.Open('sqlite'); textdata3 := LocalStorage2.GetKeyStr('stream','unknown'); LocalStorage2.Close; MyStream.DataString := textdata3; db.LoadFromStream(MyStream); //all good up to here writeln(db.active); //false
  5. Database Examples

    Here is a dogs breakfast of internal data structures and conversions between them step-1 set up and populate data structure (business unit with multiple departments with staff per department) step-2 manipulation in code step-3 convert record structure to json text step-4 define and populate TW3Dataset from json step-5 save json to local storage (persistent per device) step-6 reload json from local storage step-7 define and populate SQLite db from json step-8 save json to server step-9 reload json from server At the end of the day all these structures can be converted to some kind of string (text, json, streamstring) and then saved to local storage. When running in the browser that is the only way to save data, which gives you device-persistence. Persistence of data over multiple devices is only possible by storing data on a server. unit Form1; interface uses System.Types, System.Types.Convert, System.Objects, System.Time, SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.Controls.Button, ECMA.JSON, System.DataSet, SmartCL.Storage.Local, System.Sqlite, W3C.XMLHttpRequest, W3C.HTML5, W3C.DOM; type TForm1 = class(TW3Form) procedure W3Button1Click(Sender: TObject); private {$I 'Form1:intf'} protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; Procedure DoStuff; end; // record types type JStrC = record firstname : string; //external 'firstname'; surname : string; //external 'surname'; end; JStrB = record deptname : string; //external 'deptname'; staff : Array of JStrC; //external 'staff'; end; JStrA = record business : string; //external 'business' departments : Array of JStrB; //external 'departments'; end; implementation { TForm1 } Procedure TForm1.DoStuff; begin // // step1 : set up a leveled structure, (either records or classes) // i.e. business unit with departments with employees per department writeln('Step1'); // //business unit var StrA01 : JStrA; StrA01.business := 'lynkfs'; //department marketing var StrB01 : JStrB; StrB01.deptname := 'marketing'; //staff Keith Ansell var StrC01 : JStrC; StrC01.firstname := 'Claire'; StrC01.surname := 'Adams'; StrB01.staff.Add(StrC01); //staff Charles Bailey var StrC02 : JStrC; StrC02.firstname := 'Vicky'; StrC02.surname := 'Anderson'; StrB01.staff.Add(StrC02); StrA01.departments.Add(StrB01); //department frontdesk var StrB02 : JStrB; StrB02.deptname := 'front desk'; //staff Keith Ansell var StrC03 : JStrC; StrC03.firstname := 'Keith'; StrC03.surname := 'Ansell'; StrB02.staff.Add(StrC03); //staff Charles Bailey var StrC04 : JStrC; StrC04.firstname := 'Charles'; StrC04.surname := 'Bailey'; StrB02.staff.Add(StrC04); StrA01.departments.Add(StrB02); // check writeln(inttostr(StrA01.departments.length) + ' departments'); for var i := 0 to StrA01.departments.length -1 do begin writeln(StrA01.departments[i].deptname + ' ' + inttostr(StrA01.departments[i].staff.length) + ' staff'); for var j := 0 to StrA01.departments[i].staff.length -1 do begin writeln(StrA01.departments[i].staff[j].firstname + ' ' + StrA01.departments[i].staff[j].surname); end; end; // // step2 : depending on business logic, add/delete/change data in these arrays at will writeln('Step2'); // // employee Claire Adams leaves StrA01.departments[0].staff.Delete(0); //or coding to that effect // a new empty department is created var StrB : JStrB; StrB.deptname := 'new department'; StrA01.departments.add(StrB); // check writeln(inttostr(StrA01.departments.length) + ' departments'); for var i := 0 to StrA01.departments.length -1 do begin writeln(StrA01.departments[i].deptname + ' ' + inttostr(StrA01.departments[i].staff.length) + ' staff'); for var j := 0 to StrA01.departments[i].staff.length -1 do begin writeln(StrA01.departments[i].staff[j].firstname + ' ' + StrA01.departments[i].staff[j].surname); end; end; // // step3 : Turn object to json writeln('Step3'); // var textdata: string := ''; asm @textdata = JSON.stringify(@StrA01); console.log(@textdata); // {"business":"lynkfs","departments":[{"deptname":"marketing","staff":[{"firstname":"Vicky","surname":"Anderson"}]},{"deptname":"front desk","staff":[{"firstname":"Keith","surname":"Ansell"},{"firstname":"Charles","surname":"Bailey"}]},{"deptname":"new department","staff":[]}]} end; // // step4 : Define and populate TW3DataSet from json writeln('Step4'); // var DataSet := TW3DataSet.Create; // note TW3Dataset is not leveled Dataset.FieldDefs.Add('business',ftString); Dataset.FieldDefs.Add('deptname',ftString); Dataset.FieldDefs.Add('firstname',ftString); Dataset.FieldDefs.Add('surname',ftString); DataSet.CreateDataset; var v := json.parse(textdata); For var i := 0 to v.departments.length -1 do begin For var j := 0 to v.departments[i].staff.length -1 do begin DataSet.Append; Dataset.Fields.FieldByName('business').AsString := v.business; Dataset.Fields.FieldByName('deptname').AsString := v.departments[i].deptname; Dataset.Fields.FieldByName('firstname').AsString := v.departments[i].staff[j].firstname; Dataset.Fields.FieldByName('surname').AsString := v.departments[i].staff[j].surname; DataSet.Post; end; If v.departments[i].staff.length = 0 then begin //empty department DataSet.Append; Dataset.Fields.FieldByName('business').AsString := v.business; Dataset.Fields.FieldByName('deptname').AsString := v.departments[i].deptname; DataSet.Post; end; end; // check DataSet.First; while not DataSet.Eof do begin writeln(inttostr(DataSet.recno+1) + ' ' + Dataset.Fields.FieldByName('business').AsString + ' ' + Dataset.Fields.FieldByName('deptname').AsString + ' ' + Dataset.Fields.FieldByName('firstname').AsString + ' ' + Dataset.Fields.FieldByName('surname').AsString); DataSet.Next; end; // // step5 : save json to local storage (persistent on specific device) writeln('Step5'); // step 5 only needs to be run once // var LocalStorage := TW3LocalStorage.Create; LocalStorage.Open('mytreasure'); LocalStorage.SetKeyStr('json',textdata); LocalStorage.Close; // // step6 : retrieve json from local storage (persistent on specific device) writeln('Step6'); // textdata := ''; LocalStorage.Open('mytreasure'); textdata := LocalStorage.GetKeyStr('json','unknown'); LocalStorage.Close; var v1 := json.parse(textdata); // check writeln(inttostr(v1.departments.length) + ' departments'); for var i := 0 to v1.departments.length -1 do begin writeln(v1.departments[i].deptname + ' ' + inttostr(v1.departments[i].staff.length) + ' staff'); for var j := 0 to v1.departments[i].staff.length -1 do begin writeln(v1.departments[i].staff[j].firstname + ' ' + v1.departments[i].staff[j].surname); end; end; // // step7 : save json to SQLite writeln('Step7'); // var db : TSQLiteDatabase; var res : TSQLiteResult; db := TSQLiteDatabase.Create; db.Run('CREATE TABLE `business` (id INTEGER, business TEXT)'); db.Run('CREATE TABLE `departments` (id INTEGER, busid INTEGER, deptname TEXT)'); db.Run('CREATE TABLE `staff` (id INTEGER, deptid INTEGER, busid INTEGER, firstname TEXT, surname TEXT)'); var v2 := json.parse(textdata); db.Run("INSERT INTO `business`(`id`,`business`) VALUES (0,'" + v2.business + "')"); for var i := 0 to v2.departments.length -1 do begin db.Run("INSERT INTO `departments`(`id`,`busid`,`deptname`) VALUES (" + inttostr(i) + ",0,'" + v2.departments[i].deptname + "')"); for var j := 0 to v2.departments[i].staff.length -1 do begin db.Run("INSERT INTO `staff`(`id`,`deptid`,`busid`,`firstname`,`surname`) VALUES (" + inttostr(j) + "," + inttostr(i) + ",0,'" + v2.departments[i].staff[j].firstname + "','" + v2.departments[i].staff[j].surname + "')"); end; end; // check // res := db.Exec("SELECT a.business, b.deptname, c.firstname, c.surname " + // "from business a, departments b, staff c " + // "where c.busid = a.id and c.deptid = b.id and b.busid = a.id"); // or something a bit simpler res := db.Exec("SELECT surname FROM staff"); //writeln(res.columns[0]); //title of first field : 'business' var rows := res.values; for var i := 0 to rows.length -1 do begin var columns := rows[i].values; for var j := 0 to columns.length-1 do begin writeln(columns[j]); end; end; // or alternatively, same result // // for var i := 0 to res.values.length -1 do begin //rows // for var j := 0 to res.values[i].values.length-1 do begin //columns // writeln(res.values[i].values[j]); //cell // end; // end; /* // // step8 : save json to server (persistent over all devices) writeln('Step8'); // var s := "UPDATE `test` SET `webjson`='" + textdata + "'"; var FHttp := JXMLHttpRequest.Create; FHttp.open("POST",'...../smsdmlmysql.php'); FHttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); var encodedstr1 := browserapi.window.encodeURIComponent(S); var sql_statement := 'sql_statement=' + encodedstr1; FHttp.send(sql_statement); // // step9 : retrieve json from server writeln('Step9'); // s := "select * from test"; var FHttp2 := JXMLHttpRequest.Create; FHttp2.open("POST",'...../smsdbmysql.php'); FHttp2.setRequestHeader("Content-type","application/x-www-form-urlencoded"); var encodedstr2 := browserapi.window.encodeURIComponent(S); var sql_statement2 := 'sql_statement=' + encodedstr2; FHttp2.send(sql_statement2); JGlobalEventHandlers(FHttp2).onLoad := lambda(e:JEvent) var smscursor := JSON.parse(FHttp2.responseText); for var i := 0 to smscursor.rows.length -1 do begin textdata := smscursor.rows[i].webjson; end; writeln(textdata); result :=true; end; */ end; procedure TForm1.W3Button1Click(Sender: TObject); begin DoStuff; end; procedure TForm1.InitializeForm; begin inherited; // this is a good place to initialize components end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} end; procedure TForm1.Resize; begin inherited; end; initialization Forms.RegisterForm({$I %FILE%}, TForm1); end.
  6. Save Content Dialog

    sorry, code example local storage was not quite right procedure TForm1.W3Button2Click(Sender: TObject); begin // Retrieve Memo1.Text := browserapi.window.localStorage.getItem("lastname"); end; procedure TForm1.W3Button1Click(Sender: TObject); begin // Store browserapi.window.localStorage.setItem("lastname", "Smith"); end; this works better. Or the slightly longer version using SmartCL.Storage.Local unit Form1; interface uses System.Types, System.Types.Convert, System.Objects, System.Time, SmartCL.System, SmartCL.Time, SmartCL.Graphics, SmartCL.Components, SmartCL.FileUtils, SmartCL.Forms, SmartCL.Fonts, SmartCL.Theme, SmartCL.Borders, SmartCL.Application, SmartCL.Storage.Local, SmartCL.Controls.Memo, SmartCL.Controls.Button; type TForm1 = class(TW3Form) procedure W3Button2Click(Sender: TObject); procedure W3Button1Click(Sender: TObject); private {$I 'Form1:intf'} protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; LocalStorage : TW3LocalStorage; end; implementation { TForm1 } procedure TForm1.W3Button2Click(Sender: TObject); begin LocalStorage.Open('aaa'); W3Memo1.Text := LocalStorage.GetKeyStr('businessname','unknown'); LocalStorage.Close; end; procedure TForm1.W3Button1Click(Sender: TObject); begin LocalStorage.Open('aaa'); LocalStorage.SetKeyStr('businessname','lynkfs'); LocalStorage.Close; end; procedure TForm1.InitializeObject; begin inherited; {$I 'Form1:impl'} LocalStorage := TW3LocalStorage.Create; end;
  7. Save Content Dialog

    Edited If you're running a project in the browser, it is not possible to save files to the local harddrive. The easiest work-arounds : - use local storage. This is a browser function which lets you store key-value pairs, and it is persistent. Caveats of course are that storage capacity is limited, storage is specific to the protocol of the page, data stored when in stealth mode will be deleted after exiting from stealth mode (and is maybe even browser vendor specific ?) and data gets formatted as strings. Might be ok for your purposes though ... procedure TForm1.W3Button2Click(Sender: TObject); begin // Retrieve Memo1.Text := browserapi.window.localStorage.getItem("lastname"); end; procedure TForm1.W3Button1Click(Sender: TObject); begin // Store browserapi.window.localStorage.setItem("lastname", "Smith"); end; - use session storage instead of local storage if you don't want to hang on to data longer than current session - use cookies. An old feature to basically do something like local storage. - use a server. Uploading files to a php or node server is pretty straight forward. You'll need access to a server though - exit the browser and produce a native app
  8. back(s)lash

    OK Lennart, brain-teaser here. example of unwanted suffix : {"ribbons$1":[{"ribbonname$1":"ribbonname","params$1":[{"paramtype$1":"paramtype"}]}]} code to reproduce (normal standard latest alpha) type JW3WebParam = class paramtype : string; paramcontent : string; strbefore : string; strafter : string; end; JW3WebRibbon = class ribbonname : string; ribbonid : integer; imagename : string; category : string; params : Array of JW3WebParam; end; JW3WebProject = class projectname : string; author : string; password : string; webheader : string; webtrailer : string; webjson : string; ribbons : Array of JW3WebRibbon; end; type TForm1 = class(TW3Form) procedure W3Button1Click(Sender: TObject); private {$I 'Form1:intf'} protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; Procedure ShowProblem; end; // record types for stringify purposes type JStrC = record paramtype : string; //external 'paramtype'; end; JStrB = record ribbonname : string; //external 'ribbonname'; params : Array of JStrC; //external 'params'; end; JStrA = record ribbons : Array of JStrB; //external 'ribbons'; end; var StrC : JStrC; StrB : JStrB; StrA : JStrA; implementation { TForm1 } procedure TForm1.W3Button1Click(Sender: TObject); begin ShowProblem; end; Procedure TForm1.ShowProblem; begin StrB.ribbonname := 'ribbonname'; StrC.paramtype := 'paramtype'; StrB.params.add(StrC); StrA.ribbons.add(StrB); // Turn object to json text var textdata: string := ''; var res: string := ''; asm @textdata = JSON.stringify(@StrA); console.log(@textdata); end; end; The above code produces the faulty stringyfied string. Now notice that the first classes are actually not used : type JW3WebParam = class paramtype : string; paramcontent : string; strbefore : string; strafter : string; end; JW3WebRibbon = class ribbonname : string; ribbonid : integer; imagename : string; category : string; params : Array of JW3WebParam; end; JW3WebProject = class projectname : string; author : string; password : string; webheader : string; webtrailer : string; webjson : string; ribbons : Array of JW3WebRibbon; end; So delete these from the code, leaving just type TForm1 = class(TW3Form) procedure W3Button1Click(Sender: TObject); private {$I 'Form1:intf'} protected procedure InitializeForm; override; procedure InitializeObject; override; procedure Resize; override; Procedure ShowProblem; end; // record types for stringify purposes type JStrC = record paramtype : string; //external 'paramtype'; end; JStrB = record ribbonname : string; //external 'ribbonname'; params : Array of JStrC; //external 'params'; end; JStrA = record ribbons : Array of JStrB; //external 'ribbons'; end; var StrC : JStrC; StrB : JStrB; StrA : JStrA; implementation { TForm1 } procedure TForm1.W3Button1Click(Sender: TObject); begin ShowProblem; end; Procedure TForm1.ShowProblem; begin StrB.ribbonname := 'ribbonname'; StrC.paramtype := 'paramtype'; StrB.params.add(StrC); StrA.ribbons.add(StrB); // Turn object to json text var textdata: string := ''; var res: string := ''; asm @textdata = JSON.stringify(@StrA); console.log(@textdata); end; end; Executing this gives as output : {"ribbons":[{"ribbonname":"ribbonname","params":[{"paramtype":"paramtype"}]}]} Go figure Note : may or may not be related to
  9. back(s)lash

    As to the unwanted suffix error, it doesn't reproduce using a simple code snippet like above, and I can't pinpoint why it did in my codebase. It was resolved though by EWB's suggestion of using the 'external' keyword. Mapping to existing data : Thanks
  10. shoestring framework

    The native framework (see this thread) has been finalised and can be acquired through www.lynkfs.com.
  11. back(s)lash

    This works out quite well. It even solves the unmentioned problem that I can now use code obfuscation.
  12. back(s)lash

    Thank you, amazing
  13. back(s)lash

    5 lines of code which drove me bonkers. My brilliant idea was to have a set of classes, where class A has a field which is an array of class B, and class B has a field which is an array of class C etc - and then just stringify the whole setup and store that string in a MySQL database. That would save me from setting up multilayered tables and I could just compress everything in a single base table. JB = record field1 : string; params : Array of JC; end; JA= record fields : array of JB; end; stringifying this (asm @textdata = JSON.stringify(@data); where @data refers to JA) gives what I was after. Except that all fieldnames in the result-string have a '$1' appended ({"field1$1"). No idea why that happens or how to avoid that. Pushing on, using a while-loop with 'pos' and 'delete' to get rid of this suffix doesn't work. The Pos and Delete functions don't work on strings resulting from an asm @string operation. Next best thing : string replace with regex. In the end I needed to do something like this : asm @textdata = JSON.stringify(@data); var r = new RegExp('\\$1', 'g'); //delete all name-postfixes of '$1' @res = (@textdata).replace(r, ""); var r2 = new RegExp('\\"', 'g'); //add backslashes just before double quotes @res2 = (@res).replace(r2, '\\"'); //and add mysql backslash character as well end; The first replacement operation gets rid of all the $1 suffixes The second replacement operation sets a \ around text pieces which either have a special character or space in their content, like : font-family: \"Old Standard TT\" And to make matters worse, found out that MySQL just ignores all backslashes in update statements. Unless these backslashes are preceded with a special character. (In this case another backslash). Haven't mentioned some other caveats like handling EOL characters Anyway, got it working, but it might have been easier to set up multi-layer tables after all. If anyone has a solution for the automatic suffix addition ($1) in stringify operations please let me know.
  14. Black Screen of Death

    what is the source / domain of this conversion ?
  15. Do we need a TSplitter

    Isn't too hard to wrap this in a component, as you're doing above. Code needs to cleaned up a bit too, positioning is a couple of pixels off here and there. Please feel free to make any changes Btw : I did add a JSplitter component to the native/shoestring framework. Might contain something useful : http://www.lynkfs.com/Experiments/shoestring/index.sproj (JSplitter unit, usage see Form1) and ....shoestring/www for kitchensink
×