lrhorer
Posts: 18
Joined: Sun Feb 22, 2015 6:35 pm

javascript not working on RPi 3

Tue Nov 06, 2018 10:44 pm

I have a Raspberry Pi 3 running java 1.8.0_65, apache 2.4.25, and chromium-browser 60.0.3112.89 under Raspbian 9.4, and I am having some trouble with some javascript running from a CGI script. I have never in my life worked with javascript, and I am no expert with HTML or CGI, so am pretty lost, here.

I can bring up the following URL:

Code: Select all

http://javidan.github.io/jkeyboard/examples/index.htm
by running the following command from a script running as root in /usr/lib/cgi-bin:

Code: Select all

sudo -u pi chromium-browser --start-fullscreen http://javidan.github.io/jkeyboard/examples/index.htm
Everything seems to work just fine. When I clone the github, copy the above example file to /usr/lib/cgi-bin/lib/src, rename it to Winput.html, and run the following script, all that happens is the screen pops up with a text input form. No keyboard.

Code: Select all

#! /bin/bash
Dir=/usr/lib/cgi-bin/lib/src
cd $Dir
echo 'Content-type: text/html'
echo ""
cat $Dir/Winput.html
I don't spot any errors from the output of the script:

Code: Select all

Content-type: text/html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Keyboard Example</title>
    <link rel="stylesheet" href="/usr/lib/cgi-bin/lib/css/jkeyboard.css">
    <style>
        body{
            background: #144766
        }
        #search_field{
            display: block;
            margin: 0 auto;
            padding: 5px 10px;
            font-size: 28px; 
            width: 50%;
        }
            
    </style>
</head>
<body>
    
    <input type="text" id="search_field">
    

<div id="keyboard">
<ul class="jkeyboard">
        <li class="jline">
                <ul>
                        <li class="jkey letter">q</li>
                        <li class="jkey letter">w</li>
                        <li class="jkey letter">e</li>
                        <li class="jkey letter">r</li>
                        <li class="jkey letter">t</li>
                        <li class="jkey letter">y</li>
                        <li class="jkey letter">u</li>
                        <li class="jkey letter">i</li>
                        <li class="jkey letter">o</li>
                        <li class="jkey letter">p</li>
                </ul>
        </li>
        <li class="jline">
                <ul>
                        <li class="jkey letter">a</li>
                        <li class="jkey letter">s</li>
                        <li class="jkey letter">d</li>
                        <li class="jkey letter">f</li>
                        <li class="jkey letter">g</li>
                        <li class="jkey letter">h</li>
                        <li class="jkey letter">j</li>
                        <li class="jkey letter">k</li>
                        <li class="jkey letter">l</li>
                </ul>
        </li>
        <li class="jline">
                <ul>
                        <li class="jkey shift">&nbsp;</li>
                        <li class="jkey letter">z</li>
                        <li class="jkey letter">x</li>
                        <li class="jkey letter">c</li>
                        <li class="jkey letter">v</li>
                        <li class="jkey letter">b</li>
                        <li class="jkey letter">n</li>
                        <li class="jkey letter">m</li>
                        <li class="jkey backspace">&nbsp;</li>
                </ul>
        </li>
        <li class="jline">
                <ul>
                        <li class="jkey numeric_switch">123</li>
                        <li class="jkey layout_switch">&nbsp;</li>
                        <li class="jkey space">&nbsp;</li>
                        <li class="jkey return">Enter</li>
                </ul>
        </li>
</ul>
</div>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="/usr/lib/cgi-bin/lib/src/jkeyboard.js"></script>
<script>
        $('#keyboard').jkeyboard({
        layout: "english",
        input: $('#search_field')
        });
</script>
</body>
</html>
There are no errors in apache2 or syslog, and only what look to be ordinary reports in auth.log, but there is an error reported by chromium when I run either the local script or the web URL:

Code: Select all

[9236:9236:1106/153237.208994:ERROR:sandbox_linux.cc(344)] InitializeSandbox() called with multiple threads in process gpu-process.
I can run the code externally, and when I run the inspector on FireFox, I get:

Code: Select all

Loading failed for the <script> with source “http://192.168.1.26/usr/lib/cgi-bin/lib/src/jkeyboard.js”. Winput.cgi:79
[Show/hide message details.] TypeError: $(...).jkeyboard is not a function[Learn More]
This is jkeyboard.js:

Code: Select all

Thermostat-Leslie:/usr/lib/cgi-bin/lib/src# cat jkeyboard.js 

