NetTalk Apps is the third level of NetTalk. It
      includes all the functionality in NetTalk Desktop and NetTalk Server. In
      addition to that it also allows you to create disconnected web
      applications and native mobile applications for use on phones and tablets.
      
      
      To best understand what NetTalk Apps is doing, it's first necessary to
      understand a simple premise you have used up to now, and what the limits
      of that premise is.
      
      When writing Clarion Desktop (aka Win 32) programs it is understood that
      there is one data store. Multiple users can access that data store, but
      fundamentally there is a single data store which all users are sharing.
      This premise still exists when writing a NetTalk Web application. Many
      users sharing one exe, which in turn is sharing one store.
      
      In this context a data store can contain multiple databases, it may be
      ISAM (TPS) or SQL based and it may encompass multiple folders on a disk.
      But at it's heart the idea is that data is stored in a single place and
      (ideally) you don't have the same data stored in multiple places.
      
      Of course data does occasionally need to be replicated into multiple
      places, and maintained in multiple databases automatically. This can be
      done via replication - either Server-side replication (which requires that
      all databases be of the same SQL flavor) or client-side replication (as
      with the CapeSoft Replicate extension) which requires that all programs be
      written in Clarion. 
      
      With NetTalk Apps the goal is to allow different programs with different
      data stores, written in different languages, to share data in such a way
      that the data can easily flow between many different stores, and
      importantly, between many different kinds of stores. The actual storage of
      the data, and the capabilities of that storage vessel, cannot be taken for
      granted. Data might just as easily reside in an XML file, a SQL database,
      a browser's Local Storage or a local ISAM file. The goal is to allow many
      different kinds of programs to interact with their own data, and then
      share that data with each other.
      
      In some ways this means thinking "beyond SQL". SQL has traditionally been
      the store of choice for many programs, and indeed there are many benefits
      to having SQL as a store, but data inside a SQL database requires clients
      to connect to that database. In order to create disconnected programs we
      have to allow the data to escape the SQL database, returning (possibly
      changed) at some later point. Of course this does not replace the SQL
      database - your primary store will likely remain as a SQL database
      (although it could also be TPS) - but rather it expands the horizons of
      the database.
      
    
    
      Introduction
       Before you can start building disconnected apps you
        need to understand the concept of distributed data. By definition
        disconnected apps keep a copy of the data, and that copy has to be
        synchronized with the parent server from time to time. In order for that
        to work correctly the tables need to contain specific fields and the
        records need to be updated in specific ways. 
        
        It should be noted that the mechanism described here is specifically
        designed to be completely independent of the data store. If all the
        databases were in the same store (like say MS SQL Server) then it would
        be possible to use the replication built into that server to do the
        necessary synchronization. However as the data will be distributed to
        many different platforms (Windows, Web, iOS, Android etc) it is not
        possible to ensure that only a single data store is used. Equally this
        approach is not limited to any one programming language. NetTalk
        contains implementations in Clarion and JavaScript, but the system is
        language agnostic and so any program written in any language can join
        in, as long as it obeys some rules.
      Summary
       The logic behind these requirements are discussed
        below, but this is the checklist of requirements (for a Clarion app):
        
          - Each table needs a GUID field - type
            String(16)
- Each table needs a GuidKey, marked as
            unique, on the GUID field.
- Each table needs three TimeStamp fields, all of type Real,
            TimeStamp, ServerTimeStamp
              and DeletedTimeStamp. These
            should have external names of ts, sts and dts respectively.
            (NOTE: If using the Firebase backend, uppercase these field names.)
- Each table needs a ServerTimeStampKey 
            key on the ServerTimeStamp field. The
            key is not unique.
- Each table needs a TimeStampKey on
            the TimeStamp field. The key is not
            unique.
- Whenever a record is updated, TimeStamp must
            be set to the current time stamp. In Clarion apps this is usually
            done with the NetTalkClientSync global extension
            template.
- Whenever a record is updated on the parent server, ServerTimeStamp
            must be set to the current time stamp.
        Remember the GUID field has
 two strict rules.
        
          -  The contents of the field must NEVER be
            changed.
- Any attempt to populate it using other data should be avoided. It
            should contain pure randomized
            characters. 
Dictionary Fields
      
        GUID Field
         This field is required. It is a unique row
          identifier. This is (arbitrarily) a 16 character string using a 36
          letter alphabet.
          
          Although some databases have a native data type named 
Guid
          this field should not be native to any specific database. Rather it is
          a simple 16 character random string. To make the field both portable
          to multiple databases, and easily transportable between databases, it
          is recommended that an alphabet of 36 characters (a-z, 0-9) be used. 
          
          If considering additional characters in the alphabet:
          
            - Characters > character 127 would render the value not utf-8
              (which has transport implications)
- Any punctuation may be a problem with current or future
              transport or encoding situations. For example, “ and \ characters
              in JSON, < and > in xml and & in HTML
- Whitespace characters (especially tab, cr and lf) may be
              incompatible with file formats
- Any control characters (ASCII 0 through 31) should be avoided
- Some systems are case sensitive, others are not, hence either
              upper, or lower case letters are preferable
          Usually this field forms the primary key for the table if this is a
          new table. If another primary key exists then that's ok, but this
          field must be unique in the table. Foreign keys in other tables can
          use this as the linking key field.
          
          For example:
          
          Invoices Table
          
Inv:Guid
          
          Line Items Table
          
Lin:Guid 
          Lin:InvoiceGuid  
          The Guid field contents cannot be changed at any
            time for any reason. 
          This avoids the complexity and performance involved in doing related
          table changes. It is also necessary for this field to be unchanging in
          order to track a specific row through the (disconnected) data stores.
          If the Guid field changes, then all other databases are immediately
          inconsistent and a consistent state cannot be achieved. 
          
          
Any attempt to place order on the Guid field, by populating it
            with some calculated rather than random field, should be avoided.
          Embedding data in the Guid could lead to that data either becoming
          inaccurate, or needing to be changed. Additional fields should be
          added to the row for storage of additional data. The Guid should
          contain completely random values. 
          
          Mathematically a 16 character string using an alphabet of 36
          characters allows for 36 ^ 16 or 7 958 661 109 946 400 884 391 936
          possible identifiers. This can be written as 7.9 * 10
24.
          There are approximately (very approximately) 7.5 * 10
18
          grains of sand on earth. So a 16 character Guid is sufficient to
          identify, uniquely, all the grains of sand on 100 000 planets similar
          to Earth. Mathematically, yes, a collision within a single table is 
possible,
          however it is not 
