JQuery plugin not working on RPi 3

JQuery plugin not working on RPi 3

I *THINK* this may be a JQuery plugin issue, but I am not certain.  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 served from a CGI script. I have never in my life worked with javascript, and I am no expert with HTML or CGI, so am a little lost, here.

I can bring up the following URL:

http://javidan.github.io/jkeyboard/examples/index.htm

which contains the references:

<link rel="stylesheet" href="/usr/lib/cgi-bin/lib/css/jkeyboard.css">
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="/usr/lib/cgi-bin/lib/src/jkeyboard.js"></script>

I can run the following command from a script running as root in /usr/lib/cgi-bin:

sudo -u pi chromium-browser --start-fullscreen http://javidan.github.io/jkeyboard/examples/index.htm

and 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 and the key definitions running down the left side of the screen. No keyboard.

#! /bin/bash Dir=/usr/lib/cgi-bin/lib/src cd $Dir echo 'Content-type: text/html' echo "" cat $Dir/Winput.html

This is Winput.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>


When I try to serve the Winput.cgi script from the Raspberry Pi, I get no errors from apache2 or syslog, but I do get an error in the browser:

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

I have tried this is both a local copy of jquery-1.10.1.min.js and one served from code.jquery.com with no difference. This does seem to me to be an issue with the plugin, but I have no idea what. It is identical to the plugin on the example page.

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);