// the semi-colon before function invocation is a safety net against concatenated
// scripts and/or other plugins which may not be closed properly.
; (function ($, window, document, undefined) {

    // undefined is used here as the undefined global variable in ECMAScript 3 is
    // mutable (ie. it can be changed by someone else). undefined isn't really being
    // passed in so we can ensure the value of it is truly undefined. In ES5, undefined
    // can no longer be modified.

    // window and document are passed through as local variable rather than global
    // as this (slightly) quickens the resolution process and can be more efficiently
    // minified (especially when both are regularly referenced in your plugin).

    // Create the defaults once
    var pluginName = "jkeyboard",
        defaults = {
            layout: "english",
            input: $('#input'),
            customLayouts: {
                selectable: []
            }
        };


    var function_keys = {
        backspace: {
            text: '&nbsp;',
        },
        return: {
            text: 'Enter'
        },
        shift: {
            text: '&nbsp;'
        },
        space: {
            text: '&nbsp;'
        },
        numeric_switch: {
            text: '123',
            command: function () {
                this.createKeyboard('numeric');
                this.events();
            }
        },
        layout_switch: {
            text: '&nbsp;',
            command: function () {
                var l = this.toggleLayout();
                this.createKeyboard(l);
                this.events();
            }
        },
        character_switch: {
            text: 'ABC',
            command: function () {
                this.createKeyboard(layout);
                this.events();
            }
        },
        symbol_switch: {
            text: '#+=',
            command: function () {
                this.createKeyboard('symbolic');
                this.events();
            }
        }
    };


    var layouts = {
        selectable: ['azeri', 'english', 'russian'],
        azeri: [
            ['q', 'ü', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'ö', 'ğ'],
            ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ı', 'ə'],
            ['shift', 'z', 'x', 'c', 'v', 'b', 'n', 'm', 'ç', 'ş', 'backspace'],
            ['numeric_switch', 'layout_switch', 'space', 'return']
        ],
        english: [
            ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',],
            ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l',],
            ['shift', 'z', 'x', 'c', 'v', 'b', 'n', 'm', 'backspace'],
            ['numeric_switch', 'layout_switch', 'space', 'return']
        ],
        russian: [
            ['й', 'ц', 'у', 'к', 'е', 'н', 'г', 'ш', 'щ', 'з', 'х'],
            ['ф', 'ы', 'в', 'а', 'п', 'р', 'о', 'л', 'д', 'ж', 'э'],
            ['shift', 'я', 'ч', 'с', 'м', 'и', 'т', 'ь', 'б', 'ю', 'backspace'],
            ['numeric_switch', 'layout_switch', 'space', 'return']
        ],
        numeric: [
            ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
            ['-', '/', ':', ';', '(', ')', '$', '&', '@', '"'],
            ['symbol_switch', '.', ',', '?', '!', "'", 'backspace'],
            ['character_switch', 'layout_switch', 'space', 'return'],
        ],
        numbers_only: [
            ['1', '2', '3',],
            ['4', '5', '6',],
            ['7', '8', '9',],
            ['0', 'return', 'backspace'],
        ],
        symbolic: [
            ['[', ']', '{', '}', '#', '%', '^', '*', '+', '='],
            ['_', '\\', '|', '~', '<', '>'],
            ['numeric_switch', '.', ',', '?', '!', "'", 'backspace'],
            ['character_switch', 'layout_switch', 'space', 'return'],

        ]
    }

    var shift = false, capslock = false, layout = 'english', layout_id = 0;

    // The actual plugin constructor
    function Plugin(element, options) {
        this.element = element;
        // jQuery has an extend method which merges the contents of two or
        // more objects, storing the result in the first object. The first object
        // is generally empty as we don't want to alter the default options for
        // future instances of the plugin
        this.settings = $.extend({}, defaults, options);
        // Extend & Merge the cusom layouts
        layouts = $.extend(true, {}, this.settings.customLayouts, layouts);
        if (Array.isArray(this.settings.customLayouts.selectable)) {
            $.merge(layouts.selectable, this.settings.customLayouts.selectable);
        }
        this._defaults = defaults;
        this._name = pluginName;
        this.init();
    }

    Plugin.prototype = {
        init: function () {
            layout = this.settings.layout;
            this.createKeyboard(layout);
            this.events();
        },

        createKeyboard: function (layout) {
            shift = false;
            capslock = false;

            var keyboard_container = $('<ul/>').addClass('jkeyboard'),
                me = this;

            layouts[layout].forEach(function (line, index) {
                var line_container = $('<li/>').addClass('jline');
                line_container.append(me.createLine(line));
                keyboard_container.append(line_container);
            });

            $(this.element).html('').append(keyboard_container);
        },

        createLine: function (line) {
            var line_container = $('<ul/>');

            line.forEach(function (key, index) {
                var key_container = $('<li/>').addClass('jkey').data('command', key);

                if (function_keys[key]) {
                    key_container.addClass(key).html(function_keys[key].text);
                }
                else {
                    key_container.addClass('letter').html(key);
                }

                line_container.append(key_container);
            })

            return line_container;
        },

        events: function () {
            var letters = $(this.element).find('.letter'),
                shift_key = $(this.element).find('.shift'),
                space_key = $(this.element).find('.space'),
                backspace_key = $(this.element).find('.backspace'),
                return_key = $(this.element).find('.return'),

                me = this,
                fkeys = Object.keys(function_keys).map(function (k) {
                    return '.' + k;
                }).join(',');

            letters.on('click', function () {
                me.type((shift || capslock) ? $(this).text().toUpperCase() : $(this).text());
            });

            space_key.on('click', function () {
                me.type(' ');
            });

            return_key.on('click', function () {
                me.type("\n");
                me.settings.input.parents('form').submit();
            });

            backspace_key.on('click', function () {
                me.backspace();
            });

            shift_key.on('click', function () {
                if (capslock) {
                    me.toggleShiftOff();
                    capslock = false;
                } else {
                    me.toggleShiftOn();
                }
            }).on('dblclick', function () {
                capslock = true;
            });


            $(fkeys).on('click', function () {
                var command = function_keys[$(this).data('command')].command;
                if (!command) return;

                command.call(me);
            });
        },

        type: function (key) {
            var input = this.settings.input,
                val = input.val(),
                input_node = input.get(0),
                start = input_node.selectionStart,
                end = input_node.selectionEnd;

            var max_length = $(input).attr("maxlength");
            if (start == end && end == val.length) {
                if (!max_length || val.length < max_length) {
                    input.val(val + key);
                }
            } else {
                var new_string = this.insertToString(start, end, val, key);
                input.val(new_string);
                start++;
                end = start;
                input_node.setSelectionRange(start, end);
            }

            input.trigger('focus');

            if (shift && !capslock) {
                this.toggleShiftOff();
            }
        },

        backspace: function () {
            var input = this.settings.input,
                val = input.val();

            input.val(val.substr(0, val.length - 1));
        },

        toggleShiftOn: function () {
            var letters = $(this.element).find('.letter'),
                shift_key = $(this.element).find('.shift');

            letters.addClass('uppercase');
            shift_key.addClass('active')
            shift = true;
        },

        toggleShiftOff: function () {
            var letters = $(this.element).find('.letter'),
                shift_key = $(this.element).find('.shift');

            letters.removeClass('uppercase');
            shift_key.removeClass('active');
            shift = false;
        },

        toggleLayout: function () {
            layout_id = layout_id || 0;
            var plain_layouts = layouts.selectable;
            layout_id++;

            var current_id = layout_id % plain_layouts.length;
            return plain_layouts[current_id];
        },

        insertToString: function (start, end, string, insert_string) {
            return string.substring(0, start) + insert_string + string.substring(end, string.length);
        }
    };

    // A really lightweight plugin wrapper around the constructor,
    // preventing against multiple instantiations
    $.fn[pluginName] = function (options) {
        return this.each(function () {
            if (!$.data(this, "plugin_" + pluginName)) {
                $.data(this, "plugin_" + pluginName, new Plugin(this, options));
            }
        });
    };

})(jQuery, window, document);

