7

Creative community and javascript

Hello everyone,

For more than a year I have been exploring the Ninox code to better understand how it works. Initially driven by the lack of documentation and the frustration of some limitations, I soon realised that Ninox is based on some very clever and efficient code.

After some time, I managed to get some JavaScript to run from NinoxScript by hooked the Eval function. I created a lot of functions that didn't exist before and had a lot of fun testing and understanding Ninox. I notice now that most of the functions I have created can be done in Ninox without using JS 🤭.

Today, I'm sharing some of this code with you in order to create a creative community that experiments with new features for Ninox (see badges on the files and comments tabs). The purpose of this is to let creativity and emulation express itself and that we can enrich each other with our discoveries.

WARNING: the code I share with you here is to be used exclusively for personal use. I do not recommend using it for commercial purposes.

Here, you have to put this HTML code in a formula on a page that is displayed first in your application. Once the code is executed, you can use Ninox's Eval function extensively. I have put two examples of extended functions here, but you can create your own later.

The first one allows you to execute some JavaScript code and get the return value. The second returns the list of comments for a record.

The code is commented out so that you can start to understand what is being implemented.

html("
<script>
    debugger;
    //get the address of the Eval function of NinoxScript
    var evalFunctor = queries.getFunctor('eval', [{ sign: function () { return 'string' } }, { sign: function () { return 'nid' } }]);

    //get the execution context of the NinoxScripts
    var Ctx = new queries.JSRuntimeContext();

    //setting up the hook function
    if (evalFunctor && Ctx /* && !evalFunctor.oldFunction*/) {
        //save the old eval function, the one with the parameters eval(string,nid)
        if (evalFunctor.argTypes[0] == 'string') {
            evalFunctor.oldFunction = Ctx.F[evalFunctor.functorId];
        }


        //initialization of new parameter types: eval(any,any)
        evalFunctor.argTypes[0] = 'any';
        evalFunctor.argTypes[1] = 'any';
        evalFunctor.sign = 'eval(any,any)';

        //creation of the new extended function table
        evalFunctor.exFunctions = {};

        //add extended functions to the array
        evalFunctor.exFunctions['exEvalJS'] = exEvalJS;
        evalFunctor.exFunctions['exComments'] = exComments;

        /*
            Implementation of the new function eval in the Ninox functions array.
            Now the eval function can be called in two ways.
            1 - In the case of a call to the original eval function:

                fnt: Ninox code to be evaluated.
                params: Current record (this) which will be used as a context to evaluate the fnt code.

            2 - In the case of a call to the extended functions:
                fnt: string that contains either the name of the extended function.
                params : object which contains the parameters of the extended functions call.

            in both cases :
                db : database object.
                ret : function to call at the end of the function. The functions are called by stacking. When the first one is finished, it calls the next one.

            */

        evalFunctor.hook = function (fnt, params, db, ret) {

            try {
                //search if the first parameter of the eval function strictly contains the name of an extended function
                if (evalFunctor.exFunctions[fnt]) {
                    //if this is the case, the corresponding function is called
                    evalFunctor.exFunctions[fnt](fnt, params, db, ret);
                } else {
                    //If not, the Ninox function is called
                    evalFunctor.oldFunction(fnt, params, db, ret);
                }
            } catch (err) {
                //in case of an error, the error message is returned through the 'ret' return function
                ret(String(err.message));
            }
        };

        //recording the hooked function in the NinoxScript function array
        Ctx.F[evalFunctor.functorId] = evalFunctor.hook;
    }

    //returns the record id from its number or from the record object
    function getId(record) {
        if (typeof record === 'string') return record;
        else if (typeof record === 'object') return record._id;
        else return null;
    }

    /*
    extended function that allows the execution of JavaScript code
    eval('exEvalJS', {javascript:, arguments:{param1:, param2:...}})
        javascript : string that contains the code to be executed
        arguments : {
                    param1,
                    param2… : parameters passed to the code as arguments to a function
                    }

    return : result of JavaScript fonction.

    exemple :eval('exEvalJS', {
                                javascript: 'return a + b;'',
                                arguments : {
                                            a: 10,
                                            b: 20
                                            }
                            });
    result -> 30;

    important: for an asynchronous function, call cb('return value') rather than return 'return value'

        exemple : promise.then( (value) => {
                                            cb(value);
                                            } ).then();

    */
    function exEvalJS(fnt, params, db, cb) {

        try {
            debugger;
            var { javascript, arguments } = params;
            var head = `var {${Object.keys(arguments).join(',')}} = args;`;
            var all = head + '\n' + javascript;
            var fn = Function('args', 'cb', all,);
            try {
                var Result = fn(arguments, cb);
                //check if function use CallBack to return result asynchronously
                if (javascript.search(/\b(cb)\b/) < 0)
                    return cb(Result);
            } catch (err) {
                var msgErr =
                    err.message + ' à la ligne ' + err.line - 2 + ', colonne ' + err.column;
                return cb(err)
            }
        } catch (err) {
            var msgErr =
                err.message + 'exEvalJS : à la ligne ' + err.line + ', colonne ' + err.column;

            cb(msgErr);
        }
    }

    /*
    getting the list of comments of a record
    eval('exComments', {id:} )
    return array of JSON with :
        comment : string of comment,
        userId : id of user post the comment,
        date : date of post in milli seconde.

    exemple : eval('exComments', {id:this.id});
    result -> {comment:... ,userId:..., date:167533002 }
    */
    function exComments(fnt, params, db, cb) {

        database.loadComments(getId(params.id), function (error, commentsArray) {
            if (error)
                cb('exComments : ' + error);
            else {
                var commentsList = [];
                for (num in commentsArray) {
                    var o = {
                        comment: commentsArray[num][2],
                        userId: commentsArray[num][1],
                        date: new Date(commentsArray[num][0]),
                    };
                    commentsList.push(o);
                }

                cb(commentsList);
            }
        });
    }
</script>
")

 

Copy

 

On Ninox, put this formula :

eval("exEvalJS", {
    javascript: "return a + b;",
    arguments: {
        a: 10,
        b: 20
    }
})

 

Copy

 

result -> 30

Be creative and have fun ! 😁

7replies Oldest first
  • Oldest first
  • Newest first
  • Active threads
  • Popular
  • Wow Jacques - thats brilliant stuff and if i may say so - very dedicated to the course!

    Like
  • Following Uwe's request, I have created a database with some examples to use the EvalJS function which allows you to run javascript directly from Ninox. You can download it on GitHub at this address: https://github.com/JacquesTur/Ninext/blob/2f18b797803124825718157098fec7968466a296/demos/EvalJS/EvalJS.ninox

    Among the examples that may be useful to you, there is :
    - Ask value, which uses the prompt function to ask the user for a value,
    - copy, to copy a text or the content of a field to the clipboard.

    You can find inspiration on these two sites:
    https://www.w3schools.com or https://developer.mozilla.org (multi language).
    Have fun creating new features and share them with us 😁.

    Like
      • UweG
      • UweG
      • 4 mths ago
      • Reported - view

      Jacques TUR Thank you Jaques
      A new playground opens up in Ninox

      Like 1
    • Gianluca
    • Gianluca
    • 4 mths ago
    • Reported - view

    Jacques TUR @jacques I don't even dare to try to understand your scripts (😄) but I sure appreciate all the functionalities they add to Ninox! Congratulations!

    Like
  • Following Alan Cooke  request, I made this JavaScript code which allows you to print several records. It is the equivalent of printRecord.

    And here is how to insert the JavaScript code into a global function to make it easier to use.

    function printRecords(arguments : any) do
        eval("exEvalJS", {
            javascript: "reports.openDesigner({ nid: id, nids: records, reportName: report, printAllAndClose: true })",
            arguments: arguments
        })
    end;
    
    

    Then just call the function like this:

    printRecords({
            id: string(this.ID),
            records: select Customer,
            report: "exPrintRecords"
        })
    

    the parameters are as follows: 

    • id: id of the current record,  
    • records: list of records to be printed,
    • report: name of the report to be printed.
    Like
    • Günther
    • Gunther.1
    • 4 wk ago
    • Reported - view

    Hi, with the new way of Syntax -checking it's not possible anymore to create new code.

    • Günther 

      On the web, everything seems to work fine. I don't have any problems. The syntax check is modified by the initialization code.
      If it doesn't work, it's probably because the initialization code wasn't executed.

      Furthermore, the this.Nr parameter might not be recognised in the context of a JSON variable. Indeed, this code is validated by the syntax analysis, but the variable "this" could not be recognised at runtime. It would be better to use an intermediate variable.

      Like
Like7 Follow
  • 7 Likes
  • 4 wk agoLast active
  • 7Replies
  • 280Views
  • 9 Following