probable. 
TimeStamp field
         This field is required for all tables that allow
          updates in any non-root database. A Real allows for 15 significant
          digits. This is sufficient to hold the number of milliseconds since
          1970, which is the format used by JavaScript. 
        ServerTimeStamp field
         This field is required. It is a Real holding the
          timestamp as last set in the server. On the server this value is
          always equal to Timestamp. On the mobile device this value may be
          different to Timestamp if the record has been updated on the device.
          (The device holds the time of the last update on the device.) 
        DeletedTimeStamp field
         Acts as a deleteflag, and contains the time when
          the record was deleted.  
        CreatedBy field
         This field is optional, but can be useful when
          distributing a limited data set to other devices. The type is up to
          you, but should match an identifier used by your existing users
          system. Assuming you have a Users table, and that table has a GUID
          identifier, then this field would also be a String(16). 
        UpdatedBy field
         This field is optional, but can be useful when
          distributing a limited data set to other devices. The type is up to
          you, but should match an identifier used by your existing users
          system. Assuming you have a Users table, and that table has a GUID
          identifier, then this field would also be a String(16). 
       
      Current Time Stamp
       While time stamps are necessary in order to compare
        records, and choose the later record, the actual measurement of the time
        is very arbitrary. In order to provide a generic system, that can be
        used across many platforms and programming languages, Unix Epoch time is
        used. However Unix Epoch time is typically measured in seconds, whereas
        NetTalk allows for milliseconds. If sub-second timings are not required,
        or available, then multiply the Epoch time by 1000.
        
        Data is independent of time zone, therefore all time stamps are relative
        to UTC regardless of the local time zone where the data is added,
        updated or deleted.. 
        
        The data type used will vary depending on the platform and language. A
        type which can contain integer numbers in the range 0-4 000 000 000 000
        should be tolerated. (This allows for the system to work until at least
        2095.) In Clarion the best type for this is a REAL which easily exceeds
        this range. 
        
        All NetTalk objects expose a method, 
GetElapsedTimeUTC(),
        which returns the current time stamp (measured as the number of
        milliseconds since Jan 1, 1970.) 
        
        Here are some examples;
        
          - Clarion
 ts = glo:SyncDesktop.GetElapsedTimeUTC()
- JavaScript
 ts = Date.now();
Clarion Dictionary Tips
      
        
          - Add a Globals data section
- Add a variable, st, to this section.
            Set the type to STRINGTHEORY
- Start with one table, and add all the fields (GUID,
            TimeStamp etc). You can cut and paste
            all the fields from this table to the other tables, so it's worth
            getting them perfect before doing that.
- Set the Guid field, Initial Value, to 
              Glo:st.MakeGuid()
- For the timestamp fields be sure to set the external names (to ts, sts and dts).
- For the Guid and all the TimeStamp fields go to the Options tab
            and tick on Do Not Populate.
- For the TimeStamp field go to the Options tab and enter an Option
            called TimeStamp, with the value 1. Do the same for the ServerTimeStamp and
            DeletedTimeStamp fields, setting their options to ServerTimeStamp
              and  DeletedTimeStamp respectively.
- Add a Key on the Guid field, a key on the TimeStamp field, and a
            key on the ServerTimeStamp field to the table. (At the time of
            writing it was not possible to cut & paste keys between tables,
            but future versions of Clarion may be able to do that so test to see
            if it does.)
 
      Database Field Constraints
       It is common in data design to create constraints on
        data where one field "must exist" in another table. For example in a
        LineItems table you may place a constraint that the InvoiceGuid field
        (which identifies the parent invoice record) "Must be in File" for the
        Invoice table. Thus creating an explicit constraint that the parent
        record (Invoice) has to be added to the database before the child record
        in the LineItems table can be added.
        
        Data distribution as described in this document does not make this
        constraint impossible, but having this constraint does make the system
        more complex and leads to other potential problems. Specifically the
        order of table synchronization becomes important, and each table has to
        be completely synchronized before another table can be synchronized.
        
        As an aside, in a Clarion application (regardless of backend), the "Must
        be in File" constraint slows down the ADD to a child table enormously,
        so it's a very expensive constraint there. In a SQL application the
        constraint is a lot less costly, but will cause the problems mentioned
        above. For this reason it is recommended that this constraint be removed
        from systems which support data distribution. 
      
      Consistency
       Consistency is the concept that all the instances of
        the database have the same data.
        
        A disconnected / synchronization system, is by nature not always
        consistent. There are times when the mobile device is disconnected from
        the network and so has a possibly old version of the database. Since the
        program is reading from the local data store the value returned is the
        last known correct value, but this may differ from a value elsewhere in
        the network.
        
        The system however is eventually consistent. Meaning that once
        all data stores have synchronized with the server, they will ultimately
        end up with the same version of the data. 
      
      Transactions
       Consider for a moment the nature of a transaction.
        It provides an atomic wrapper around multiple writes to the database,
        ensuring that if any write is made, all writes are made and if all
        writes cannot be made then none is made.
        
        The key phrase in that sentence is the - as in the database. How then do
        transactions occur when the database you are writing to is a local data
        store, and not necessarily the main data store? Log based replication
        systems are able to support transactions in the sense that the
        transaction frame can be written into the log file.
        
        However a disconnected system, such as the one described above,
        synchronizes the end result, on a table basis, and does not replay
        individual writes, or maintain the order of the writes. 
        
        There are fundamentally two kinds of transactions.
        
          - Multiple rows added to one, or more, tables. This is an Insert
            transaction - requiring that the whole set of records are inserted,
            or non are inserted.
- Data "moved" from one table to another - for example increasing a
            running total in one place, with a balancing decrease in a running
            total elsewhere. Think of this as an Edit transaction - one or more
            records are being edited to maintain some consistent state.
        Insert transactions are not a problem. They are added to the local store
        using whatever mechanism the local store offers. They can be in one, or
        multiple, tables. When synchronizing all the records will be written to
        the server.
        
        Edit transactions are more problematic. Consider a common case;
        When selling stock of an item, the quantity of the item is set in the
        Invoice Line Item, and deducted from the inStock value held in the
        Products table. 
        In this case the inStock value is a running total and every edit or
        insert in InvoiceLineItem requires a balancing edit to this value.
        
        This sort of transaction cannot be done in a distributed system, and
        indeed running total fields (or calculated fields) of this nature cannot
        be maintained using simple data synchronization.
        
        One approach to solving this problem is not to write to the running
        total at all at the deice, but rather set the server to update this
        total as it updates the lineItems table. In other words move the
        maintenance of the running table to the server rather than doing it on
        the device.
        
        Another approach is to calculate running totals, rather than storing
        them. So, rather than Edit records to indicate a change, Insert a record
        to a table so that the change can be recalculated at will.
        
      
