PageSourceSearch

https://syriaca.org/resources/keyboard/syr/keyboardSupport.js

js syriaca.org collected 2026-10-02 05:01:15 UTC 7,408 bytes, 248 lines download raw bytes

1/*
2 * Adds a virtual keyboard to a text input field. The virtual keyboard with these extenstions
3 * are known to work on Chrome, Firefox, and Safari running on Windows and iOS and on iOS and
4 * Android mobile devices. In mobile environments the native keyboard will be suppressed.
5 * 
6 * Parameters:
7 * 
8 * 	textBoxId - Id of the text input field to which the keyboard should be attached
9 * 
10 * 	layout - Layout that the keyboard should use
11 * 
12 * 	buttonId (optional) - Id of the DOM object to which a CLICK event handler will be attached 
13 * 		for poping up the keyboard. This DOM object will be hidden when the keyboard is visible to
14 * 		reduce interactions with the keyboard and causing subtle bugs. If this parameter is missing
15 * 		or "falsy" then the keyboard will popup when the text input field receives focus.
16 * 
17 * 	autocompleteUri (optional) - A URI that is compatible with the JQuery UI autocomplete widget.
18 * 		If this parameter is specified then a JQuery UI autocomplete widget will be attached to
19 * 		the text input field and will interact with the virtual keyboard. If the parameter is
20 * 		missing or "falsy" then a JQuery UI autocomplete widget will not be attached.
21 * 
22 *  selectionCallback (optional) - If a JQuery UI autocomplete widget was attached to the text input
23 *  	field then this callback, if specified, will be called when the user selects an item from
24 *  	the autocomplete dropdown list.
25 *  
26 *  changeCallback (optional) - If a JQuery UI autocomplete widget was attached to the text input
27 *  	field then this callback, if specified, will be called when the user changes selection in
28 *  	the autocomplete dropdown list.
29 *  
30 *  Examples:
31 * <link rel="stylesheet" href="./css/keyboard.css" type="text/css" media="all" />
32 * <link rel="stylesheet" href="./css/keyboard-previewkeyset.css" type="text/css" media="all" />
33 * 
34 * <script src='./js/jquery.cookie.js'></script>
35 * <script src='./js/jquery.keyboard.js'></script>
36 * <script src='./js/jquery.keyboard.extension-mobile.js'></script>
37 * <script src='./js/jquery.keyboard.extension-navigation.js'></script>
38 * <script src='./js/jquery.keyboard.extension-autocomplete.js'></script>
39 * <script src='./js/jquery.keyboard.syriac.js'></script>
40 * <script src='./js/keyboardSupport.js'></script>
41 * 
42 * // Add a keyboard with a button to popup the keyboard
43 * initializeKeyboard('#lexeme', getKeyboardLayout, '#lexeme-keyboard');
44 *  
45 * // Add a keyboard with a button to popup the keyboard with an autocomplete widget attached
46 * initializeKeyboard('#lexeme', getKeyboardLayout, '#lexeme-keyboard',  Routing.generate('listlexemes'), onSelection);
47 *  
48 * // Add a keyboard that will popup when the text input box gets focus
49 * initializeKeyboard('#lexeme', getKeyboardLayout);
50 *  
51 * // Add a keyboard that will popup when the text input box gets focus with an autocomplete widget attached
52 * initializeKeyboard('#lexeme', getKeyboardLayout, null, Routing.generate('listlexemes'), onSelection);
53 */
54function initializeKeyboard(textBoxId, layout, buttonId, autocompleteUri, selectionCallback, changeCallback) {
55	var mobile = (navigator.userAgent.toLowerCase().search(/ipad|iphone|android/i) > -1);
56	var popupButton = buttonId ? $(buttonId) : false;
57
58	function isFunction(functionToCheck) {
59		var getType = {};
60		return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
61	};
62
63	var fixDirection = function()
64	{
65		var textBox = $(textBoxId);
66		var value = textBox.val();
67		var matches = value.match(/[\u0700-\u074F]/g);
68
69		if (matches)
70		{
71			textBox.css('text-align', 'right');
72			textBox.css('direction', 'rtl');
73		}
74		else
75		{
76			textBox.css('text-align', 'left');
77			textBox.css('direction', 'ltr');
78		}
79	};
80
81	var keyboardButtonPressed = function(e)
82	{
83		var textBox = $(textBoxId);
84		var text = textBox.val();
85
86		fixDirection();
87
88		if (text && text.length > 1) {
89			textBox.autocomplete("search");
90		}
91	};
92
93	var renderListItem = function(ul, item) {
94		return $('<li></li>')
95			.data('item.autocomplete', item)
96			.append('<a>' + item.label + '</a>')
97			.appendTo(ul);
98	};
99
100	var attachAutoComplete = function () {
101		var textBox = $(textBoxId);
102
103		var options = { 
104			source: autocompleteUri, 
105			minLength: 2, 
106			delay: 500,
107			autoFocus: true
108		};
109
110		if (isFunction(selectionCallback)) options.select = selectionCallback;
111		if (isFunction(changeCallback)) options.change = changeCallback;
112
113		$(textBox).autocomplete(options).data("ui-autocomplete")._renderItem = renderListItem;
114	};
115
116	var getKeyboard = function(close) {
117		var textBox = $(textBoxId);
118		return (isFunction(textBox.getkeyboard) ? textBox.getkeyboard() : false);
119	};
120
121	var createKeyboard = function() {
122		var textBox = $(textBoxId);
123		var keyboardLayout = isFunction(layout) ? layout() : layout;
124		var options = {
125			stayOpen : false,
126			layout : keyboardLayout,
127			usePreview: false,
128			autoAccept: true,
129			autoAcceptOnEsc: true,
130			useWheel: false,
131			lockInput : mobile,
132			beforeVisible: function (e, keyboard, el) {
133	            keyboard.el.blur();
134	        },
135	        position: {
136            // null = attach to input/textarea;
137            // use $(sel) to attach elsewhere
138            of: null,
139            my: 'center top',
140            at: 'center top',
141            // used when "usePreview" is false
142            at2: 'center bottom'
143            }
144		};
145
146		if (popupButton) options.openOn = "";
147
148		textBox.keyboard(options);
149
150		if (autocompleteUri) {
151			attachAutoComplete();
152		
153			textBox.addAutocomplete({
154				position : {
155					of : textBox,
156					my : 'left top',
157					at : 'left bottom',
158					at2 : 'left bottom',
159					collision: 'none'
160				}
161			});
162		}
163
164		var kb = getKeyboard();
165
166		if (popupButton && kb) {
167			textBox.on('hidden', function(e){
168				var kb = getKeyboard();
169				if (kb) {
170					removeKeyboard(kb.options.autoAccept ? 'true' : false);
171					rebuildTextBox();
172					if (autocompleteUri) {
173						attachAutoComplete();
174					}
175				}
176				popupButton.prop('hidden', false);
177			});
178
179			popupButton.prop('hidden', true);
180			kb.reveal();
181		}
182
183		if (autocompleteUri) {
184			if (mobile) {
185				textBox.on('change', autocompleteUri ? keyboardButtonPressed : fixDirection);
186				$('button.ui-keyboard-button').on('touch', autocompleteUri ? keyboardButtonPressed : fixDirection);
187			} else {
188				textBox.on('change', autocompleteUri ? keyboardButtonPressed : fixDirection);
189				$('button.ui-keyboard-button').on('click', autocompleteUri ? keyboardButtonPressed : fixDirection);
190			}
191		}
192	};
193
194	var removeKeyboard = function(close) {
195		var kb = getKeyboard();
196
197		if (kb) {
198			kb.close(close);
199			kb.destroy();
200		}
201
202		if (popupButton) popupButton.prop('hidden', false);
203	};
204	
205	var rebuildTextBox = function() {
206		var textBox = $(textBoxId);
207		var t = textBox.clone(false);
208
209		t.insertAfter(textBox);
210		textBox.remove();
211	};
212	
213	$.keyboard.keyaction.enter = function() {
214		var kb = getKeyboard();
215
216		if (kb) {
217			removeKeyboard(kb.options.autoAccept ? 'true' : false);
218			rebuildTextBox();
219			if (autocompleteUri) {
220				attachAutoComplete();
221			}
222		}
223
224		return false;
225	};
226
227	var clickButton = function(e){
228		var kb = getKeyboard();
229		var isVisible = kb ? kb.isVisible() : false;
230		
231		if (isVisible) {
232			removeKeyboard();
233		}
234
235		rebuildTextBox();
236		createKeyboard();
237	};
238
239	if (popupButton) {
240		popupButton.click(clickButton);
241
242		if (autocompleteUri) {
243			attachAutoComplete();
244		}
245	} else {
246		createKeyboard();
247	}
248}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.