bzt
Posts: 174
Joined: Sat Oct 14, 2017 9:57 pm

Re: javascript not working on RPi 3

Sat Nov 10, 2018 11:35 am

Hi,

since you got no responses at all, I'll try to answer.

First of all, your problem is not Raspberry related. Second, as you have admitted, you have serious shortage of required knowledge. I'll try to explain a few thing to shed some light on the topic.

Server client architecture
The web is using the so called server client architecture, in which client is a webbrowser, and server is webserver. They are usually not on the same machine (although you can put them on the same Raspberry as you did). They supposed to communicate over the network: the client (chromium/Firefox/opera etc.) sends a query with an URL to the server (apache/nginx/lighttpd etc.) using the HTTP protocol. The server accepts that query, examines the URL and decides what to do with it. If a file exists on the server's local filesystem, it sends that file as a response. If not, it can call a script to generate the response (CGI). If neither can be done, it sends a HTTP 404 File Not Found response. The response most often is a HTML, which is a descriptive language that tells the browser how to display things (what text to display, where to put images etc.). CSS is another descriptive language, called stylesheets, which is a collection of styles to be used when displaying a HTML (font face and font size of the text, image maximum size and alignment etc.). Note this is really just an introductory description in a nutshell.

Java has nothing to do with JavaScript.
Java is a compiled bytecode language, running in a virtual machine (jvm). You can run java bytecode on the server side and on the client side as well (the former called java application, the latter called java-applet and run by the jvm built into your browser, not by the java installed on the server. Lately that technology is abadoned in favour of HTML5 and JavaScript, but server side java is still used in many places, mostly by big companies and banks). Unlike java, JavaScript is an interpreted script language, and has no well-defined bytecode representation at all, and you don't compile it either.