Auto Numbering
       Auto Numbering is a common approach to generating a
        unique ID field for each table in a dictionary. In most cases the auto
        numbered value is a surrogate value (ie does not correspond to any
        actual value in real life - it's just used as a unique ID) although in
        some cases it may be a natural value (which has meaning, for example an
        Invoice Number.)
        
        Auto Numbered values are usually sequential integers - as each record is
        added the next ID is fetched from the server and used as the ID for that
        record.
        
        Auto Numbering in order to create a unique identifier has a number of
        disadvantages, not least of which is the requirement that there is "one
        entity allocating numbers". In a distributed system it therefore cannot
        be used as a means to generating primary keys values, since multiple
        (distributed) systems can create records at the same time.
        
        Auto Number can still be used for natural values (like invoices) with
        the proviso that they can only be allocated when the data reaches the
        (single) parent store. So multiple systems could add Invoices (using the
        GUID as the primary key) but the actual Invoice  Number is only
        allocated when the data is synced with the parent store.
        
        Of course since the Auto Numbered value is a natural value, it should
        not be used as the linking field to any child tables. The GUID of the
        parent table should be used as the linking value.
      
     
     The majority of requirements for disconnected
      applications are the same regardless of whether you are doing disconnected
      desktop, web or mobile applications.
      
Requirements
       The following global templates are required;
        
          - Activate NetTalk Global Extension
- NetTalk Global "Set Time Stamp Fields" template. 
- Activate StringTheory Global Extension
- Activate jFiles Global Extension
Field Rules
       The fields in dictionary need to be set as follows;
        
          - GUID field
 Set to a Random 16 character string when a record is created
- TimeStamp field
 Set to the current time stamp when a record is created, updated, or
            "deleted".
- ServerTimeStamp field
 Unaltered in the desktop program. In the server program (and ONLY
            the server program) is set to the current time stamp.
- DeletedTimeStamp field
 If a record is "deleted" by a user then the DeletedTimeStamp is set
            to the current time stamp. The record is NOT deleted from the table,
            it is written to the table as an update. Browses (Reports, Processes
            etc) in the table would need to be changed so that records marked as
            Deleted are not included in the Browse or Report etc.
Qualifying Tables
       In order to be included in a disconnected (web or
        mobile) application, the table has to qualify. The following criteria
        are used;
        
          - The table has to have a GUID field.
- The table has to have a TimeStamp, ServerTimeStamp and
            DeletedTimeStamp field.
- The table does not have the user option 
              NOSYNC=1
You can also control the specific tables exported in database.js
          using the 
            Tables tab.
      Deleted Records
       Most of the changes required to turn a "normal"
        Clarion ABC application into disconnected desktop application can be
        automated. The necessary changes to the timestamp fields and the
        changing of a Delete into an Update can be managed by a global template
        overriding the File Manager object.
        
        However procedures that read the tables will need to be adapted. The
        tables will now contain records which are marked as deleted, and these
        records need to be filtered out from browses, reports, processes and so
        on where appropriate.
        
        In the case of browses it may be desirable to offer the user a switch to
        show, or hide, deleted records - ultimately allowing them to be
        undeleted (by setting the deleted time stamp back to 0.) 
        
        Unique keys, other than the GuidKey, also need to be considered in the
        context of deleted records. For example, if there is a unique key on the
        Name column, and a Customer "Bruce" is deleted, then adding another
        customer (or the same customer) back with the same name will fail, even
        though no customer called "Bruce" appears to be in the table. Adding the
        DeletedTimeStamp as an additional component to these unique keys may
        suffice as a solution.
      Template Support
        (ABC)
       A Global Extension template, called 
NetTalkClientSync
        template can be added to ABC applications. If you are making a
        disconnected desktop application then you will need to add this template
        to the desktop app. If you are creating a disconnected web, or mobile,
        application then add this template to the web app. While you do not need
        to add it to the SyncServer app, it does no harm to have it in that app,
        if that app is shared with say the web app.
        
        This template performs the following chores;
        
          - A global utility object called glo:SyncDesktop
              is added to the application. This object can be used to get
            the current time stamp. As in;
 glo:SyncDesktop.GetElapsedTimeUTC()
- All the time stamp fields are updated appropriately when records
            are inserted or updated in any table. This is done in the FileManager
              object, so will apply as long as ACCESS:table.INSERT
            method is used for adding records (not just a file driver ADD
              or APPEND) and ACCESS:table.UPDATE
            is used instead of the file driver PUT command.
            
- When a record is deleted, the DeletedTimeStamp is set, and the
            record is updated, not deleted. This works as long as the ACCESS:table.DELETERECORD
            method is used and not the file driver DELETE
            command.
Apps
       Conceptually in any disconnected system there are at
        least two parts which need to work together, the client program and the
        server program.
        
        In the case of a disconnected desktop system this usually means two
        separate apps, a desktop app, and a server app.
        
        In the case of a web (or mobile) program though the server app can
        fulfill both the server role, and the client role. In other words in the
        web case the server part and the client part can be developed in the
        same app file.
      
      Server App
       The server app consists of a single NetWebService
        (usually called Sync) and a number of NetWebServiceMethods. Usually
        there is a single method for each table in the database. This
        application can be generated for you by the NetTalk Wizard. For more
        information on generating this application see the section 
Server
          for Disconnected Apps below.
        
        You can then expand the Server app to include a web-client interface.
        This interface can be a normal web app, or a disconnected web app. If a
        disconnected web app then it can be packaged as a stand-alone mobile app
        as well.
Non-Clarion (or
        PROP:SQL) Writes
       If the data in the table is edited from a program
        not using the NetTalk Client Sync Template, then the rules for updating
        the Guid and Timestamp fields MUST be observed. This includes programs
        like TopScan, SQL Manager, and PROP:SQL statements that do direct writes
        to the database.
        
        Use of this distributed data system specifically does not limit you to
        Clarion programs. Any program in any language can read and write to the
        data, using any appropriate technique, as long as the field rules are
        followed. Some samples of getting the time-stamp value in various
        languages are; 
    
      
      Introduction
       Disconnected Desktop apps basically have a local
        copy of the data. The program itself is written to talk to this local
        copy pretty much like any Clarion desktop program would talk to any
        database.
        
        Then a "Sync" procedure is added to the local program. This procedure
        synchronizes the local data with the remote NetTalk API "Sync" Server.
        That Sync Server then interacts with the server-side database.
        
        

        This architecture is useful when the connection between the desktop
        program is either too slow, or too unreliable for direct connections to
        the main server in the cloud.
        
        The key is in noticing that the desktop program, and the desktop data is
        completely unconcerned with the network connection. It is the
        responsibility of the sync procedure, which is running in a separate
        thread, or even a separate process, to communicate changes with the
        parent server, and to get changes made in the parent server back to the
        desktop.
        
        This architecture is not limited to a single desktop data set. The
        following is also valid
        
        

        Multiple different locations can synchronize with the same server.
      
Sync with Server
       A Sync procedure can be imported from the example 
desktop.app application. This example is in 
\clarion\examples\nettalk\apps\DisconnectedDesktop\Desktop\Desktop.app.
        In order for data to flow between the desktop app and the server, a
        synchronization procedure is required. This procedure can be a
        background procedure in your application (ideal where a single user
        exists for the remote program) or as a separate program on the LAN. The
        sync procedure can be triggered by a timer (every few minutes or so) or
        it could be triggered by NetRefresh so that it synchronizes after every
        write.
        
        If the desktop is unable to connect to the server then the desktop will
        continue working as normal, and the data will then be sync'd on the next
        working connection.
        
        The Sync procedure is created as a simple window with the 
NetTalkClientSyncControls
        Control template added to it. This template requires the 
NetTalk
          - Set Time Stamp Fields  global extension be added to
        the application. 
        
        The Sync procedure runs on its own thread and can be communicated with
        using events. The thread number of the procedure is stored in 
          glo:SyncDesktop.ControllerThread
        
        A Sync can be triggered at any point, by any thread, by simply posting 
 Event:SyncNow to the Sync thread. If a Sync is
        currently underway then it will complete the current sync and do another
        full sync immediately afterwards. For example;
        
        
Post(Event:SyncNow,
          ,glo:SyncDesktop.ControllerThread)
        
        The Sync itself is a fairly cheap operation with minimal overhead, so it
        can be called on a regular basis. If no data in a table has been updated
        on the server, or client, then it's a very small request and response
        for the server.
        
        
Note  the Sync Procedure will
        not create a table on the desktop app if it does not already exist. If
        you want the sync procedure to create the table, then add the table to
        the data pad for the procedure.
        
On Delete
         As explained earlier, when records are deleted
          they are not really deleted, the DeletedTimeStamp
            field is just set. This allows the delete to propagate up to
          the server when a sync occurs.
          
          By default these records are then left in the local database, as well
          as the server database until you do a purge.
          
          The Sync Controls extension template, in the Sync procedure, has an
          option to purge deleted records from the client as soon as they are
          sync'd with the server. The deleted records will still appear in the
          server database as before, but they will no longer appear on the
          client. If this option is on then any deleted records sent by the
          server to the client will also be immediately removed from the local
          database (if they exist) and will not be added if they don't exist. 
        
        Sync Procedure Template
          Options
         The Sync procedure contains a NetTalk - Sync
          client tables with server Extension template. The options on that
          template are as follows;
          
Server URL:
          The URL of the server that this program will be synchronizing with.
          
Timer Period
          The background timer that determines how often this client connects to
          the server. This is a clarion time value, ie hundredths of a second.
          The default value of 6000 is 1 minute. This mostly affects fetching
          data that has been changed on the server side - data changed on this
          client side tends to be sent to the server immediately.
          
Various Icons
          Change the names of the icons if you wish.
          
          UPGRADING: This limits the number
          of records that will be sent to the server (or requested from the
          server) in a single Synchronization request. Keeping this number small
          reduces the memory requirements of the server and the client. A value
          of around 500 or less is recommended. If this value is set to 0, then
          support for this feature is turned off, and each synchronize will
          send, or fetch, all the data that has changed. If this is high number
          of records, then this can lead to significant memory consumption on
          the server and the client.
          If this feature is on then the server has to be set to support it. See
          above for 
more
          information.
          
Status Field
          If you have a string control on the window, which can be used to give
          the user updates, then set it here. If this is blank then the
          processes will still proceed, but with fewer messages to the user
          about what is happening.
          
Auto Format Fields
          If on the fields are formatted, according to their picture, when being
          sent.
          
Purge Deleted Records on Sync
          If this is on the the client database will not keep any deleted
          records after they have been sent to the server. Deleted records
          typically live on the server for a while (so they can be replicated to
          other databases) but if this option is on then deleted records are not
          stored on the client machine. 
Customizing the Sync procedure
         Out of the box the Client Sync procedure (usually
          called ServerSync) will synchronize all the tables, and rows, in the
          local database with the server (for all tables that have the necessary
          synchronization fields.) 
          There are however a number of embed points in the sync procedure where
          code can be added to customize the sync.
          
          Inside all embed point  the property 
self.TableLabel
          can be checked to determine which table is currently being
          synchronized.
          
          
SetJsonProperties
           This method provides an opportunity to override,
            or set, jFile properties for the outgoing JSON being sent to the
            server.
            the self.json property contains the
            jFiles object.
            
            For example
             self.json.SetTagCase(jf:CaseUpper)
          
          CustomData method
           The CustomData method
            allows you to enter additional JSON fields into the outgoing JSON
            collection. 
            
            Fields such as token, table
              and action are already
            included, but this embed allows you to enter additional information
            that may be required by your server. 
            
            The self.json property contains a
            jFiles collection, which you can Append to.
            for example; 
            
            self.json.append('user','Humperdink')
          
          SetFilter method
           When the local data is loaded into the outgoing
            JSON a View with a FILTER are used to determine which records are
            sent to the server. 
            
            A method, called SetFilter, is called
            AFTER the filter is set, but before the call to jFiles.append,
            to allow you the opportunity to edit the filter. 
            
            Use the self.TabelLabel property to
            know which table is being exported. 
            
            TThis embed allows you to suppress rows in the table which should
            not be sent to the server. For example you may choose to exclude all
            records from the table where some LocalField is set.
            
          
          ValidateRecord method
           This method is called on both exporting records
            to the server and importing records from the server. The properties
            self.Importing and self.Exporting
            are set appropriately, so test these when importing or exporting.
            
            If you add code to this method it should return one of the following
            values;
            jf:ok - the record is fine;
            jf:Filtered - the record should be
            excluded from the import or export. 
             jf:OutOfRange - the record (and all
            subsequent records) should be excluded. This this option very
            carefully, especially on import.
            
          
          FormatValue method
           Allows you to format values in outgoing JSON.
            Typically this is done for you by the templates (based on pictured
            in the dictionary) but you can supplement that, or override it if
            you like.
          DeformatValue method
           Allows you to deformat values in incoming 
            JSON. Typically this is done for you by the templates (based on
            pictured in the dictionary) but you can supplement that, or override
            it if you like.
        Server-side Filtering
         Consider the case where there is one server (with
          the complete database of all branches) and several distributed data
          sets each with a sub-set of the data (we'll call these "branches" in
          this example.) 
          
          The goal is allow each branch to receive only the data, and data
          updates that apply to this branch. The problem breaks down into two
          parts;
          
            - The branch needs to identify itself when doing a sync, and
- The server needs to filter the data based on that ID.
Client Identification
           As described above, additional information can
            be sent to the server by specifying Custom Data on the client side.
            So, in the Desktop program, in the ServerSync procedure, NetTalk
            Sync tables with server Extension, Custom Fields tab, add a
            parameter name and value. 
            (Hint: The name should be in quotes)
            
            
 
            
            You can name the parameter anything you like, and the value would be
            a local, or global variable (or whatever) that identifies this
            branch. Keep the name part as a lowercase string.
Server Filter
           On the server side, for each Sync procedure that
            needs this filter;
            
              - Add a local variable of the same name (branchid
                  in this example) as is being sent by the client
- Add the variable as a PARMAMETER to the method. You will
                already see other parameters listed there, like maxrecords,
                and skiprecords and so on. Add
                your variable (branchid) to the
                list.
- Use the local variable, branchid,
                in your filter.
 If the id value is a number then the filter might look like
                this;
 
 'cus:branchid = ' & branchid
 
 If the id value is a string then the filter would look something
                like this;
 
 'cus:branchid = <39>' &
                  clip(branchid) & '<39>'
 
 If you have an index (key) on the table, and the key is Not Case
                Sensitive then the filter becomes something like this;
 
 'UPPER(cus:branchid) = <39>' &
                  upper(clip(branchid)) & '<39>'
 
    
      Introduction
       A disconnected web app is a web application where
        the browser can be disconnected from the network, but the web app itself
        continues to work. In other words a user can go to the web site (say via
        a Wi-Fi connection) start working, then walk away, out of Wi-Fi range.
        While out of range the app continues to work, and the user can continue
        to capture and edit data. Once the user is back in range, and connected
        to the server again, then the data is automatically synchronized.
        
        The basic architecture for a disconnected web app is very similar to a
        normal web app, however there are some key differences;
        
          - The app is created as a Single Page Application. This means that
            the browser will do a single FETCH to the web server, and will
            receive the entire application. Supplementary resources (CSS and JS)
            are also fetched at this time.
- The app does not interact with the server as the user uses the
            system. Rather everything is done on the client, and data is stored
            in the browser local storage. This means that embedding Clarion code
            in the server is not useful as the server code is not called.
            Embedding, where necessary, would need to be done in JavaScript.
- The synchronization of the data requires a Server
              for Disconnected Apps. In this server there will typically be
            one sync method for each table in the application. Once the web app
            is running on the device, the only communication with the server
            will be via the Sync methods. 
- The Sync methods can be included in the same actual app file as
            the Disconnected Web App.
It should be noted that a web app can contain a mix of connected, and
          disconnected areas. In other words a single system can contain both a
          "normal" web app, as well as one or more "disconnected" apps. This
          leads to some flexibility in the way the application is designed.
      Supported Browsers
       Disconnected Web Apps make use of the HTML 5 feature
        called "Local Storage". This allows browsers to store information in a
        data store belonging to the browser, which in turn means the app can
        work when it is not connected to the network. Local Storage is supported
        in all the modern browsers with the notable exception of Opera Mini. 
        
        In iOS 5 and iOS 6 it's possible for data in the local storage to be
        cleared by the OS, so use of devices running those operating systems is
        not recommended.
        
        IE7 and earlier is not supported. Use of any version of IE for
        disconnected web apps is not recommended as Local Storage under IE has a
        number of different behaviors compared to other browsers. 
      Task Based Design
       Because of the above differences it is very
        important that the design of the application be focused around what
        tasks the user will need to accomplish when using this app. It is
        probable that in many cases only a limited sub-section of functionality
        is required by a user when they are disconnected from the network. By
        clearly thinking through the tasks which you need to support, you will
        better be able to design the appropriate application.
      Summary
      
        
          - Global Extensions, Activate NetTalk Web Server extension, Advanced
            tab. Tick on;
 Generate for Disconnected App is on.
- Make sure the menu is set so that all the links are opened as
            Popup. Links set as Link or Content are allowed, but those links
            will not work if the browser is disconnected from the network (ie
            cannot access the server.)
 
 
      Application Cache
       The Application Cache Manifest file, while not
        absolutely required in order to do web apps, is a way of explicitly
        telling the browser which resources will be required by the app. This
        feature is supported all major browsers except for IE9 and earlier, and
        Opera Mini. 
        
        The application cache file is generated into the web folder as app.appcache
        where app is replaced by the name of your application. A Manifest
        attribute in the HTML of your root page tells the browser of the
        appcache file. 
        
        You do not need to do anything to turn this support on.
     
    
      
      Introduction
       Because NetTalk now creates disconnected web
        applications, it is possible to take those web applications and
        repackage them into a native app format.
        
        For example, using Adobe PhoneGap (which in turn is built on Apache
        Cordova), it is possible to wrap the HTML, CSS and JavaScript into a
        native application which then uses a native HTML control to show the
        HTML to the user. This application contains a mix of native code (eg
        ObjectC on iOS or Java on Android) and the HTML you have created. In
        addition frameworks like PhoneGap extend the JavaScript language to give
        the JavaScript engine access to the native operating system API, which
        in turn means you have access to the hardware, contacts list and so on.
        
        Apps built in this way are sometimes know as Hybrid apps, because they
        combine the portability of HTML and JavaScript, with a minimal amount of
        generic native code to expose the underlying hardware and OS.
        
        The big advantage of this approach is that multiple operating systems
        can be supported from a single code base. This makes it ideal for
        systems which need a mobile application, but which don't necessarily
        have the economic viability of re-coding the same app multiple times
        (especially for some of the more minor platforms.)
        
        There are two disadvantages with this approach though. The first is raw
        performance. Because the code is HTML and JavaScript it does not have
        the raw performance of a native ObjectC or Java program. This makes it
        unsuitable for high-performance situations, like games. For data
        collection applications though performance is certainly good enough, and
        the responses are mostly quite snappy. The second disadvantage is that
        the interface can look, and behave differently to a native application.
        Using themes and good layout it is possible to mimic functionality to a
        high degree, but it will never be 100% the same. 
        
        Hybrid apps using PhoneGap can be added to the Apple AppStore but must
        still go through the Apple approval process just as any iOS app needs to
        do. As such it must conform to Apple's standards for design and
        usability. Apps can also be submitted to the Google PlayStore (which has
        a much lower set of standards and requirements.)
        
        NetTalk generates the necessary file to make it easy to package the
        application using Adobe PhoneGap. 
      Adobe PhoneGap
       PhoneGap is an open source framework, currently
        owned by Adobe, which makes it easy to package hybrid applications.
        PhoneGap was released under an open source license as Apache Cordova.
        the current PhoneGap is an Adobe product, built on top of Cordova, which
        encompasses some extra tools. 
        
        PhoneGap supports a number of platforms including iOS, Android, Windows
        Phone, Blackberry, Bada, Symbian, webOS, Tizen, Ubuntu Touch and Firefox
        OS.
      
      Adobe PhoneGap Build
       PhoneGap Build is an online platform that allows you
        to upload all your HTML, CSS and JavaScript and then does the
        heavy-lifting of turning that into a native iOS package or Android SDK.
        You don't have to use PhoneGap build, but it is certainly a convenient
        and reasonably cheap way to get started. You do not have to use PhoneGap
        Build, it is perfectly possible to create your own build environments,
        but it is a recommended way to start.
        
        PhoneGap Build supports iOS, Android and Windows 8 as target platforms.
        
        PhoneGap Build requires a configuration file, called config.xml,
        which contains information for the build system. This file is generated
        for you by mBuild.
      NetTalk PhoneGap
       Using NetTalk to generate Mobile applications using
        PhoneGap is the result of many NetTalk technologies working together.
        
          - The database has to be designed to work is a disconnected way.
- A Server-side API app for synchronizing the data is created.
- A disconnected Web Application is created.
- The resources for the disconnected app are then packaged together
            and passed through the PhoneGap Build system. This is done for you
            by the mBuild utility.
- The resultant Android APK can then be downloaded and installed on
            Android devices. Similarly the iOS build can be installed on iOS
            devices and so on.
mBuild
       mBuild is a utility that ships with NetTalk Apps. It
        does the work of turning your disconnected web application into an APK,
        IPA or XAP file. For more on mBuild see the mBuild documentation 
here.
      
Compatibility
       The following mobile device requirements are needed
        to run NetTalk Apps.
        At this stage it's not known that these versions work, however it is
        known that versions before this won't work.
        More information will be posted here when available.
        
          - IPA File: iOS devices : iOS version 10.3 or later.
- APK File: Android 5 (Lollipop) or later (with Chrome 58 or later
            installed)
- Chrome Browser: Version 58 or later
Web-App to Phone-App Checklist
       If you have a web app then you can use this list to
        set it up to be a phone app ready for mBuild. This is a checklist of
        setup items, it doesn't comment on the content of the app itself.
        
          - Adjust the Dictionary to use the table layouts as discussed in Distributed Data Synchronization
            above.
- Consider the addition of a Single Record
              Settings Table - you'll almost certainly need one.
- Create the Server App.
            Make sure this app is served over
              HTTPS.
- Go to the Global NetTalk Extension (Activate NetTalk Web Server)
            
              - WebApps tab - Turn Off Pinch To Zoom should be on.
- Apps Tab - Set everything it needs here.
 
- WebServer procedure - NetTalk extension - Settings / Defaults /
            Browse & Form . Layout Method to DIV.
- Create favicon512.png and put into the
              \web folder.
- Make sure the Phone app is served
              over HTTPS.
- After mBuild runs make sure manifest.json is deployed to the \web folder
 
    
      Introduction
       As the web, and mobile apps, have evolved, so a new
        class of application has appeared. The Progressive Web Application is a
        web app which conforms to a set of standard behaviors and rules. Once it
        does this it can be installed on a phone and will appear to the user as
        a native application.
        Since we are already creating applications in NetTalk suitable for being
        published as a phone app, it is only a short path from there to creating
        a PWA. In effect you create your application and can then publish it as
        a PWA, Phone app, or (likely) both.
        
        There are a number of advantages of a PWA over a Phone app.
        
          - No approval by any App store
- No need for a user to download the App
- Very easy to roll out updates (since the app effectively checks
            for an update on each run.)
        Phone apps still have a slight edge in being able to access the native
        OS directly and thus access certain things the PWA cannot.
Requirements
       In order to be a PWA a web app has to conform to a
        number of very specific requirements. The exact list is maintained at 
https://developers.google.com/web/progressive-web-apps/checklist#baseline
        and could be subject to change in the future.
        
        The key requirements, and how they are managed in NetTalk is listed
        below.
        
        
          
            
              | Requirement | Handling | 
            
              | Site is served over HTTPS | NetTalk can serve HTTPS apps, and with LetsEncrypt support
                this process is free of any cost, is easy to do, and is
                maintained automatically. | 
            
              | Pages are Responsive | DIV Layout mode for Tables and Forms | 
            
              | All App URL's load while Offline | NetTalk generates a ServiceWorker.Js file into the \web folder
                to do this. (See Global Extension, apps, PWA tab.)
 | 
            
              | Metadata provided for Add to Home screen. | A manifest.json file is generated by mBuild and needs to be in
                the \web folder. | 
            
              | First Load fast (even on 3G) | NetTalk does this for you by implementing several speed
                optimizations under the hood. | 
            
              | Site works cross-browser. | NetTalk does this for you. | 
            
              | Page transitions don't feel like they block the network. | The app is a single-Page-Application, so no need for page
                transitions. | 
            
              | Each Page has a URL | The app is a single-Page-Application, so there is only the
                home URL. | 
          
        
        From the above list you can see that many of these items are a
        side-effect of creating a phone app, and so there is no extra work to be
        done. There is an extra tab to setup on the global extension, and an
        extra tab to be completed in mBuild.
        
        mBuild will generate a 
manifest.json
        file, this needs to be deployed into the \web folder when you deploy
        your web app.
        
        The 
manifest.json file will include a
        reference to a png file, also in the web folder, called 
favicon512.png.
        You should create this icon and place it in the web folder. As the name
        implies the file is a PNG file, and is 512 x 512 pixels in size. If you
        fail to do this the server will serve 
defaultfavicon512.png
        in its place.
        
      
ServiceWorker.js
       A new JavaScript file, ServiceWorker.Js is generated
        into the web folder. This needs to be deployed along with the rest of
        the web folder when deploying the application. This file cannot be moved
        into the \web\scripts folder. 
        
        Service workers are a special kind of JavaScript that runs in the
        browser and acts as a wrapper to the application. Many of the features
        necessary to make a PWA happen inside this file. The browser will
        aggressively cache this file, so replacing it can be tricky. It
        sometimes requires multiple loads of the site, or manual intervention in
        the browser developer tools, to refresh this file. Fortunately changes
        to this file should not be common.
        
        The service worker also caches a number of other files from your site,
        which in turn can have implications when updating static files,
        especially js and css files. Be aware that 2 page refreshes may be
        necessary to see the latest content.
      Chrome, Developer Tools, Audits
       The Chrome browser contains a number of features in
        the Developer Tools window that make it easier to test and debug PWA
        applications. Most specifically an audit tab
        is available so a site can be tested to see that it conforms to all the
        PWA requirements.
     
     This section covers areas where programming a
      disconnected (browser) application differs from a connected web
      application.
      
      Database Upgrades
       In order to work in an offline state, the
        disconnected app makes use of local data storage on the device, which is
        then synchronized with the server from time to time. the name of the
        storage used is IndexedDB. 
        
        IndexedDB has a version number system. Certain types of changes require
        that the version number is incremented, whereas other types of changes
        do not. 
        
        You have 2 options when it comes to setting the dictionary version. 
        The first option is on the 
          NetTalk global extension, Activate NetTalk Web Server, Apps / Tables
        tab. If the Dictionary Version is set here, to 1 or higher, then this
        value is used. If this is set to 0, then this value is not used, and the
        second option is used.
        
        The second options is for NetTalk to take the version from the
        Dictionary Version number (Dictionary menu, show Properties Dialog). 
        
        Adding, or changing fields in a table does not require a version number
        change.
        Adding, or changing relationships in the dictionary does not require a
        version number change.
        
        Adding a table, or a key, does require a dictionary number change. Until
        this is done the table, and key, will not be available in the
        application on the phone.
        
        Remember - if you add a new table to the dictionary that you wish to use
        in the mobile app and you are not including all the tables in the mobile
        app, then you will need to add the new table to the 
Tables
          List as well.
      
Custom JavaScript File
       In a web application it is unnecessary for you to
        write custom JavaScript functions as the server is able to let you write
        your code in Clarion. In a disconnected application though all the code
        has to be written in JavaScript. While the templates will generate most
        of what you need, you will almost certainly get to a place where you
        want to make some small adjustment or add some code not currently
        generated by the templates.
        
        To do this you will need to create a custom JavaScript file, and link
        that file into your application. Once that is done you are free to add
        to the file whenever you need to.
        
        JavaScript files are simple text files, usually with the extension .js. They belong in your \web\scripts
        folder. You can name the file anything you like - something unique and
        related to the application is recommended.
        
        Once you have created the file, add it to the WebServer procedure. It is
        added on the NetTalk extension, Settings / Scripts tab, in the scripts
        list. For now just add the new file to the list, and accept all the
        default settings for the file.
      
      Field Priming 
       The most common use for a JavaScript function is to
        prime form fields when a form opens. On the template side NetTalk allows
        you to specify an expression for priming fields. You can prime fields on
        Insert, Change, Copy and you can also Assign fields on Save. 
        
        Since these expressions are language-specific, you will need to enter
        either, or both, Clarion and JavaScript values here. Also, the goal is
        to keep these expressions as simple as possible, which often results in
        a small Clarion Source procedure, or a small JavaScript function being
        used.
        
        Some simple JavaScript functions exist in the NetTalk framework which
        may help in priming fields; (remember that JavaScript is Case Sensitive)
        
          
            
              | Expression | Description | 
            
              | clock(picture) | Returns the current time, as hh:mm:ss The picture parameter is not currently used.
 | 
            
              | getUTCTime() | The number of milliseconds since 1970/01/01: | 
            
              | today(picture) | Returns the current date, formatted using the picture
                parameter (which is a Clarion Date Picture).  If omitted
                then @d1 is used. Supported pictures are @d1, @d6, @d10. | 
          
        
        Note also that the From Location sub-tab (on the Priming tab) can be
        used to prime fields with the current Latitude and Longitude. Fields to
        receive these values need to be on the firm, but they can be set as
        Hidden fields if necessary.
        
        You can also write your own JavaScript function, add it to your 
custom.js,
        and call it from here.
        
Assign on Save
         The 
primeOnSave JavaScript
          function is called when the user clicks on the Save button, before the
          record is written to the IndexedDB data store. Any code added into
          this function by the 
template
          MUST NOT use any asynchronous functions - in other words it cannot
          call any of the IndexedDB methods. If fields need to be primed from
          the IndexedDB data store you should do this when the form opens.
Priming from the Local Database on the device
         There are some  complications which come into
          play if you want to prime a field from some other field in the
          database. 
          
          Firstly, the interaction with the database is asynchronous. So it has
          to happen when the form opens, and not as an AssignOnSave. [3]
          
          Secondly you will need to pass something into the form to indicate a
          field, either in the parent record, or on the screen, that needs to be
          primed.
          
          It is a good idea to pass the form itself to the function as the first
          parameter (and then pass more parameters if you like.) The form can be
          passed using the parameter this. [4]
          So in the template settings the call could be made as
           primeSickLeave(this)
          or perhaps 
           primeSickLeave(this,'lea__leavetype') [5]
          
          All of this is best demonstrated with an annotated example; 
          
          In this example a default value for Leave Type is being set when the
          user wishes to add a Leave record. The default leave type is set with
          a field (quicksick) in the leavetype table.
          
          function primeSickLeaveType(form,field){
              idbSelect(database,database.leavetype.table,
               
            0,                                            
            
               
            false,                                        
            
               
            0,                                            
            
               
function(index,record){                        
                       
            if (value.quicksick ==
            1){            
                       
            form.record.leavetypeguid = record.guid            
            if (field){
               
                       
            $("#" + field).val(record.guid)       
                        }
                       
            return
            false;                         
            
                    }
                    return true; // get next
            record
                },
                0, // i
               
            function(){                                    
            
               
            },                                             
            
               
            function(event){                               
            
                    console.log('Error
            Priming SickLeave record: ' + event.target.error.name + ' ' + 
                                
            event.target.error.message)
                }
             
            )                                                
            
            }                                                  
            
          
          Because the idbSelect call is
          asynchronous, functions are passed to the call which execute when a
          record is located in the database. This means the field may not be
          primed immediately as the form opens, but rather shortly (very
          shortly) thereafter.
          
          Notes
          
          1. idbSelect selects the records from a
          table. For each record found the onrecord function
          is called. If that returns  false, then
          the result set is terminated.
          
          2. The onrecord function takes 2
          parameters, the index in the result set,
          and the record itself. All the fields in the table (parameter 2 to
          idbSelect) are available, and primed, as a property of the record
          parameter. Thus record.guid is the guid field in the result set. Note that at
          this point 
            database.mleavetype.table.record.guid is not set.
          
          3. You may be tempted to use the Assign On Save priming type, and you
          can use that for simple functions, however you can't use that for code
          which contains asynchronous functions. Since all idb functions are
          asynchronous, you are not allowed to use them in code called by
          assignOnSave.
          
          4. this is a special word in JavaScript,
          which is not unlike the word self in
          Clarion.
          
          5. If the field you wish to prime is not on the form itself then you
          don't need the second parameter. If the field you wish to prime is on
          the form, regardless of whether it is visible or hidden, then pass the
          field id (field equate) in as the second parameter. Remember that id's
          in JavaScript are case-sensitive and the colon (:) character is
          translated into 2 underscores.
          
          
        
      Single Record Settings Table
       In a web app there are multiple users using the same
        server, and so there is typically a record in a user table for each
        user. This table contains all the settings for a user. 
        
        By contrast, in a disconnected app it is often necessary to have a
        "settings" table for the user using that device. This is a single-record
        table, and contains important settings which allow the app to work.
        
        Specifically it will likely contain one or more of the URL of the Sync
        Server machine, the User name and Password. Since it contains
        information needed to connect to the sync server, the Settings form
        needs to be functional without a connection to the server.
        
        The application needs to be able to do the following;
        
          - Add a single record to the table if it does not already exist.
- Prime the GUID field in the new record to a unique value.
- Open a form to allow the user to edit the fields in this record
            (although the GUID of the record is not known at the time when the
            program is generated.)
Fortunately there is template support for a single record table, and
          this generates the necessary JavaScript to add the first record for
          you. The settings for this is on the Global Extensions, the "second"
          NetTalk global extension (Activate NetTalk Web Server), on the Apps /
          Settings Table tab. Enter the settings table here, as well as identify
          some common fields that might be in your table.
        The above template settings take care of the first two requirements
          in the list. The last one is done when you open the form. Because the
          GUID of the row is unknown, the unique id value can be set to the
          special value, _first_. The form will
          then understand that you mean to edit the first row (in this case the
          only row) in the table.
      User Authentication
       One of the aspects of App development you will come
        across fairly early is identifying, and authenticating, the user who is
        using the app. Typically you will want to restrict access to the Web API
        to users using your app, or you may want to restrict the data being sent
        to the user based on who they are.
        
        There are many ways to approach this, this document shows some possible
        methods, but it doesn't cover all the possibilities. Feel free to salt
        to taste as required.
        
        The simplest approach is to make use of a Single-Record settings table,
        as described 
above. Make sure the table
        has a field to hold the user name and another field for the password.
        Then assign those fields in the global description.
        
        If this is done then those fields are automatically used when making a
        sync request to the server. They are used to construct the HTTP
        Authentication header, using Basic Authentication. If done over a TLS
        (SSL) connection this is safe and secure.
        
        On the server side, the authentication needs to be validated in the
        WebHandler procedure, in the Authenticate method. The user name and
        password will be passed in there and you can authenticate it there. 
        
      
Data Type Selection and Transformation
       It is common when designing a database to
        distinguish between the display of the data and the storage of the data.
        For example a date is usually displayed as a string (3 Oct 2016 or
        10/03/2016) but stored as a DATE or LONG type. 
        
        In order for this to work data has to be transformed when read from the
        database before being displayed, and after user entry, it has to be
        transformed again before being stored in the database. In Clarion this
        happens automatically, based largely on settings set in the dictionary.
        Since other languages do not have a dictionary this process becomes very
        manual, and has to be applied manually on a field by field basis.
        
        For this reason it is suggested that the storage, and display, of the
        data be the same - at least at the mobile app end of things. The server
        side can then transform the data as required between a string and a
        local data type if desired.
        
        This process is automated by the templates for the DATE and TIME field
        types, as well as for LONG fields set in the dictionary with a @D or @T
        picture. In these cases the field generated in the JavaScript is a
        String, not a number. For the Sync methods on the server the incoming
        data is automatically transformed back into the appropriate numeric
        (DATE, TIME, LONG) format.
      
      Client-Side Field Validation
       In a regular web app field validation is done on the
        server. In a disconnected web app (or mobile app) there is no connection
        to the server so the validation has to be done on the client 
[1].
        
        
        To make this possible each form field has a validation tab. On this tab
        is an option  
onchange
          [js] which allows you to enter an expression, containing
        JavaScript code, which will execute on the client side when the field is
        changed. Typically this is a call to a JavaScript function in your 
custom.js file.
        
        Note that at this time none of the other Validation settings are
        automatically applied to the app. So any, and all, validation must be
        done in your custom JavaScript code.
        
        
Note 1: Validation still needs to be done
        on the server side, in the sync procedure, because data from the client
        cannot be trusted. However validating it in the client will help
        "honest" users in the sense that their data will not be rejected.
      
Client-Side Filtering
       On the Browse template, Filters tab, there's an
        option to set a server-side filter (in Clarion) or a client-side filter
        (in JavaScript).
        This section describes how local data can be filtered when displaying it
        in a local browse.
        
        Note: The easiest way to filter, is not to filter at all. In other words
        the need for a client-side filter should be minimal - ideally the device
        should only receive the data it needs, by filtering it in the relevant
        sync procedure. So if you find yourself "always" filtering, ask yourself
        if the data should be on the phone in the first place.
        
        That said, it's not uncommon to filter at least some of the browses on
        the device, even when it's reading from local data. 
        There are some differences to the approach between Clarion and
        JavaScript filters though.
        
        First thing to note is that the filter itself is written in JavaScript,
        not Clarion code. This would seem to be obvious, but is worth
        mentioning.
        
        Second thing is that Clarion filters are designed to be used inside a
        VIEW prop:filter, which in turn treats them like a kind of IF statement.
        If the statement is true, the record passes.
        
        In JavaScript the code needs to return a value. Permissible values are;
        recordOk, recordFiltered
        and  recordOutOfRange. Remember JavaScript
        is case sensitive so these equates need to be used exactly as is. You
        don't have to return all these values, any one will do. recordOk
           means the record will be included in the result set, recordFiltered
          means it will not be included, but reading will continue,  recordOutOfRange excludes this record, and any
        subsequent records. 
        
        In a JavaScript filter the fieldname is prefixed by record.
        . This means the "current value in this row" is being compared to
        something. for example;
        
        'return record.paid ==1 ? recordOk :
          recordFiltered'
        
        In the above expression the value in paid is compared to 1 (in
        JavaScript an equals comparison is two = characters). The ? is similar
        to a Clarion CHOOSE command - if the
        expression is true the first choice is used, if false the second. The
        choice are separated by a colon.
        
        The most common filter compares the record to another field on the
        screen - presumably where the user has entered a value. For example;
        
        'return record.date >= $("#cloFromDate").val() ?
          recordOk : recordFiltered;' 
        
        The cloFromDate field is an entry field on
        the form. In the above syntax, $("#cloFromDate").val()
          returns the current value of the field, as it's seen on the
        screen. Since the filter is inside single quotes (because on the server
        side it is treated as an expression - a string - it is handy to use
        double quotes when needing to quote things in the actual JavaScript.
        
        The record prefix only applies to the primary table in the browse. If
        you wish to test the value of any secondary table fields then you must
        use the full database.tablename.record
        prefix there. Table names are always in lowercase. For example;
        
        'return (record.date >= $("#cloFromDate").val()
          && database.leave.record.approved > 0)? recordOk :
          recordFiltered;' 
        
        Given that the filter is a function you can write a custom function in
        your custom.js file and call it from here
        as well. Just be sure to return one of the values specified above.
      
     A general discussion of the JavaScript language is
      beyond the scope of this documentation, however there are some useful tips
      around the specific NetTalk JavaScript which may be useful here.
      
Database.js
       Note: As from build 9.18 the resultset
           property has been removed. Functions using resultset
        (idbSelect, idbSummary) should pass in a resultset parameter array
        instead.
        
        The tables from your dictionary, and a number of helper functions, are
        generated into scripts/database.js. This
        file contains the table structures, as well as other information for
        each table. It also contains a number of properties for the database
        itself. 
        
        A global object called database is
        declared. 
        This contains a number of properties (such as synchost, user, password,
        and so on.) You can use these properties in your own code if you need
        to. For example;
        var user = database.user;
        
        It also contains an array of tables. One item in the array for each
        table used by your application. As it is an array each table has a
        number, but this number could potentially change if new tables are added
        to the system. Each table also has a name, and you can reference a table
        using this if you like. For example;
        var sometable = database.tables[0]
         JavaScript arrays are base 0.
        
        Here's an example of code to loop through all the tables;
        
        var j = 0;
          for (j in database.tables){
              if(database.tables[j].name == 'customer'){
                  sometable =
          database.tables[j];
              }
          } 
        
        To make things a bit easier an alternate name is generated for you.
        Instead of referring to the table by its number in the array, you can
        refer to it by name. For example, for a table called customers;
        
        sometable = database.customers.table;
        
        As you can see this is the simplest approach if you are accessing a
        single table with a known name.
        
        Each table has a number of properties (name, keys, relations and so on)
        but importantly it also has a record. This contains the individual
        fields that make up the data area of the table. For example;
        
        somename =
          database.customers.table.record.firstname;
        
        This can be shortened a bit to
        
        somename = database.customers.record.firstname;
        
        The record structure contains a single record in the table. This
        structure needs to be populated before any record is written to the
        table (using idbOne, idbWrite, idbAdd, idbPut,
          idbMarkDelete or idbDelete). 
        
        When reading a single record (idbGet) then
        the record is populated with the result of the read. If the record is
        not located then the record is unaltered.
        
        When reading a group of records (idbSelect),
        an a onRecord function is used, then the
        record structure is populated on each call to onRecord. If an onRecord
          function is not used, then the record structure is not primed,
        but all the records are instead sent to the resultset
          parameter.  For example;
        
        
        
        
        
        Calls to idbSelect which do not pass an onRecord function will have the result sent to
        the oncomplete function. You can then loop through the result set,
        inspecting each record as you go.
        
        You can iterate through the result set;
        
         var row=0;
          for (row in resultset){
              database.customers.record = resultset[row];
            // do something here
          }
        
        Two utility functions are also provided. These are mostly used for
        debugging purposes.
        
        database.tablename.view() 
        
        This sends the contents of the table to the browser debugging console as
        a pipe separated list. This allows you to see the number of records in
        the table, and also the contents of the table. You can use this function
        directly in the browser developer tools area. 
        
        database.tablename.empty()
        
        As the name suggests this empties the table in the browser. Again, this
        is mostly useful for debugging purposes. 
        
        This will drive you insane: Bear in
        mind that if you are sync'ing data with a server then the data in the
        server will be re-fetched on the next sync. So if you empty the table
        locally either empty it on the server as well, or be prepared for the
        data to re-appear. 
      nt-idb.js
       The reading and writing to the local data is done
        with a set of NetTalk JavaScript functions. These functions are located
        in scripts\nt-idb.js. If you wish to access the data directly from your
        own JavaScript code then you will want to make use of these functions,
        so they are documented here.