From 749caec760c6ff4ed6e4f6d4f38ffa770e03493a Mon Sep 17 00:00:00 2001 From: "Constante \"Tino\" Gonzalez" Date: Mon, 12 Jun 2023 14:14:15 -0400 Subject: [PATCH 01/13] virtual_keyboard extension add to setup.py, add extension to adhoc_browser_pool.py --- lg_common/setup.py | 27 - lg_common/src/lg_common/adhoc_browser_pool.py | 577 ------------------ 2 files changed, 604 deletions(-) diff --git a/lg_common/setup.py b/lg_common/setup.py index 5be70540a..e69de29bb 100644 --- a/lg_common/setup.py +++ b/lg_common/setup.py @@ -1,27 +0,0 @@ -#!/usr/bin/env python3 - -from distutils.core import setup -from catkin_pkg.python_setup import generate_distutils_setup - -d = generate_distutils_setup( - packages=['lg_common'], - package_dir={'': 'src'}, - package_data={ - 'lg_common': [ - 'extensions/ros_window_ready/*', - 'extensions/monitor_page_urls/*', - 'extensions/current_url/*', - 'extensions/minimize_adhoc_browser/*', - 'extensions/ros_window_ready/*/*', - 'extensions/monitor_page_urls/*/*', - 'extensions/current_url/*/*', - 'extensions/minimize_adhoc_browser/*/*' - ] - }, - scripts=['bin/lg-code-to-command'], - requires=[] -) - -setup(**d) - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/lg_common/src/lg_common/adhoc_browser_pool.py b/lg_common/src/lg_common/adhoc_browser_pool.py index 3bb305cde..e69de29bb 100644 --- a/lg_common/src/lg_common/adhoc_browser_pool.py +++ b/lg_common/src/lg_common/adhoc_browser_pool.py @@ -1,577 +0,0 @@ -import rospy -import threading -import json -import glob -import lg_common -import os -import time - -from lg_common import ManagedAdhocBrowser, ManagedWindow -from lg_msg_defs.msg import ApplicationState -from lg_msg_defs.msg import WindowGeometry -from lg_msg_defs.msg import BrowserExtension -from lg_msg_defs.msg import AdhocBrowser, AdhocBrowsers, BrowserURL -from lg_common.helpers import get_app_instances_ids -from lg_msg_defs.srv import BrowserPool -from .managed_browser import DEFAULT_BINARY -from urllib.parse import urlparse, parse_qs, urlunparse -from urllib.parse import urlencode - -from lg_common.logger import get_logger -logger = get_logger('adhoc_browser_pool') - - -class AdhocBrowserPool(): - """ - Handles browser pool on a biewport - - self.browsers ivar: dict of browser_pool_id and ManagedAdhocBrowser() - - Smooth transitions functionality overview: - - handle_ros_message creates new browsers - - wait for ready signal (see rediness.py) - - when new browsers ready, hide and destroy - old browsers and show new - - """ - - def __init__( - self, - viewport_name, - extensions_root, - hide_delay, - destroy_delay, - ): - """ - AdhocBrowserPool manages a pool of browsers on one viewport. - self.browsers ivar keeps a dict of (id: ManagedAdhocBrowser) per viewport. - """ - - self.browsers = {} - self.browsers_info = {} - self.hide_delay = hide_delay - self.destroy_delay = destroy_delay - - # FIXME: Check that extensions_root ends with '/' - self.extensions_root = extensions_root - self.lg_common_internal_extensions_root = self._get_lg_common_extensions_root() - self.lock = threading.Lock() - self.viewport_name = viewport_name - # FIXME: make this class ros - offline: move ros dependancies to script - self._init_service() - self.log_level = rospy.get_param('/logging/level', 0) - self.rosbridge_port = rospy.get_param('~rosbridge_port', 9090) - self.rosbridge_secure = rospy.get_param('~rosbridge_secure', False) - - def _get_lg_common_extensions_root(self): - """ - Gets path to a directory with extensions for lg_common - e.g. '/opt/ros/$ROS_DISTRO/lib/python2.7/dist-packages/lg_common' for - deb distributed extensions or '/home/lg/catkin_ws/src/lg_common/src/lg_common' - for lg_common built with catkin. - - It returns a first matched dir under which an `extensions` dir exists - """ - logger.info("Going o to check %s dirs looking for extensions" % lg_common.__path__) - for module_directory in lg_common.__path__: - possible_extensions_directory = module_directory + "/extensions" - logger.info("Checking if %s exists to load extensions from it" % possible_extensions_directory) - if os.path.isdir(possible_extensions_directory): - logger.info("%s exists!" % possible_extensions_directory) - return possible_extensions_directory - - return '' - - def _normalize_url(self, current_url, injected_get_args): - replace = [] - - url_parts = urlparse(current_url) - if url_parts.query: - get_args = parse_qs(url_parts.query, keep_blank_values=True) - filtered = dict((k, v) for k, v in get_args.items() if k not in injected_get_args) - - newurl = urlunparse([ - url_parts.scheme, url_parts.netloc, url_parts.path, url_parts.params, - urlencode(filtered, doseq=True), # query string - url_parts.fragment]) - - return newurl - - return current_url - - def _serialize_browser_pool(self, with_info=False): - """ - iterates over a dict of self.browsers and returns a dict - where key is the ID of the browser and value is a json - serialized representation of browsers' .__str__() method - - rtype: dict - """ - serialized_browsers = {} - for browser_id, browser in list(self.browsers.items()): - serialized_browser = json.loads(browser.__str__()) - for key in list(self.browsers_info[browser_id].keys()): - serialized_browser[key] = self.browsers_info[browser_id][key] - - serialized_browser['current_url_normalized'] = self._normalize_url( - serialized_browser.get('current_url', ''), - serialized_browser.get('injected_get_args', [])) - - serialized_browsers[browser_id] = serialized_browser - - return serialized_browsers - - def process_service_request(self, req): - """ - Callback for service requests. We always return self.browsers - in a form of a strigified dictionary - """ - - with self.lock: - response = json.dumps(self._serialize_browser_pool()) - logger.info("Received BrowserPool service request") - logger.info("Returning %s" % response) - - try: - options = json.loads(req.options) - self._filter_service_response(options, response) - except Exception: - pass - - return response - - def _filter_service_response(self, options, response): - pass - - def process_url_update(self, msg): - with self.lock: - browser_info = self.browsers_info.get(msg.browser_id, None) - if browser_info: - browser_info['current_url'] = msg.url - browser_info['url_ts'] = time.time() - - def _init_service(self): - rospy.Service('/browser_service/{}'.format(self.viewport_name), - BrowserPool, - self.process_service_request) - - rospy.Subscriber("/browser_service/{}/update_url".format(self.viewport_name), - BrowserURL, - self.process_url_update) - - def _get_incoming_browsers_dict(self, browsers): - """ - Return dict(id: AdhocBrowser()) with all AdhocBrowser()s with pool ids as keys - """ - return {b.id: b for b in browsers} - - def _remove_browser(self, browser_pool_id): - """ - call .close() on browser object and cleanly delete the object - """ - logger.debug("Removing browser with id %s" % (browser_pool_id)) - self.browsers[browser_pool_id].close() - del self.browsers[browser_pool_id] - del self.browsers_info[browser_pool_id] - logger.debug("State after %s removal: %s" % (browser_pool_id, self.browsers)) - - def _filter_command_line_args(self, command_line_args): - """ - remove/escape dangerous command_line_args - """ - result = [] - for arg in command_line_args: - if ';' in arg: - logger.erroror("There is ';' in command line arguments for adhock browser") - return [] - - if 'enable-arc' in arg or 'enable-nacl' in arg: - logger.error("Unsupported command line arg %s" % arg) - return [] - - result.append(arg) - - return result - - def _get_browser_window_geometry(self, adhoc_browser_msg): - """ - accepts AdhocBrowser message and returns WindowGeometry class instance - """ - return WindowGeometry(x=adhoc_browser_msg.geometry.x, - y=adhoc_browser_msg.geometry.y, - width=adhoc_browser_msg.geometry.width, - height=adhoc_browser_msg.geometry.height) - - def _get_browser_extensions(self, adhoc_browser_msg, system_extensions): - """ - accepts AdhocBrowser message and rewrites extension names - to full extension's path under self.extensions_root that will - be passed to --load-extension upon initialization - - adds extensions from system_extensions which were added by - pool itself to serve internal needs - - if extensions exists under lg_common/extensions directory then - it's going have higher prio over external extensions. In other words - choose lg_ros_nodes shipped extensions over external extensions from - the filesystem located under self.extensions_root - - returns: list of extensions paths - """ - extensions = [] - msg_extensions = [extension.name for extension in adhoc_browser_msg.extensions] - for extension in system_extensions + msg_extensions: - if os.path.isdir(self.lg_common_internal_extensions_root + "/" + extension): - extensions.append(self.lg_common_internal_extensions_root + "/" + extension) - else: - extensions.append(self.extensions_root + extension) - - return extensions - - def _get_browser_command_line_args(self, adhoc_browser_msg): - """ - accepts AdhocBrowser message and extracts command line arguments from it - with some filtering of dangerous stuff - - returns: list - """ - command_line_args = [cmd_arg.argument for cmd_arg in adhoc_browser_msg.command_line_args] - return self._filter_command_line_args(command_line_args) - - def _get_browser_binary(self, adhoc_browser_msg): - """ - accepts AdhocBrowser message and returns binary that should be used for - launching ManagedAdhocBrowser - - returns: string - """ - if adhoc_browser_msg.version: - binary = "/usr/bin/google-chrome-%s" % adhoc_browser_msg.version - else: - binary = DEFAULT_BINARY - return binary - - def _get_browser_allowed_urls(self, adhoc_browser_msg): - if adhoc_browser_msg.allowed_urls: - return adhoc_browser_msg.allowed_urls - - return None - - def _create_browser(self, new_browser_pool_id, new_browser, initial_state=None): - """ - Accept AdhocBrowser message and an id. - Assemble ManagedAdhocBrowser instance and add it to the pool - - Set the initial_state of it. - - returns: bool - """ - browser_info = {} - browser_info['ros_instance_name'] = new_browser_pool_id - browser_info['initial_url'] = new_browser.url - browser_info['current_url'] = new_browser.url - browser_info['url_ts'] = time.time() - browser_info['creation_ts'] = time.time() - browser_info['injected_get_args'] = [] - - additional_extensions = [] - if new_browser.allowed_urls: - additional_extensions.append('monitor_page_urls') - - if new_browser.preload: - additional_extensions.append('ros_window_ready') - - additional_extensions.append('current_url') - - geometry = self._get_browser_window_geometry(new_browser) - extensions = self._get_browser_extensions(new_browser, additional_extensions) - command_line_args = self._get_browser_command_line_args(new_browser) - default_args_removal = [cmd_arg.argument for cmd_arg in new_browser.default_args_removal] - binary = self._get_browser_binary(new_browser) - - if new_browser.custom_preload_event: - logger.info("Using custom preloading event") - new_browser.url = self._inject_get_argument(new_browser.url, 'use_app_event', 1) - else: - logger.info("NOT Using custom preloading event") - - # Add ros_instance_name to evry browser - new_browser.url = self._inject_get_argument(new_browser.url, 'ros_instance_name', new_browser_pool_id) - browser_info['injected_get_args'].append('ros_instance_name') - - # Add viewport to evry browser - new_browser.url = self._inject_get_argument(new_browser.url, 'viewport', self.viewport_name) - browser_info['injected_get_args'].append('viewport') - - # Default host for rosbridge is localhost - # and we are not going to change that very often - new_browser.url = self._inject_get_argument(new_browser.url, - 'rosbridge_port', - self.rosbridge_port) - browser_info['injected_get_args'].append('rosbridge_port') - - new_browser.url = self._inject_get_argument(new_browser.url, - 'rosbridge_secure', - 1 if self.rosbridge_secure else 0) - browser_info['injected_get_args'].append('rosbridge_secure') - - allowed_urls = self._get_browser_allowed_urls(new_browser) - - if allowed_urls: - new_browser.url = self._inject_get_argument( - new_browser.url, - 'allowed_urls', - allowed_urls - ) - browser_info['injected_get_args'].append('allowed_urls') - - logger.debug( - "Creating new browser %s with id %s and url %s" % - (new_browser, new_browser_pool_id, new_browser.url) - ) - managed_adhoc_browser = ManagedAdhocBrowser(geometry=geometry, - log_level=self.log_level, - slug=self.viewport_name + "_" + new_browser_pool_id, - user_agent=new_browser.user_agent, - extensions=extensions, - command_line_args=command_line_args, - default_args_removal=default_args_removal, - binary=binary, - url=new_browser.url, - uid=new_browser_pool_id, - scene_slug=new_browser.scene_slug, - preload=new_browser.preload, - user_data_dir=new_browser.user_data_dir, - kiosk=new_browser.kiosk, - layer=ManagedWindow.LAYER_ABOVE, - ) - - self.browsers[new_browser_pool_id] = managed_adhoc_browser - self.browsers_info[new_browser_pool_id] = browser_info - logger.info("State after addition of %s: %s" % (new_browser_pool_id, list(self.browsers.keys()))) - - if initial_state: - logger.debug("Setting initial state of %s to %s" % (new_browser_pool_id, initial_state)) - managed_adhoc_browser.set_state(ApplicationState.STARTED) - else: - managed_adhoc_browser.set_state(ApplicationState.VISIBLE) - - return True - - def minimize_browsers(self, ids): - """ - For now, just hide the browserts - """ - self._hide_browsers_ids(ids) - - def _hide_browsers_ids(self, ids): - """ - Accepts a list of browser pool ids to hide - - returns: bool - """ - for browser_pool_id in ids: - logger.debug("Hiding browser with id %s" % browser_pool_id) - self.browsers[browser_pool_id].set_state(ApplicationState.HIDDEN) - - return True - - def _destroy_browsers_ids(self, ids): - """ - Accepts a list of browser ids to destroy (kill and remove) - """ - logger.debug("Browsers to remove = %s" % (ids)) - for browser_pool_id in ids: - logger.debug("Removing browser id %s" % browser_pool_id) - self._remove_browser(browser_pool_id) - - def _get_all_preloadable_instances(self, data): - """ - returns all browsers' ID prefixes shepherded by this pool, that are preloadable (which means - that they are already shown as well as the instances that need to become unhidden) - - all browsers are identified with an ID. preloadable browsers have a prefix+suffix ID - that's created from their normal stastic id + a suffix to make them be unique and reloaded - upon every scene (unlike non-preloadable browsers that persists thru scenes) - - returns: list - """ - - # TODO (wz) - check if sha1 generates a `_` character - preloadable_prefixes = [new_browser_id.split('_')[0] for new_browser_id in data.instances if '_' in new_browser_id] - - return preloadable_prefixes - - def unhide_browsers(self, data): - """ - This is a callback executed upon a message that contains information - about readiness of browsers that belong to a scene e.g. - - ----- - scene_slug: 659f6d8d-7c40-4f2c-911b-49390ac13e5f__tvn24 - activity_type: browser - instances: ['50220834', '1234rewq'] - ----- - - These browsers should already exist in STARTED state and this callback should - unhide them and hide old browsers. - - """ - with self.lock: - logger.info("============UNHIDING BEGIN %s ============" % data.instances) - preloadable_prefixes = self._get_all_preloadable_instances(data) - logger.info("Preloadable prefixes: %s" % preloadable_prefixes) - old_preloadable_instances_to_remove = frozenset(self._get_old_preloadable_browser_instances(preloadable_prefixes, data)) - self._unhide_browser_instances(data) - logger.info("Old preloadable instances to remove: %s" % old_preloadable_instances_to_remove) - rospy.sleep(self.hide_delay) - self._hide_browsers_ids(old_preloadable_instances_to_remove) - rospy.sleep(self.destroy_delay) - self._destroy_browsers_ids(old_preloadable_instances_to_remove) - logger.info("============UNHIDING END %s ============" % data.instances) - - def _get_old_preloadable_browser_instances(self, preloadable_prefixes, data): - """ - Accepts a list of all preloadable prefixes - Returns a list of prelodable browser prefixes that should be removed - - Consider preloadable browsers only - """ - remove = [] - - for browser_instance in list(self.browsers.values()): - # consider only preloadable browsers - if browser_instance.preload is True: - # if browser prefix is not mentioned in the incoming scene - # then it may be marked for removal - if browser_instance.id.split('_')[0] not in preloadable_prefixes: - remove.append(browser_instance.id) - # edgcase coverage: if two consecutive scenes - # have an identical slug then remove all preloadable - # instances that are not in the incoming readiness message - # use preloadable_prefixes to find which ones are to be removed - if browser_instance.id.split('_')[0] in preloadable_prefixes and browser_instance.id not in data.instances: - remove.append(browser_instance.id) - - return remove - - def _unhide_browser_instances(self, data): - """ - Accepts a list of browser instances that are ready to be unhidden. - Since data contains all browsers from a scene, we may not be shepherding - some of the browsers on this viewport at this time. - - returns: bool - """ - for browser_pool_id in data.instances: - logger.info("Unhiding browser with id %s" % (browser_pool_id)) - try: - self.browsers[browser_pool_id].set_state(ApplicationState.VISIBLE) - except KeyError: - logger.debug("Could not remove %s from %s because browser doesnt exist in this pool" % (browser_pool_id, list(self.browsers.keys()))) - except Exception as e: - logger.debug("Could not remove %s from %s because browser doesnt exist in this pool because: %s" % (browser_pool_id, list(self.browsers.keys()), e)) - - def _inject_get_argument(self, url, get_arg_name, get_arg_value): - """ - Accepts a string of url and injects get argument with - get_arg_name and get_arg_value - - returns modified URL - - """ - url_parts = urlparse(url) - - if url_parts.query is None: - new_q = '{}={}'.format(get_arg_name, get_arg_value) - url_parts = url_parts._replace(query=new_q) - return url_parts.geturl() - - get_args = parse_qs(url_parts.query) - get_args[get_arg_name] = get_arg_value - - # join args without encoding - browser will do the rest - arg_list = [] - for item in list(get_args.items()): - if type(item[1]) == list: - for val in item[1]: - arg = str(item[0]) + "=" + str(val) - arg_list.append(arg) - else: - arg = str(item[0]) + "=" + str(item[1]) - arg_list.append(arg) - - new_q = '&'.join(arg_list) - url_parts = url_parts._replace(query=new_q) - return url_parts.geturl() - - def handle_ros_message(self, data): - """ - Handles a message that contains AdhocBrowser list inside of an AdhocBrowsers message. - - Creates and unhides browsers that are not preloadable. - Creates browsers that need to preload. - - """ - - with self.lock: - self.scene_slug = data.scene_slug - logger.debug("============= NEW SCENE BEGIN (slug: %s) ===============" % self.scene_slug) - incoming_browsers = self._get_incoming_browsers_dict(data.browsers) - incoming_browsers_ids = set(incoming_browsers.keys()) # set - current_browsers_ids = get_app_instances_ids(self.browsers) # set - ids_to_preload = set([incoming_browser_id for incoming_browser_id in incoming_browsers_ids if incoming_browsers[incoming_browser_id].preload]) - ids_to_remove = current_browsers_ids - incoming_browsers_ids - ids_to_create = (incoming_browsers_ids - current_browsers_ids).union(ids_to_preload) - logger.debug('incoming ids: {}'.format(incoming_browsers_ids)) - logger.debug('preloadable ids: {}'.format(ids_to_preload)) - logger.debug('current ids: {}'.format(current_browsers_ids)) - logger.debug('partitioned fresh ids: {}'.format(ids_to_create)) - logger.debug('browsers to remove: {}'.format(ids_to_remove)) - self._create_browsers(ids_to_create, incoming_browsers) - self._execute_browser_housekeeping(ids_to_create, ids_to_preload, ids_to_remove) - logger.debug("============== NEW SCENE END (slug: %s) ================" % self.scene_slug) - return True - - def _execute_browser_housekeeping(self, ids_to_create, ids_to_preload, ids_to_remove): - """ - Accepts ids to create, to preload and to remove to be able to decide - which browsers to remove on the basis of their preloadability - - Remove all browsers that are not preloadable immediately - """ - if len(ids_to_preload) > 0: - # if there exist ANY preloadable browsers in new scene then - # filter out all old browsers that are preloadable and need to stay - # on the screens until new browsers become ready and old browsers - # will get hidden and removed - ids_to_remove = [browser for browser in ids_to_remove if '_' not in browser] - - self._destroy_browsers_ids(ids_to_remove) - - return True - - def _create_browsers(self, ids_to_create, incoming_browsers): - """ - Accepts a set of ids to create and incoming_browsers that contain - browsers with these ids. - """ - for browser_pool_id in ids_to_create: - logger.info("Creating browser %s with preload flag: %s" % (browser_pool_id, incoming_browsers[browser_pool_id].preload)) - if incoming_browsers[browser_pool_id].preload: - self._create_browser(browser_pool_id, - incoming_browsers[browser_pool_id], - ApplicationState.STARTED) - else: - self._create_browser(browser_pool_id, - incoming_browsers[browser_pool_id]) - - return True - - def handle_soft_relaunch(self, *args, **kwargs): - # iterate over all browsers and call the handle_soft_relaunch - # from the great grand parent ManagedApplication - for browser in self.browsers: - self.browsers[browser].handle_soft_relaunch() - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From bfd08d16b86bdcd756995b683ff8da554e5ea35d Mon Sep 17 00:00:00 2001 From: "Constante \"Tino\" Gonzalez" Date: Mon, 12 Jun 2023 14:15:46 -0400 Subject: [PATCH 02/13] virtual_keyboard add files --- .../extensions/virtual_keyboard/README.md | 10 + .../virtual_keyboard/background.html | 16 + .../extensions/virtual_keyboard/background.js | 123 ++ .../virtual_keyboard/buttons/keyboard_1.png | Bin 0 -> 222 bytes .../virtual_keyboard/buttons/keyboard_2.png | Bin 0 -> 168 bytes .../virtual_keyboard/buttons/keyboard_3.png | Bin 0 -> 245 bytes .../virtual_keyboard/icons/backspace-e.png | Bin 0 -> 1054 bytes .../virtual_keyboard/icons/backspace.png | Bin 0 -> 868 bytes .../virtual_keyboard/icons/close.png | Bin 0 -> 1600 bytes .../virtual_keyboard/icons/enter-e.png | Bin 0 -> 781 bytes .../virtual_keyboard/icons/enter.png | Bin 0 -> 664 bytes .../virtual_keyboard/icons/settings.png | Bin 0 -> 1767 bytes .../virtual_keyboard/icons/shift.png | Bin 0 -> 1540 bytes .../extensions/virtual_keyboard/keyboard.html | 138 ++ .../extensions/virtual_keyboard/keyboard.png | Bin 0 -> 16237 bytes .../virtual_keyboard/keyboard_de.html | 96 ++ .../virtual_keyboard/keyboard_en.html | 58 + .../virtual_keyboard/keyboard_es.html | 59 + .../virtual_keyboard/keyboard_fr.html | 118 ++ .../virtual_keyboard/keyboard_hu.html | 63 + .../virtual_keyboard/keyboard_it.html | 106 ++ .../virtual_keyboard/keyboard_kr.html | 76 + .../virtual_keyboard/keyboard_no.html | 60 + .../virtual_keyboard/keyboard_pl.html | 118 ++ .../virtual_keyboard/keyboard_ru.html | 62 + .../virtual_keyboard/keyboard_sl.html | 64 + .../virtual_keyboard/keyboard_sw.html | 60 + .../virtual_keyboard/keyboard_ta.html | 62 + .../extensions/virtual_keyboard/manifest.json | 31 + .../extensions/virtual_keyboard/options.html | 118 ++ .../extensions/virtual_keyboard/options.js | 135 ++ .../virtual_keyboard/options/script.js | 138 ++ .../virtual_keyboard/options/styles.css | 261 ++++ .../extensions/virtual_keyboard/script.js | 1392 +++++++++++++++++ .../extensions/virtual_keyboard/style.css | 484 ++++++ .../extensions/virtual_keyboard/toggle.html | 28 + .../extensions/virtual_keyboard/toggle.js | 34 + 37 files changed, 3910 insertions(+) create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/README.md create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/background.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/background.js create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/buttons/keyboard_1.png create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/buttons/keyboard_2.png create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/buttons/keyboard_3.png create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/icons/backspace-e.png create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/icons/backspace.png create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/icons/close.png create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/icons/enter-e.png create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/icons/enter.png create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/icons/settings.png create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/icons/shift.png create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard.png create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_de.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_en.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_es.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_fr.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_hu.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_it.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_kr.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_no.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_pl.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_ru.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_sl.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_sw.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_ta.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/manifest.json create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/options.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/options.js create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/options/script.js create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/options/styles.css create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/script.js create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/style.css create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/toggle.html create mode 100644 lg_common/src/lg_common/extensions/virtual_keyboard/toggle.js diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/README.md b/lg_common/src/lg_common/extensions/virtual_keyboard/README.md new file mode 100644 index 000000000..c5ecbf5ad --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/README.md @@ -0,0 +1,10 @@ +Virtual Keyboard for Google Chrome™ +========================================= + +Virtual Keyboard for Google Chrome™ will popup automatically when the user clicks on an input field such as textboxes and textareas. Futhermore, the keyboard will disappear automatically once no longer needed. + +This extension is ideal for touch screen devices. This keyboard works like an iOS/Android/Windows 8 touch virtual keyboard. + + + +For more details visit: http://xontab.com/Apps/VirtualKeyboard/ diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/background.html b/lg_common/src/lg_common/extensions/virtual_keyboard/background.html new file mode 100644 index 000000000..fe35c9630 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/background.html @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/background.js b/lg_common/src/lg_common/extensions/virtual_keyboard/background.js new file mode 100644 index 000000000..4834ab8e1 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/background.js @@ -0,0 +1,123 @@ +chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { + if (request.method == "getLocalStorage") + { + sendResponse({data: localStorage[request.key]}); + } + else if (request.method == "getSmallKeyboardCoords") + { + sendResponse({smallKeyboard: localStorage["smallKeyboard"], smallKeyboardTop: localStorage["smallKeyboardTop"], smallKeyboardBottom: localStorage["smallKeyboardBottom"], smallKeyboardRight: localStorage["smallKeyboardRight"], smallKeyboardLeft: localStorage["smallKeyboardLeft"]}); + } + else if (request.method == "loadKeyboardSettings") + { + sendResponse({openedFirstTime: localStorage["openedFirstTime"], + capsLock: localStorage["capsLock"], + smallKeyboard: localStorage["smallKeyboard"], + touchEvents: localStorage["touchEvents"], + keyboardLayout1: localStorage["keyboardLayout1"], + urlButton: localStorage["urlButton"], + keyboardEnabled: localStorage["keyboardEnabled"]}); + } + else if (request.method == "initLoadKeyboardSettings") + { + sendResponse({hardwareAcceleration: localStorage["hardwareAcceleration"], + zoomLevel: localStorage["zoomLevel"], + autoTrigger: localStorage["autoTrigger"], + repeatLetters: localStorage["repeatLetters"], + intelligentScroll: localStorage["intelligentScroll"], + autoTriggerLinks: localStorage["autoTriggerLinks"], + autoTriggerAfter: localStorage["autoTriggerAfter"]}); + } + else if (request.method == "setLocalStorage") + { + localStorage[request.key] = request.value; + sendResponse({data: "ok"}); + } + else if (request.method == "openFromIframe") + { + chrome.tabs.getSelected(null, function(tab) { + chrome.tabs.sendRequest(tab.id, request); + }); + } + else if (request.method == "clickFromIframe") + { + chrome.tabs.getSelected(null, function(tab) { + chrome.tabs.sendRequest(tab.id, request); + }); + } + else if (request.method == "toogleKeyboard") + { + if (localStorage["keyboardEnabled"] != "false") { + localStorage["keyboardEnabled"] = "false"; + } else { + localStorage["keyboardEnabled"] = "true"; + } + chrome.tabs.getSelected(null, function(tab) { + vkeyboard_loadPageIcon(tab.id); + if (localStorage["keyboardEnabled"] == "false") { + chrome.tabs.sendRequest(tab.id, "closeKeyboard"); + } else { + chrome.tabs.sendRequest(tab.id, "openKeyboard"); + } + }) + sendResponse({data: "ok"}); + } + else if (request.method == "toogleKeyboardOn") + { + localStorage["keyboardEnabled"] = "true"; + chrome.tabs.getSelected(null, function(tab) { + vkeyboard_loadPageIcon(tab.id); + chrome.tabs.sendRequest(tab.id, "openKeyboard"); + }) + sendResponse({data: "ok"}); + } + else if (request.method == "toogleKeyboardDemand") + { + localStorage["keyboardEnabled"] = "demand"; + chrome.tabs.getSelected(null, function(tab) { + vkeyboard_loadPageIcon(tab.id); + chrome.tabs.sendRequest(tab.id, "openKeyboard"); + }) + sendResponse({data: "ok"}); + } + else if (request.method == "toogleKeyboardOff") + { + localStorage["keyboardEnabled"] = "false"; + chrome.tabs.getSelected(null, function(tab) { + vkeyboard_loadPageIcon(tab.id); + chrome.tabs.sendRequest(tab.id, "closeKeyboard"); + }) + sendResponse({data: "ok"}); + } + else if (request.method == "openUrlBar") + { + chrome.tabs.getSelected(null, function(tab) { + chrome.tabs.sendRequest(tab.id, "openUrlBar"); + sendResponse({data: "ok" }); + }); + } else if (request.method == "createTab") { + chrome.tabs.create({ url: request.url }); + } + else { + sendResponse({}); + } +}); + +function vkeyboard_loadPageIcon(tabId) { + if (localStorage["keyboardEnabled"] == "demand") { + chrome.pageAction.setIcon({ tabId: tabId, path: "buttons/keyboard_2.png" }, function() { }) + } else if (localStorage["keyboardEnabled"] != "false") { + chrome.pageAction.setIcon({ tabId: tabId, path: "buttons/keyboard_1.png" }, function() { }) + } else { + chrome.pageAction.setIcon({ tabId: tabId, path: "buttons/keyboard_3.png" }, function() { }) + } +} + +chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { + if (localStorage["toogleKeyboard"] != "false") { + chrome.pageAction.show(tabId); + vkeyboard_loadPageIcon(tabId); + } else { + localStorage["keyboardEnabled"] = "true"; + chrome.pageAction.hide(tabId); + } +}); diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/buttons/keyboard_1.png b/lg_common/src/lg_common/extensions/virtual_keyboard/buttons/keyboard_1.png new file mode 100644 index 0000000000000000000000000000000000000000..f913802901e322a5abca0d910c986043485a47a6 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X8a!PbLn>~)nd->Jpvd7`nsHfjm&5n{!RZ`b1qFdqE+29| zdy4;1_>VnYDk@4lRB6D literal 0 HcmV?d00001 diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/buttons/keyboard_2.png b/lg_common/src/lg_common/extensions/virtual_keyboard/buttons/keyboard_2.png new file mode 100644 index 0000000000000000000000000000000000000000..bd528fa656c9617037beb94804d675defb2da74a GIT binary patch literal 168 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 literal 0 HcmV?d00001 diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/icons/enter-e.png b/lg_common/src/lg_common/extensions/virtual_keyboard/icons/enter-e.png new file mode 100644 index 0000000000000000000000000000000000000000..90d618871a8f2443a454da1bef008fa4f227fbbf GIT binary patch literal 781 zcmV+o1M>WdP)?Xy62!atLtq3BoMOQ}?n zymRNEZgGL!$46 z2LgekMx$|7uh&oHa`{uV76P>$2yntDBsgmig+dq8>GXK;Ky4^-Xty{2$p!d@vZiz=;~m1Bw7NjtE;S1t4D{LJEa?uh)AgolZZ{YPE9y z{%|;4K)gJ_u#dgdRX0GUjNS~YPVL)e$TD7YT)_xnxxeEw9Y(<%6jrcx;? z7K?pBkI!)_kIvuQW>er+66o+<6OWR+qii;Nib}e*P+u;WzaHFfu5gE=~=i^t=gUaxn#&r5_7NPsy0`DK^Ob-z?9 zJv5um8lgkTIGEXN_ERVs+|l!wR;%?Rc)rNU$Mqh{&FXHq`);LDd1Nw~RQyB5VzD0% zhdVdCtAp-(1}Ya}lLu%04u|9YU@*88YVg!*wd=NSh}RhUtA-MY$a?z-Rh!NBj-sfk zkOAyC*a7mTI@m!d;Rw+0_ZcAr*kP~(m6y&zJH%pF`{`Yyh~_D-;S}SHzz|!gm<*OMn3YvuK9i)$-gr00000 LNkvXXu0mjfo&$5n literal 0 HcmV?d00001 diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/icons/enter.png b/lg_common/src/lg_common/extensions/virtual_keyboard/icons/enter.png new file mode 100644 index 0000000000000000000000000000000000000000..25fc03cf58eefb03d7ae3e7222df13289db89f2a GIT binary patch literal 664 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 JPDHLkV1hslS=Imm literal 0 HcmV?d00001 diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/icons/shift.png b/lg_common/src/lg_common/extensions/virtual_keyboard/icons/shift.png new file mode 100644 index 0000000000000000000000000000000000000000..e991c89795b2f66514f3a3144b9f53af5c14b86a GIT binary patch literal 1540 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qErUQl>DSr1<%~X^wgl# z#FWaylc_cg49rTIArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8NvY|XdA3UL zckfqH$V{%1*XSQL?vFu&J;D8jzb>lBiITo0C^; zRbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XRMoSU}&gdW~OIoVrph)sH0$H zU}&Uo07PcGh9*{~W>!Y#3Q(W~w5=#5%__*n4QdyVXRDM^Qc_^0uU}qXu2*iXmtT~w zZ)j<02{OaTNEfI=x41H|B(Xv_uUHvof=g;~a#3bMNoIbY0?5R~r2NtnTP2`NAzsKW zfE$}v3=Jk=fazBx7U&!58GyV5Q|Rl9UukYGTy=3tP%6T`SPd=?sVqp<4@xc0FD*(2 zMqHXQ$f^P>=c3falKi5O{QMkPCP^Az79*T` z^?{Dj2SqGWM8kxDsRzV_CtDx~p72xifT_I*n5=JgKI{W#2}Mwr0*5^)rG#YWrZRxi zrp@o&H5ip7FDd$mHfc5}Ffqr|H1arM@yunS~RUU0C2M5H~Z*%{B2UYgA%NR(sXgd3U&v9{&FB z%31dNo$r6w|Cj&%@!jrVjVEhGBg&YV58RlXuDG77#cAPmAGOb_2ev=G>9LxLul@12 z@Q9NspPU==y*5u=#wOPNbk_6AGjIPNOWL@KA%1$A`*Jq5$ty49m{mK6JoLAaNn_p7 zcCOE_G|fcn^Vh1q%4|og4<>wwY`DBrD1=>W>Z>`ak$EyA&u!$+KQ?)=`-IB$)3ui! zHXeIfa?h9PzSGf{CBK5XM6%zO{qB|7AuOsY)LCvB5OVo#S>+Fw7|+Jp6D{QW_n&Ni zw5x3QU1JHJ=@;HldA6m|L*5Y}3fK*HVQdmS29Fy)}yUm4u+~EoXT_YZdu9?5$3XSvj|I%uF_3xFWq< z%%dst>GKDXrDaFT@4c#A{olI5P0QE%LA&CtHg6T(uMby+t)3gU`l>SP@j{yxGmi%s oJ@w)?@^62uvbpgOo9AzK-JQ=8F5gvJ4Ju_lUHx3vIVCg!0H3Qfs{jB1 literal 0 HcmV?d00001 diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard.html new file mode 100644 index 000000000..be13ac1f6 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard.html @@ -0,0 +1,138 @@ + + + +
+
+ + +
+
+
+
+
+ +
+ + + + + + + + + + + + + +
1234567890
+
+
+ +
\ No newline at end of file diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard.png b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard.png new file mode 100644 index 0000000000000000000000000000000000000000..5b7663465ddeb5acafd0ac31bb8a6a7e291f6cec GIT binary patch literal 16237 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 T@7x2o(L=~fD@)Z$7>E8J*S82I literal 0 HcmV?d00001 diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_de.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_de.html new file mode 100644 index 000000000..b6ce6c51e --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_de.html @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + +
qwertzuiopüBackspace
+ + + + + + + + + + + + + + + + +
asdfghjklöäEnter
+ + + + + + + + + + + + + + + + + +
yxcvbnm,.?@URL
+ + + + + + + +
&123
+ diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_en.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_en.html new file mode 100644 index 000000000..656868392 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_en.html @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + +
qwertyuiopBackspace
+ + + + + + + + + + + + + + + +
asdfghjkl'Enter
+ + + + + + + + + + + + + + + + + +
zxcvbnm,.?@URL
+ + + + + + + +
&123
+ diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_es.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_es.html new file mode 100644 index 000000000..603b3ce31 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_es.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + +
qwertyuiopBorrar
+ + + + + + + + + + + + + + + + +
asdfghjklñçIntro
+ + + + + + + + + + + + + + + + + +
zxcvbnm,.?@URL
+ + + + + + + +
&123
+ diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_fr.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_fr.html new file mode 100644 index 000000000..458ca3a09 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_fr.html @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + +
azertyuiopBackspace
+ + + + + + + + + + + + + + + +
qsdfghjklmEnter
+ + + + + + + + + + + + + + + + +
,wxcvbn.?@URL
+ + + + + + + +
&123
+ diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_hu.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_hu.html new file mode 100644 index 000000000..093046505 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_hu.html @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + +
qwertzuiopőúTöröl
+ + + + + + + + + + + + + + + + +
asdfghjkléáű
+ + + + + + + + + + + + + + + + + + +
Shiftíyxcvbnmöüó,.?
+ + + + + + + + +
&123Enter
\ No newline at end of file diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_it.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_it.html new file mode 100644 index 000000000..bdbd0a357 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_it.html @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + +
qwertyuiopBackspace
+ + + + + + + + + + + + + + + + +
asdfghjkl'Enter
+ + + + + + + + + + + + + + + + + + +
zxcvbnm,.?@URL
+ + + + + + + + +
&123
diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_kr.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_kr.html new file mode 100644 index 000000000..255df22b7 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_kr.html @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + +
Backspace
+ + + + + + + + + + + + + + + +
'Enter
+ + + + + + + + + + + + + + + + + +
,.?@URL
+ + + + + + + +
&123
+ diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_no.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_no.html new file mode 100644 index 000000000..d11ec394f --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_no.html @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + +
qwertyuiopåBackspace
+ + + + + + + + + + + + + + + + +
asdfghjkløæEnter
+ + + + + + + + + + + + + + + + + +
zxcvbnm,.?@URL
+ + + + + + + +
&123
+ diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_pl.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_pl.html new file mode 100644 index 000000000..08ee6fd17 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_pl.html @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + +
qwertyuiopBackspace
+ + + + + + + + + + + + + + + +
asdfghjkl'Enter
+ + + + + + + + + + + + + + + + + +
zxcvbnm,.?@URL
+ + + + + + + +
&123
+ diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_ru.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_ru.html new file mode 100644 index 000000000..cca2b40ca --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_ru.html @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + +
йцукенгшщзхъBackspace
+ + + + + + + + + + + + + + + + +
фывапролджэEnter
+ + + + + + + + + + + + + + + + + +
ячсмитьбю?@URL
+ + + + + + + + + +
&123.,
diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_sl.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_sl.html new file mode 100644 index 000000000..c06c3bb82 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_sl.html @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + +
qwertzuiopšđžBack
+ + + + + + + + + + + + + + + + +
asdfghjklčćEnter
+ + + + + + + + + + + + + + + + + + + +
<yxcvbnm,.-?@URL
+ + + + + + + +
&123
+ diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_sw.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_sw.html new file mode 100644 index 000000000..b1362912e --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_sw.html @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + +
qwertyuiopåBackspace
+ + + + + + + + + + + + + + + + +
asdfghjklöäEnter
+ + + + + + + + + + + + + + + + + +
zxcvbnm,.?@URL
+ + + + + + + +
&123
+ diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_ta.html b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_ta.html new file mode 100644 index 000000000..da50a0ad3 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/keyboard_ta.html @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + +
<---
+ + + + + + + + + + + + + + + + +
Enter
+ + + + + + + + + + + + + + + + + + +
,.?@URL
+ + + + + + + +
&123
+ diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/manifest.json b/lg_common/src/lg_common/extensions/virtual_keyboard/manifest.json new file mode 100644 index 000000000..c62e6f5ba --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/manifest.json @@ -0,0 +1,31 @@ +{ + "background": { + "scripts": [ "background.js" ] + }, + "content_scripts": [ { + "all_frames": true, + "js": [ "script.js" ], + "matches": [ "\u003Call_urls>" ], + "run_at": "document_end" + } ], + "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", + "description": "Fullscreen on-screen virtual keyboard for touch screen devices", + "icons": { + "128": "keyboard.png", + "16": "keyboard.png", + "48": "keyboard.png" + }, + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdWUOT4txwajSX/30Qe8yIrGiKr6ehAI2wPRTYvv3mOUdKanEiG7USGczwcbtFWU7BvrcyEUCcPEN+OmYK+XNGeYm1EDhJ716pJjIBvJXmcYVGcNw+ByuLPQk/1pg+veTSyZuE8MyBVxVSKAUgTUL8+lFN0UbgctaSPRjCzpefuwIDAQAB", + "manifest_version": 2, + "name": "Virtual Keyboard", + "options_page": "options.html", + "page_action": { + "default_icon": "buttons/keyboard_2.png", + "default_popup": "toggle.html", + "default_title": "Virtual Keyboard Toggle" + }, + "permissions": [ "tabs", "\u003Call_urls>" ], + "update_url": "https://clients2.google.com/service/update2/crx", + "version": "1.12.8", + "web_accessible_resources": [ "keyboard.html", "keyboard_*.html", "keyboard_en.html", "keyboard_ru.html", "style.css", "options/*", "toggle.html", "toggle.js", "icons/*", "icons/backspace.png", "icons/backspace-e.png", "icons/close.png", "icons/enter.png", "icons/enter-e.png", "icons/settings.png", "icons/shift.png", "buttons/*", "buttons/keyboard_1.png", "buttons/keyboard_2.png", "buttons/keyboard_3.png" ] +} diff --git a/lg_common/src/lg_common/extensions/virtual_keyboard/options.html b/lg_common/src/lg_common/extensions/virtual_keyboard/options.html new file mode 100644 index 000000000..cee065185 --- /dev/null +++ b/lg_common/src/lg_common/extensions/virtual_keyboard/options.html @@ -0,0 +1,118 @@ + + + + + + Virtual Keyboard Settings + + + + +

Virtual Keyboard Settings

+
+ +
+

Keyboard Layout

+ + + + + + + + + + + +
Available Layouts:Chosen Layouts:
+ + + +
+ +
+ +
+
+

Behaviour

+ + + + +
+ +
+ + +
+
+

User Interface (UI)

+ + + + + +
+