JavaScript is not running as CGI
CGI means the script is called by the webserver, therefore interpreted on the server side. Because JavaScript is interpreted on client side in the browser, there's no such thing running JavaScript as a CGI (let's put node.js aside as you're clearly not using that framework). You are using shell script as CGI btw, interpreted by "/bin/bash".

You won't see JavaScript errors in apache logs
As mentioned before, JS is running in your browser, so there's no point in looking for logs on the server. As you have figured out, JS logs can be found in the browser, on the interface called Console. Both chromium and Firefox can run your JS code, and they both have that Console under their Inspector.

About the chromium message
That's perfectly normal when an instance is already running. Don't care about it.

About the JS Console message
As the Firefox message says, your JS does not define the jkeyboard function. And indeed, if you search for the string "function jkeyboard" in your js file, you'll find none.

About your jkeyboard.js file
It's a bit misleading, but what you have here is not JavaScript at all. It's a jQuery plugin. As such, it requires the jQuery framework in order to work. The jQuery framework is written in JavaScript, which can be interpreted by the browser. To make it a bit clearer, your js file is referencing the '$' object. There's no such thing in JavaScript, that's defined by jQuery. Without the jQuery framework, your browser does not know what to do with it.

Again, your problem is not Raspberry Pi related, so I'd suggest to visit jQuery related forums, there's a good chance you'll find your answer there.

Hope this helps a bit,
bzt

Heater
Posts: 9957
Joined: Tue Jul 17, 2012 3:02 pm

Re: javascript not working on RPi 3

Sun Nov 11, 2018 7:23 am

lrhorer,
When I clone the github, copy the above example file to /usr/lib/cgi-bin/lib/src, rename it to Winput...
I really don't think you should be putting example.html into any cgi bin or lib directory. example.html is just a plain old HTML file, it does not contain any PHP or whatever that is to be run by the webserver as CGI. example.html could as well be served from /var/www/ like any other static HTML page.
..., and run the following script, all that happens is the screen pops up with a text input form. No keyboard.
Well yes. Because the example.html requires to load two pieces of javascript as shown by the script tags it contains:

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="../lib/js/jkeyboard.js"></script>


The jquery-1.10.1.min.js is being loaded from code.jquery.com so be sure the browser you are viewing with can reach the internet.

No doubt it is not finding jkeyboard.js because you have not moved that file to any place the web server can serve it.

Be sure to put jkeyboard.js into a proper place on your server and change the script tag in example.html to point to it.

Which is what the Firefox error message is telling you:

Loading failed for the <script> with source “http://192.168.1.26/usr/lib/cgi-bin/lib ... eyboard.js”. Winput.cgi:79


Means your server does not have jkeyboard.js at the given URL /usr/lib/cgi-bin/lib/src/. Personally I don't think .js files should be in that cgi directory either. Put them somewhere under your /var/www and change that URL in example.html

[Show/hide message details.] TypeError: $(...).jkeyboard is not a function[Learn More]

Means it cannot find the function jkeyboard. Of cours enot because that function is in jkeyboard.js which failed to load.

lrhorer
Posts: 18
Joined: Sun Feb 22, 2015 6:35 pm

Re: javascript not working on RPi 3

Mon Nov 12, 2018 5:54 pm

First of all, your problem is not Raspberry related.
That is an assumption, and making unnecessary and especially undocumented assumptions is a bad idea when troubleshooting. I admit it doesn't seem most likely to be an issue related to the RPi, or more properly Raspbian, but I have documented significantly different behavior of Apache 2.4.25 under Raspbian Stretch than Apache 2.4.10 under Debian Jessie. The point is, virtually the exact same code running on my RPi behaves very differently than the same code - verified by inspector - running on his server.
Server client architecture
I did not say I was new at computing. I have been doing this for more than 40 years. I've written a few servers and more than a few clients. I am just not an expert at HTML or CGI. Not being an expert does not mean I am not familiar with the concepts.
Java has nothing to do with JavaScript.
So I have been informed. As I admitted, I have never worked with either. Not knowing the verbs, syntax, or punctuation rules of javascript, troubleshooting it by myself is a significant undertaking.
JavaScript is not running as CGI
Yeah, OK. Put it however you like. The point is, one difference, significant or not, between the example web server and the same code being served by my web server is the fact mine is being produced by a CGI script rather than being directly read by my web server. Although once again I find it unlikely the addition of

Code: Select all

Content-type: text/html
(blank line)
to the HTML page would make a difference to the jquery call, but it is possible. Regardless, it is a data point, and someone more expert than I may consider it significant. Thus, I needed to report it.
You won't see JavaScript errors in apache logs
Of course not. The same goes for syslog. Reporting the fact there are no errors more or less eliminates Apache and CGI script errors as the culprit. That is an important data point, and the first thing I would ask of anyone reporting trouble. Any such errors would need to be fixed first before moving on.
About the chromium message
That's perfectly normal when an instance is already running. Don't care about it.
Except that an instance is not running. This is what I get if an instance is running:

Code: Select all

Thermostat-Leslie:/usr/lib/cgi-bin# Created new window in existing browser session.
Created new window in existing browser session.
with no error at all. Once again, it seems very unlikely this is related to the problem at hand, since it is reported whether the code is served from his server or mine, but until we know what the problem actually is, we can't say with any authority, and it needs to be reported with the incident.
About the JS Console message
As the Firefox message says, your JS does not define the jkeyboard function. And indeed, if you search for the string "function jkeyboard" in your js file, you'll find none.
Granted, but then neither does the JS script on his server. More to the point, they are *EXACTLY* the same script. I did not do anything to the script other than clone the entire repository to my RPi. Furthermore, the entire script topology was transferred to the RPi by the cloning. If there were any problems with file permissions or locations, then it would be reported by Apache and / or by running the CGI script from the command line. So why do both Chrome and FireFox on two different systems complain about the exact same script, with the exception of the header required by the CGI interpreter, served from my RPi but not his server?
It's a bit misleading, but what you have here is not JavaScript at all. It's a jQuery plugin. As such, it requires the jQuery framework in order to work. The jQuery framework is written in JavaScript, which can be interpreted by the browser. To make it a bit clearer, your js file is referencing the '$' object. There's no such thing in JavaScript, that's defined by jQuery. Without the jQuery framework, your browser does not know what to do with it.
Isn't that provided by jquery-1.10.1.min.js? I have tried both a local copy and one served from code.jquery.com, as his script does. Either way, the jquery-1.10.1.min.js code loads without error.
Again, your problem is not Raspberry Pi related, so I'd suggest to visit jQuery related forums
OK, I found one. I will report there.

lrhorer
Posts: 18
Joined: Sun Feb 22, 2015 6:35 pm

Re: javascript not working on RPi 3

Mon Nov 12, 2018 7:18 pm

This does not appear to be a JQuery error nor a problem with the jkeyboard plugin. Note the issue is with the script not loading, not with the syntax of the script. Actually, I am not sure why Apache is not spawning an error, but from the HTTP stream:

Code: Select all

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /keyboard/lib/css/jkeyboard.css was not found on this server.</p>
<hr>
<address>Apache/2.4.25 (Raspbian) Server at 192.168.1.26 Port 80</address>
</body></html>
and

Code: Select all

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /keyboard/lib/src/jkeyboard.js was not found on this server.</p>
<hr>
<address>Apache/2.4.25 (Raspbian) Server at 192.168.1.26 Port 80</address>
</body></html>
Note I moved the repository from /usr/lib/cgi-bin/ to /keyboard/ to make sure it wasn't a permissions issue with /usr/lib/cgi-bin.

lrhorer
Posts: 18
Joined: Sun Feb 22, 2015 6:35 pm

Re: javascript not working on RPi 3

Mon Nov 12, 2018 7:49 pm

OK, I fixed it. Depending on exactly how I specified the URLs, Apache either thought the JS files were scripts executable on the server, rather than the browser, or else they were in a location Apache refused to read. I moved everything not CGI related to /var/www/html/lib, and now it works.

lrhorer
Posts: 18
Joined: Sun Feb 22, 2015 6:35 pm

Re: javascript not working on RPi 3

Mon Nov 12, 2018 7:52 pm

What I still don't quite understand is why Apache does not log 404 errors. Had it done so, I would have figured this out much earlier.

Return to “Other programming languages”