From d35201b1420dea25c6d02a573f8d342ba7c8c97f Mon Sep 17 00:00:00 2001 From: Wonnam Jang Date: Wed, 15 Feb 2017 11:26:12 +0900 Subject: [PATCH] Init version Change-Id: Ia882e01e6283761b018a5b104653d2f44bc4f746 Signed-off-by: Wonnam Jang --- AUTHORS | 4 + CMakeLists.txt | 67 ++ LICENSE | 206 +++++ NOTICE | 3 + js/vc-webview-en_US.js | 335 +++++++ js/vc-webview-ko_KR.js | 491 ++++++++++ js/vc-webview.js | 1646 ++++++++++++++++++++++++++++++++++ js_custom/www.youtube.com.js | 20 + js_custom/www.youtube.com.tv.js | 39 + js_custom/www.youtube.com.tv_kids.js | 39 + packaging/vc-webview-js.manifest | 5 + packaging/vc-webview-js.spec | 45 + 12 files changed, 2900 insertions(+) create mode 100644 AUTHORS create mode 100755 CMakeLists.txt create mode 100644 LICENSE create mode 100644 NOTICE create mode 100755 js/vc-webview-en_US.js create mode 100755 js/vc-webview-ko_KR.js create mode 100755 js/vc-webview.js create mode 100755 js_custom/www.youtube.com.js create mode 100755 js_custom/www.youtube.com.tv.js create mode 100755 js_custom/www.youtube.com.tv_kids.js create mode 100755 packaging/vc-webview-js.manifest create mode 100755 packaging/vc-webview-js.spec diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..b5fa653 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ +Wonnam Jang +Kwangyoun Kim +Sooyeon Kim +Suyeon Hwang \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..6887347 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,67 @@ +# Check minimum CMake version +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +# Project name +PROJECT(vc-webview-js) + +# pkg config tool +INCLUDE(FindPkgConfig) + +# Build type +SET(CMAKE_BUILD_TYPE "Release") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -g -fprofile-arcs -ftest-coverage -D_GNU_SOURCE") + +# CMake settings +MESSAGE(STATUS "========================================") +MESSAGE(STATUS "CMAKE_BINARY_DIR: " ${CMAKE_BINARY_DIR}) +MESSAGE(STATUS "CMAKE_CURRENT_BINARY_DIR: " ${CMAKE_CURRENT_BINARY_DIR}) +MESSAGE(STATUS "CMAKE_SOURCE_DIR: " ${CMAKE_SOURCE_DIR}) +MESSAGE(STATUS "CMAKE_CURRENT_SOURCE_DIR: " ${CMAKE_CURRENT_SOURCE_DIR}) +MESSAGE(STATUS "PROJECT_BINARY_DIR: " ${PROJECT_BINARY_DIR}) +MESSAGE(STATUS "PROJECT_SOURCE_DIR: " ${PROJECT_SOURCE_DIR}) +MESSAGE(STATUS "EXECUTABLE_OUTPUT_PATH: " ${EXECUTABLE_OUTPUT_PATH}) +MESSAGE(STATUS "LIBRARY_OUTPUT_PATH: " ${LIBRARY_OUTPUT_PATH}) +MESSAGE(STATUS "CMAKE_MODULE_PATH: " ${CMAKE_MODULE_PATH}) +MESSAGE(STATUS "CMAKE_COMMAND: " ${CMAKE_COMMAND}) +MESSAGE(STATUS "CMAKE_ROOT: " ${CMAKE_ROOT}) +MESSAGE(STATUS "CMAKE_CURRENT_LIST_FILE: " ${CMAKE_CURRENT_LIST_FILE}) +MESSAGE(STATUS "CMAKE_CURRENT_LIST_LINE: " ${CMAKE_CURRENT_LIST_LINE}) +MESSAGE(STATUS "CMAKE_INCLUDE_PATH: " ${CMAKE_INCLUDE_PATH}) +MESSAGE(STATUS "CMAKE_LIBRARY_PATH: " ${CMAKE_LIBRARY_PATH}) +MESSAGE(STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM}) +MESSAGE(STATUS "CMAKE_SYSTEM_NAME: " ${CMAKE_SYSTEM_NAME}) +MESSAGE(STATUS "CMAKE_SYSTEM_VERSION: " ${CMAKE_SYSTEM_VERSION}) +MESSAGE(STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR}) +MESSAGE(STATUS "UNIX: " ${UNIX}) +MESSAGE(STATUS "WIN32: " ${WIN32}) +MESSAGE(STATUS "APPLE: " ${APPLE}) +MESSAGE(STATUS "MINGW: " ${MINGW}) +MESSAGE(STATUS "CYGWIN: " ${CYGWIN}) +MESSAGE(STATUS "BORLAND: " ${BORLAND}) +MESSAGE(STATUS "MSVC: " ${MSVC}) +MESSAGE(STATUS "MSVC_IDE: " ${MSVC_IDE}) +MESSAGE(STATUS "MSVC60: " ${MSVC60}) +MESSAGE(STATUS "MSVC70: " ${MSVC70}) +MESSAGE(STATUS "MSVC71: " ${MSVC71}) +MESSAGE(STATUS "MSVC80: " ${MSVC80}) +MESSAGE(STATUS "CMAKE_COMPILER_2005: " ${CMAKE_COMPILER_2005}) +MESSAGE(STATUS "CMAKE_SKIP_RULE_DEPENDENCY: " ${CMAKE_SKIP_RULE_DEPENDENCY}) +MESSAGE(STATUS "CMAKE_SKIP_INSTALL_ALL_DEPENDENCY: " ${CMAKE_SKIP_INSTALL_ALL_DEPENDENCY}) +MESSAGE(STATUS "CMAKE_SKIP_RPATH: " ${CMAKE_SKIP_RPATH}) +MESSAGE(STATUS "CMAKE_VERBOSE_MAKEFILE: " ${CMAKE_VERBOSE_MAKEFILE}) +MESSAGE(STATUS "CMAKE_SUPPRESS_REGENERATION: " ${CMAKE_SUPPRESS_REGENERATION}) +MESSAGE(STATUS "CMAKE_C_FLAGS: " ${CMAKE_C_FLAGS}) +MESSAGE(STATUS "CMAKE_CXX_FLAGS: " ${CMAKE_CXX_FLAGS}) +MESSAGE(STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE}) +MESSAGE(STATUS "BUILD_SHARED_LIBS: " ${BUILD_SHARED_LIBS}) +MESSAGE(STATUS "CMAKE_C_COMPILER: " ${CMAKE_C_COMPILER}) +MESSAGE(STATUS "CMAKE_CXX_COMPILER: " ${CMAKE_CXX_COMPILER}) +MESSAGE(STATUS "CMAKE_COMPILER_IS_GNUCC: " ${CMAKE_COMPILER_IS_GNUCC}) +MESSAGE(STATUS "CMAKE_COMPILER_IS_GNUCXX : " ${CMAKE_COMPILER_IS_GNUCXX}) +MESSAGE(STATUS "CMAKE_AR: " ${CMAKE_AR}) +MESSAGE(STATUS "CMAKE_RANLIB: " ${CMAKE_RANLIB}) +MESSAGE(STATUS "========================================") +# Warning flags + +INSTALL(DIRECTORY js DESTINATION ${TZ_SYS_RO_SHARE}/voice/vc-webview/res/) +INSTALL(DIRECTORY js_custom DESTINATION ${TZ_SYS_RO_SHARE}/voice/vc-webview/res/) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..571fe79 --- /dev/null +++ b/LICENSE @@ -0,0 +1,206 @@ +Flora License + +Version 1.1, April, 2013 + +http://floralicense.org/license/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and +all other entities that control, are controlled by, or are +under common control with that entity. For the purposes of +this definition, "control" means (i) the power, direct or indirect, +to cause the direction or management of such entity, +whether by contract or otherwise, or (ii) ownership of fifty percent (50%) +or more of the outstanding shares, or (iii) beneficial ownership of +such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation source, +and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice +that is included in or attached to the work (an example is provided +in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial +revisions, annotations, elaborations, or other modifications represent, +as a whole, an original work of authorship. For the purposes of this License, +Derivative Works shall not include works that remain separable from, +or merely link (or bind by name) to the interfaces of, the Work and +Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original +version of the Work and any modifications or additions to that Work or +Derivative Works thereof, that is intentionally submitted to Licensor +for inclusion in the Work by the copyright owner or by an individual or +Legal Entity authorized to submit on behalf of the copyright owner. +For the purposes of this definition, "submitted" means any form of +electronic, verbal, or written communication sent to the Licensor or +its representatives, including but not limited to communication on +electronic mailing lists, source code control systems, and issue +tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding +communication that is conspicuously marked or otherwise designated +in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +"Tizen Certified Platform" shall mean a software platform that complies +with the standards set forth in the Tizen Compliance Specification +and passes the Tizen Compliance Tests as defined from time to time +by the Tizen Technical Steering Group and certified by the Tizen +Association or its designated agent. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work +solely as incorporated into a Tizen Certified Platform, where such +license applies only to those patent claims licensable by such +Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work solely +as incorporated into a Tizen Certified Platform to which such +Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim +in a lawsuit) alleging that the Work or a Contribution incorporated +within the Work constitutes direct or contributory patent infringement, +then any patent licenses granted to You under this License for that +Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof pursuant to the copyright license +above, in any medium, with or without modifications, and in Source or +Object form, provided that You meet the following conditions: + + 1. You must give any other recipients of the Work or Derivative Works + a copy of this License; and + 2. You must cause any modified files to carry prominent notices stating + that You changed the files; and + 3. You must retain, in the Source form of any Derivative Works that + You distribute, all copyright, patent, trademark, and attribution + notices from the Source form of the Work, excluding those notices + that do not pertain to any part of the Derivative Works; and + 4. If the Work includes a "NOTICE" text file as part of its distribution, + then any Derivative Works that You distribute must include a readable + copy of the attribution notices contained within such NOTICE file, + excluding those notices that do not pertain to any part of + the Derivative Works, in at least one of the following places: + within a NOTICE text file distributed as part of the Derivative Works; + within the Source form or documentation, if provided along with the + Derivative Works; or, within a display generated by the Derivative Works, + if and wherever such third-party notices normally appear. + The contents of the NOTICE file are for informational purposes only + and do not modify the License. You may add Your own attribution notices + within Derivative Works that You distribute, alongside or as an addendum + to the NOTICE text from the Work, provided that such additional attribution + notices cannot be construed as modifying the License. You may add Your own + copyright statement to Your modifications and may provide additional or + different license terms and conditions for use, reproduction, or + distribution of Your modifications, or for any such Derivative Works + as a whole, provided Your use, reproduction, and distribution of + the Work otherwise complies with the conditions stated in this License + and your own copyright statement or terms and conditions do not conflict + the conditions stated in the License including section 3. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Flora License to your work + +To apply the Flora License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Flora License, Version 1.1 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://floralicense.org/license/ + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..205e4cf --- /dev/null +++ b/NOTICE @@ -0,0 +1,3 @@ +Copyright (c) 2017 Samsung Electronics Co., Ltd. All rights reserved. +Except as noted, this software is licensed under Flora License, Version 1.1 +Please, see the LICENSE.Flora file for Flora License, Version 1.1 terms and conditions. \ No newline at end of file diff --git a/js/vc-webview-en_US.js b/js/vc-webview-en_US.js new file mode 100755 index 0000000..fe01a8b --- /dev/null +++ b/js/vc-webview-en_US.js @@ -0,0 +1,335 @@ +/** + * Copyright 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * vc-webview-en_US.js + * + * This source code is a language specified script for English(en_US). + */ + +/** + * vc_search_word function found the text with similarity calculated using words in the @param. + * + * @param param param from voice-control. + * @param replace If replace true, function correct some confused charaters and try again. + */ +function vc_search_word(param, replace) { + /* phase 2. search partial word in the webpage */ + /* second, compare with links in html documents */ + if (vc_flag_log == true) { + vc_rec_result.style.background = 'rgba(0, 100, 200, 1)'; + } + var resultTokenArr = param.split(' '); + var threshold = resultTokenArr.length * 0.3; + var temp = []; + var el = undefined; + + vc_print_log('=== start vc_search_word'); + + for (var i = 0; i < vc_text_indicators.length; ++i) { + var text = vc_text_indicators[i].textContent.replace(/[ `~!‘’@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '').toLowerCase(); + temp[i] = 0; + + for (var j = 0; j < resultTokenArr.length; j++) { + if (vc_is_visible(vc_text_indicators[i], vc_scr, true) && text.indexOf(resultTokenArr[j].toLowerCase()) != -1) { + temp[i]++; + } + } + } + + var max = -1; + + for (var i = 0; i < vc_text_indicators.length; i++) { + if (temp[i] >= threshold && (max == -1 || temp[i] > temp[max])) { + el = vc_text_indicators[i]; + max = i; + } + } + + if (el != undefined) { + return el; + } + + /* first, compare with whole text of elements in html documents */ + var result = []; + for (var i = 0; i < resultTokenArr.length; i++) { + var obj = vc_selector([resultTokenArr[i]]); + for (var j = 0; j < obj.length; j++) { + temp = obj[j]; + if (temp.childElementCount === 0) { + if (temp.hasAttribute('vc_count') == false) { + temp.setAttribute('vc_count', 1); + result.push(temp); + } else { + temp.setAttribute('vc_count', parseInt(temp.getAttribute('vc_count')) + 1); + } + } + } + } + + for (var i = 0; i < result.length; i++) { + var vccnt = parseInt(result[i].getAttribute('vc_count')); + if (vccnt >= threshold && (el == undefined || vccnt > parseInt(el.getAttribute('vc_count')))) { + el = result[i]; + } + } + + for (var i = 0; i < result.length; i++) { + result[i].removeAttribute('vc_count'); + } + + if (el != undefined) { + return el; + } + + return vc_search_character(param); +} + +/** + * vc_search_word function found the text with similarity calculated using characters in the @param. + * + * @param param param from voice-control. + */ +function vc_search_character(param) { + vc_print_log('=== start searching(character level)'); + if (vc_flag_log == true) { + vc_rec_result.style.background = 'rgba(200, 100, 0, 1)'; + } + var similarity = []; + var threshold = 0; + + param = param.toLowerCase().split(' '); + for (var i = 0; i < vc_all_elem.length; i++) { + if (vc_all_elem[i].childElementCount == 0 && vc_is_visible(vc_all_elem[i]) == true) { + var text = vc_all_elem[i].textContent.replace(/[`~!‘’@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, ' ').toLowerCase().split(' '); + var score = 0; + + for(var k = 0; k < param.length; k++) { + var min = 2147483647; + + if (3 > param[k].length) { + continue; + } + + for(var j = 0; j < text.length; j++) { + if (3 > text[j].length) { + continue; + } + var cost = []; + var ncost = []; + var longStr; + var shortStr; + + if (text[j].length > param[k].length) { + longStr = text[j]; + shortStr = param[k]; + } else { + longStr = param[k]; + shortStr = text[j]; + } + + for (var l = 0; l <= longStr.length; l++) { + cost[l] = l; + } + + for (var s = 1; s <= shortStr.length; s++) { + ncost[0] = s; + + for (var l = 1; l <= longStr.length; l++) { + var match = shortStr[s - 1] == longStr[l - 1] ? 0 : 1; + + var rep = cost[l - 1] + match; + var ins = cost[l] + 1; + var del = ncost[l - 1] + 1; + + ncost[l] = Math.min(rep, ins, del); + } + + var arr = cost; + cost = ncost; + ncost = arr; + } + + if (param[k].length * 0.25 >= cost[longStr.length] && min > cost[longStr.length]) { + min = cost[longStr.length]; + } + } + + if (min < 2147483647) { + score = score + min; + } else { + score = score + param[k].length; + } + } + + similarity.push(score); + } else { + similarity.push(2147483647); + } + } + + vc_print_log('=== finish searching(character level)'); + + var min = 2147483647; + for (var i = 0; i < similarity.length; i++) { + if (min > similarity[i]) { + min = similarity[i]; + } + } + + for (var i = 0; i < param.length; i++) { + if (3 <= param[i].length) { + threshold = threshold + param[i].length; + } + } + + if (threshold > min) { + return vc_all_elem[similarity.indexOf(min)]; + } + + return undefined; +} + +/** + * vc_search_word function found the text with similarity calculated using pronunciation keys in the @param. + * + * @param param param from voice-control. + */ +function vc_search_pronunciation(param) { + var el = undefined; + + /* TODO: Fill the logic here to find the link or text include the pronunciation keys in param. + * If you find the link or text, then allocate that into el. + * If the language does not need to pronunciation comapring, you can erase this function. + */ + + return el; +} + +/** + * vc_is_included_number function separate a number value from the @text. + * + * @param text text string from voice-control-webview.cpp. + */ +function vc_is_included_number(text) { + var numbers = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']; + var convert = text.toLowerCase(); + var result; + + for (var i = 0; numbers.length > i; i++) { + if (true == convert.startsWith(numbers[i]) && (' ' == text[numbers[i].length] || text.length == numbers[i].length)) { + var partial = text.substr(numbers[i].length); + + if (vc_visible_hints[i].type == 'input' || text.length == numbers[i].length) { + result = { + cmd : (i + 1), + param : partial.trim() + }; + return result; + } + } + } + + if (true == convert.startsWith('to') && (' ' == text[2] || text.length == 2)) { + if (vc_visible_hints[1].type == 'input' || text.length == 2) { + result = { + cmd : 2, + param : text.substr(3).trim() + }; + return result; + } + } else if (true == convert.startsWith('for') && (' ' == text[3] || text.length == 3)) { + if (vc_visible_hints[3].type == 'input' || text.length == 3) { + result = { + cmd : 4, + param : text.substr(4).trim() + }; + return result; + } + } + + result = { + cmd : NaN, + param : text.trim() + }; + + return result; +} + +/** + * vc_correct_parameter function correct the voice recognition result. + * + * @param text text string from voice-control-webview.cpp. + */ +function vc_correct_parameter(text) { + var result = vc_is_included_number(text), + words = result.param.split(' '); + + if (isNaN(result.cmd) == true && isNaN(words[0]) == false) { + result.cmd = parseFloat(words[0]); + result.param = result.param.substr(words[0].length + 1).trim(); + } + + return result +} + +/** + * vc_check_web_control function check some special keyword and run it. + * + * @param spokenWord voice recognized result string. + */ +function vc_check_web_control(text) { + text = text.toLowerCase(); + var convert = text.replace(/ /g, ''); + var googleSearch = 'google search'; + var youtubeSearch = 'youtube search'; + + if (text.startsWith(googleSearch) == true) { + location.href = 'https://www.google.co.kr/search?q=' + (text.substr(googleSearch.length)).trim(); + } else if (text.startsWith(youtubeSearch) == true) { + location.href = 'https://www.youtube.com/results?search_query=' + (text.substr(youtubeSearch.length)).trim(); + } else if (convert == 'refresh') { + location.reload(); + } else if (convert == 'google') { + location.href = 'https://www.google.co.kr/'; + } else if (convert == 'facebook') { + location.href = 'https://www.facebook.com/'; + } else if (convert == 'amazon') { + location.href = 'https://www.amazon.com/'; + } else if (convert == 'yahoo') { + location.href = 'https://www.yahoo.com/'; + } else if (convert == 'youtube') { + location.href = 'https://www.youtube.com/'; + } else if (convert == 'scrolldown') { + vc_scroll_event_firing('DOWN'); + } else if (convert == 'scrollup') { + vc_scroll_event_firing('UP'); + } else if (convert == 'tothetop') { + vc_scroll_event_firing('TOP'); + } else if (convert == 'showsourcecode') { + vc_print_html(); + } else if (convert == 'next' || convert == 'forward') { + history.forward(); + } else if (convert == 'before' || convert == 'back' || convert == 'previous') { + history.back(); + } else if (convert == 'playlog' && vc_flag_log) { + if (vc_log_area.style.visibility == 'visible') vc_log_area.style.visibility = 'hidden'; + else vc_log_area.style.visibility = 'visible'; + } else { + return false; + } + return true; +} \ No newline at end of file diff --git a/js/vc-webview-ko_KR.js b/js/vc-webview-ko_KR.js new file mode 100755 index 0000000..25c4cdd --- /dev/null +++ b/js/vc-webview-ko_KR.js @@ -0,0 +1,491 @@ +/** + * Copyright 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * vc-webview-ko_KR.js + * + * This source code is a language specified script for Korean(ko_KR). + * If there is no script for some language, this script is loaded as default. + */ + +/** + * vc_search_word function found the text with similarity calculated using words in the @param. + * + * @param param param from voice-control. + * @param replace If replace true, function correct some confused charaters and try again. + */ +function vc_search_word(param, replace) { + /* phase 2. search partial word in the webpage */ + /* second, compare with links in html documents */ + if (vc_flag_log == true) { + vc_rec_result.style.background = 'rgba(0, 100, 200, 1)'; + } + var resultTokenArr = param.split(' '); + var threshold = resultTokenArr.length * 0.3; + var temp = []; + var el = undefined; + + vc_print_log('=== start vc_search_word'); + + for (var i = 0; i < document.links.length; ++i) { + var text = document.links[i].textContent.replace(/[`~!‘’@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, ''); + text = text.replace(/ /g, '').toLowerCase(); + temp[i] = 0; + + for (var j = 0; j < resultTokenArr.length; j++) { + if (vc_is_visible(document.links[i], vc_scr, true) && text.indexOf(resultTokenArr[j].toLowerCase()) != -1) { + temp[i]++; + } + } + } + + var max = -1; + + for (var i = 0; i < document.links.length; i++) { + if (temp[i] >= threshold && (max == -1 || temp[i] > temp[max])) { + el = document.links[i]; + max = i; + } + } + + if (el != undefined) { + return el; + } + + /* first, compare with whole text of elements in html documents */ + var result = []; + for (var i = 0; i < resultTokenArr.length; i++) { + var obj = vc_selector([resultTokenArr[i]]); + for (var j = 0; j < obj.length; j++) { + temp = obj[j]; + if (temp.childElementCount === 0) { + if (temp.hasAttribute('vc_count') == false) { + temp.setAttribute('vc_count', 1); + result.push(temp); + } else { + temp.setAttribute('vc_count', parseInt(temp.getAttribute('vc_count')) + 1); + } + } + } + } + + for (var i = 0; i < result.length; i++) { + var vccnt = parseInt(result[i].getAttribute('vc_count')); + if (vccnt >= threshold && (el == undefined || vccnt > parseInt(el.getAttribute('vc_count')))) { + el = result[i]; + } + } + + for (var i = 0; i < result.length; i++) { + result[i].removeAttribute('vc_count'); + } + + if (el != undefined) { + return el; + } + + if (replace == true) { + var rep = param.replace('이 ', '의 '); + el = vc_search_word(rep, false); + if (el != undefined) return el; + + rep = param.replace('으 ', '이 '); + el = vc_search_word(rep, false); + if (el != undefined) return el; + + rep = param.replace('에 ', '의 '); + el = vc_search_word(rep, false); + if (el != undefined) return el; + + rep = param.replace('의 ', '에 '); + el = vc_search_word(rep, false); + if (el != undefined) return el; + } + + return vc_search_character(param); +} + +/** + * vc_search_word function found the text with similarity calculated using characters in the @param. + * + * @param param param from voice-control. + */ +function vc_search_character(param) { + vc_print_log('=== start searching(character level)'); + if (vc_flag_log == true) { + vc_rec_result.style.background = 'rgba(200, 100, 0, 1)'; + } + var similarity = []; + var threshold = 0; + + param = param.toLowerCase().split(' '); + for (var i = 0; i < vc_all_elem.length; i++) { + if (vc_all_elem[i].childElementCount == 0 && vc_is_visible(vc_all_elem[i]) == true) { + var text = vc_all_elem[i].textContent.replace(/[`~!‘’@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, ' ').toLowerCase().split(' '); + var score = 0; + + for(var k = 0; k < param.length; k++) { + var min = 2147483647; + + if (3 > param[k].length) { + continue; + } + + for(var j = 0; j < text.length; j++) { + if (3 > text[j].length) { + continue; + } + var cost = []; + var ncost = []; + var longStr; + var shortStr; + + if (text[j].length > param[k].length) { + longStr = text[j]; + shortStr = param[k]; + } else { + longStr = param[k]; + shortStr = text[j]; + } + + for (var l = 0; l <= longStr.length; l++) { + cost[l] = l; + } + + for (var s = 1; s <= shortStr.length; s++) { + ncost[0] = s; + + for (var l = 1; l <= longStr.length; l++) { + var match = shortStr[s - 1] == longStr[l - 1] ? 0 : 1; + + var rep = cost[l - 1] + match; + var ins = cost[l] + 1; + var del = ncost[l - 1] + 1; + + ncost[l] = Math.min(rep, ins, del); + } + + var arr = cost; + cost = ncost; + ncost = arr; + } + + if (param[k].length * 0.25 >= cost[longStr.length] && min > cost[longStr.length]) { + min = cost[longStr.length]; + } + } + + if (min < 2147483647) { + score = score + min; + } else { + score = score + param[k].length; + } + } + + similarity.push(score); + } else { + similarity.push(2147483647); + } + } + + vc_print_log('=== finish searching(character level)'); + + var min = 2147483647; + for (var i = 0; i < similarity.length; i++) { + if (min > similarity[i]) { + min = similarity[i]; + } + } + + for (var i = 0; i < param.length; i++) { + if (3 <= param[i].length) { + threshold = threshold + param[i].length; + } + } + + if (threshold > min) { + return vc_all_elem[similarity.indexOf(min)]; + } + + return vc_search_pronunciation(param); +} + +/** + * vc_search_word function found the text with similarity calculated using pronunciation keys in the @param. + * + * @param param param from voice-control. + */ +function vc_convert_to_syllable(param) { + var korUnicodeStart = 44032; + var korUnicodeNum = 11172; + var korUnicodeEnd = 55204; + var korChoCnt = 19; + var korJungCnt = 21; + var korJongCnt = 28; + + var result = []; + + for (var i = 0; i < param.length; i++) { + var c = param.charCodeAt(i); + + if (korUnicodeStart > c || korUnicodeEnd < c) { + continue; + } + + var jongIdx = (c - korUnicodeStart) % korJongCnt; + var jungIdx = (((c - korUnicodeStart) - jongIdx) / korJongCnt) % korJungCnt; + var choIdx = ((((c - korUnicodeStart) - jongIdx) / korJongCnt) - jungIdx) / korJungCnt; + + result.push(choIdx, jungIdx, jongIdx); + } + + return result; +} + +/** + * vc_search_word function found the text with similarity calculated using pronunciation keys in the @param. + * + * @param param param from voice-control. + */ +function vc_search_pronunciation(param) { + if (vc_flag_log == true) { + vc_rec_result.style.background = 'rgba(220, 0, 0, 1)'; + } + if (param.length > 1) { + return undefined; + } + + vc_print_log('=== start searching(pronuciation level)'); + param = param[0]; + + var sylla = vc_convert_to_syllable(param); + var threshold = sylla.length * 0.5; + var el = undefined; + var elScore = 2147483247; + + for (var i = 0; i < vc_all_elem.length; i++) { + if (vc_all_elem[i].childElementCount == 0 && vc_is_visible(vc_all_elem[i]) == true) { + var text = vc_all_elem[i].textContent.replace(/[`~!‘’@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, ' ').toLowerCase().split(' '); + var score = 2147483247; + + for (var j = 0; j < text.length; j++) { + if (param.length > text[j].length) { + continue; + } + var min = 2147483247; + + for (var s = 0; s <= text[j].length - param.length; s++) { + var temp = vc_convert_to_syllable(text[j].substr(s, param.length)); + var cnt = 0; + + for (var k = 0; k < sylla.length; k++) { + if (sylla[k] != temp[k]) { + cnt++; + } + + if (cnt == threshold) { + cnt++; + break; + } + } + + if (min > cnt) { + min = cnt; + } + } + + if (score > min) { + score = min; + } + } + + if (threshold > score && elScore > score) { + el = vc_all_elem[i]; + elScore = score; + } + } + } + + vc_print_log('=== finish searching(pronuciation level)'); + + if (el != undefined) { + return el; + } + + return undefined; +} + +/** + * vc_is_included_number function separate a number value from the @text. + * + * @param text text string from voice-control-webview.cpp. + */ +function vc_is_included_number(text) { + var numbers = ['일', '이', '삼', '사', '오', '육', '칠', '팔', '구', '십', + '십일', '십이', '십삼', '십사', '십오', '십육', '십칠', '십팔', '십구', '이십', + '이십일', '이십이', '이십삼', '이십사', '이십오', '이십육', '이십칠', '이십팔', '이십구', '삼십', + '삼십일', '삼십이', '삼십삼', '삼십사', '삼십오', '삼십육', '삼십칠', '삼십팔', '삼십구', '사십', + '사십일', '사십이', '사십삼', '사십사', '사십오', '사십육', '사십칠', '사십팔', '사십구', '오십', + '오십일', '오십이', '오십삼', '오십사', '오십오', '오십육', '오십칠', '오십팔', '오십구', '육십', + '육십일', '육십이', '육십삼', '육십사', '육십오', '육십육', '육십칠', '육십팔', '육십구', '칠십', + '칠십일', '칠십이', '칠십삼', '칠십사', '칠십오', '칠십육', '칠십칠', '칠십팔', '칠십구', '팔십', + '팔십일', '팔십이', '팔십삼', '팔십사', '팔십오', '팔십육', '팔십칠', '팔십팔', '팔십구', '구십', + '구십일', '구십이', '구십삼', '구십사', '구십오', '구십육', '구십칠', '구십팔', '구십구', '백']; + var seven = ['채널', '질']; + var eight = ['발', '달']; + var convert = text.toLowerCase(); + var result; + + for (var i = 0; numbers.length > i; i++) { + if (true == convert.startsWith(numbers[i]) && (' ' == text[numbers[i].length] || text.length == numbers[i].length)) { + var partial = text.substr(numbers[i].length + 1); + i = i + 1; + + for (var j = 0; 9 > j; j++) { + if (true == partial.toLowerCase().startsWith(numbers[j]) && (' ' == partial[numbers[j].length] || partial.length == numbers[j].length)) { + partial = partial.substr(numbers[j].length + 1); + i = i + j + 1; + break; + } + } + + if (vc_visible_hints[i - 1].type == 'input' || text.length == numbers[i].length) { + result = { + cmd : i, + param : partial.trim() + }; + return result; + } + } + } + + for (var i = 0; seven.length > i; i++) { + if (true == convert.startsWith(seven[i]) && (' ' == text[seven[i].length] || text.length == seven[i].length) && (vc_visible_hints[6].type == 'input' || text.length == seven[i].length)) { + result = { + cmd : 7, + param : text.substr(seven[i].length + 1).trim() + }; + return result; + } + } + + for (var i = 0; eight.length > i; i++) { + if (true == convert.startsWith(eight[i]) && (' ' == text[eight[i].length] || text.length == eight[i].length) && (vc_visible_hints[7].type == 'input' || text.length == eight[i].length)) { + result = { + cmd : 8, + param : text.substr(eight[i].length + 1).trim() + }; + return result; + } + } + + result = { + cmd : NaN, + param : text.trim() + }; + return result; +} + +/** + * vc_correct_parameter function correct the voice recognition result. + * + * @param text text string from voice-control-webview.cpp. + */ +function vc_correct_parameter(text) { + var result = vc_is_included_number(text); + + /* if param is a number, move the value in param to cmd */ + var words = result.param.split(' '); + if (isNaN(result.cmd) == true && isNaN(words[0]) == false) { + result.cmd = parseFloat(words[0]); + result.param = result.param.substr(words[0].length + 1).trim(); + } + + var idx_sep = result.param.indexOf('번 '); + if (isNaN(result.cmd) == true && 0 < idx_sep) { + if (idx_sep == result.param.length - 1) { + result.cmd = parseFloat(result.param.substr(0, idx_sep)); + result.param = ''; + } else { + result.cmd = parseFloat(result.param.substr(0, idx_sep)); + result.param = result.param.substr(idx_sep + 2, result.param.length); + } + } + + return result +} + +/** + * vc_check_web_control function check some special keyword and run it. + * + * @param spokenWord voice recognized result string. + */ +function vc_check_web_control(text) { + text = text.toLowerCase(); + var convert = text.replace(/ /g, ''); + var googleSearch = '구글 검색'; + var naverSearch = '네이버 검색'; + var youtubeSearch = '유튜브 검색'; + var googleSearch2 = '구글 '; + var naverSearch2 = '네이버 '; + var youtubeSearch2 = '유튜브 '; + + if (text.startsWith(googleSearch) == true) { + location.href = 'https://www.google.co.kr/search?q=' + (text.substr(googleSearch.length)).trim(); + } else if (text.startsWith(naverSearch) == true) { + location.href = 'https://search.naver.com/search.naver?where=nexearch&query=' + (text.substr(naverSearch.length)).trim(); + } else if (text.startsWith(youtubeSearch) == true) { + location.href = 'https://www.youtube.com/results?search_query=' + (text.substr(youtubeSearch.length)).trim(); + } else if (convert == '유튜브TV') { + location.href = 'https://www.youtube.com/tv'; + } else if (text.startsWith(googleSearch2) == true) { + location.href = 'https://www.google.co.kr/search?q=' + (text.substr(googleSearch2.length)).trim(); + } else if (text.startsWith(naverSearch2) == true) { + location.href = 'https://search.naver.com/search.naver?where=nexearch&query=' + (text.substr(naverSearch2.length)).trim(); + } else if (text.startsWith(youtubeSearch2) == true) { + location.href = 'https://www.youtube.com/results?search_query=' + (text.substr(youtubeSearch2.length)).trim(); + } else if (convert.startsWith('구글') == true) { + location.href = 'https://www.google.co.kr/'; + } else if (convert.startsWith('네이버') == true) { + location.href = 'http://www.naver.com/'; + } else if (convert.startsWith('유튜브') == true) { + location.href = 'https://www.youtube.com/'; + } else if (convert == '취소') { + vc_remove_hints(); + } else if (convert == '리프레쉬' || convert == '새로고침') { + location.reload(); + } else if (convert == '아래로' || convert == '내려' || convert == '내려봐' || convert == '내려줘') { + vc_scroll_event_firing('DOWN'); + } else if (convert == '위로' || convert == '올려' || convert == '올려봐' || convert == '올려줘') { + vc_scroll_event_firing('UP'); + } else if (convert == '맨위로' || convert == '처음으로') { + vc_scroll_event_firing('TOP'); + } else if (convert == '광고닫기' || convert == '광고찾기') { + document.querySelector('.videoAdUiSkipButton').click(); + } else if (convert == '소스보기') { + vc_print_html(); + } else if (convert == '다음페이지' || convert == '다음으로' || convert == '앞으로가기' || convert == '앞으로') { + history.forward(); + } else if (convert == '이전페이지' || convert == '이전으로' || convert == '뒤로가기' || convert == '뒤로' || convert == '백' || convert == '벡') { + history.back(); + } else if (convert == '로그보기' && vc_flag_log) { + if (vc_log_area.style.visibility == 'visible') vc_log_area.style.visibility = 'hidden'; + else vc_log_area.style.visibility = 'visible'; + } else { + return false; + } + return true; +} \ No newline at end of file diff --git a/js/vc-webview.js b/js/vc-webview.js new file mode 100755 index 0000000..8c785df --- /dev/null +++ b/js/vc-webview.js @@ -0,0 +1,1646 @@ +/** + * Copyright 2017 Samsung Electronics Co., Ltd. + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * vc-webview.js + * + * This source code is a base script of web voice touch. + */ + + /** + * vc_docs holds analysed documents' first hint number and last hint number + * @member doc document elements. + * @member start the first tooltip number in @doc. + * @member end the last tooltip number in @doc. + */ +var vc_docs = [], + +vc_conflict_timer = null, + +/** + * vc_conflict_links holds references to links that match the same keyword + */ +vc_conflict_links = []; + +/** + * vc_scr holds info about screen and website + * @member top coordinate of the top boundary. + * @member left coordinate of the left boundary. + * @member bottom coordinate of the bottom boundary. + * @member right coordinate of the right boundary. + */ +var vc_scr = { + top : 0, + left : 0, + bottom : window.innerHeight, + right : window.innerWidth +}, + +/** + * vc_xpath_query is a query that processes DOM tree to get clickable objects. + * It analyses HTML tree as it is XML and returns matching objects in one of selected forms. + */ +vc_xpath_query = ".//xhtml:input[not(@type='hidden')] | .//xhtml:a | .//xhtml:area | .//xhtml:textarea | .//xhtml:button | .//xhtml:*[" + + "@role='link' or @role='button' or @role='checkbox' or @role='combobox' or @role='radio'] |" + + " .//xhtml:div[contains(@class, 'ui-btn')] | .//xhtml:div[contains(@class, 'sideBarButton')] | .//xhtml:div[contains(@class, 'buttonCaption')]", + +/** + * vc_page_hints holds all hints on website + */ +vc_page_hints = [], + +/** + * vc_hint_div is a DIV that gathers all hints + */ +vc_hint_div, + +/** + * vc_high_div is a DIV that gathers red highlight boxes + */ +vc_high_div, + +/** + * vc_hint_num is used to numerate hints from 1 on every screen they're displayed + */ +vc_hint_num, + +/** + * vc_visible_hints holds all hints that are currently visible + */ +vc_visible_hints = [], + +/** + * vc_bad_iframes holds iframes that should be highlighted with red color + */ +vc_bad_iframes = [], + +/** + * vc_log_area is a textarea element for log texts. + */ +vc_log_area = document.createElement('textarea'), + +/** + * vc_html_area is a textarea element to read raw source code of html document in TV. + */ +vc_html_area = undefined, + +/** + * vc_asr_result is a div element shows the asr result for debugging. + */ +vc_asr_result = undefined, + +/** + * vc_rec_result is a div element shows the recognition result of document searching for debugging. + */ +vc_rec_result = undefined, + +/** + * vc_flag_log is a flag variable. If it is true, vc_log_area is created. Otherwise, vc_log_area is nothing. + */ +vc_flag_log = false, + +/** + * vc_all_elem is a array that includes whole element in html document for text searching. + */ +vc_all_elem = undefined, + +/** + * vc_flag_conflict is a flag variable. When multiple elements are selected, it is true. Otherwise, it is false. + */ +vc_flag_conflict = false, + +/** + * vc_flag_hint_exist is a flag variable. When tooltips are already exist, it is true. Otherwise, it is false. + */ +vc_flag_hint_exist = false, + +/** + * vc_text_indicators is an array that contains text indicator elements + */ +vc_text_indicators = [], + +/** + * vc_made_hint shows the state tooltip making. + */ +vc_made_hint = false, + +/** + * vc_tag_index stores index number of the set of tooltips in web page. + * this variable prevents making duplicated tooltips. + */ +vc_tag_index = 0; + +/** + * prototypes of overridable custom function + */ +function vc_custom_pre_generate_hints() { + vc_print_log('No custom script'); +} + +function vc_custom_pre_show_hints() { + vc_print_log('No custom script'); +} + +function vc_custom_pre_remove_hints() { + vc_print_log('No custom script'); +} + +function vc_custom_pre_hide_hints() { + vc_print_log('No custom script'); +} +/* ======================== */ + +/** + * vc_custom_add_xpath_query functino adds given condition to xpath query + * + * @param className the name of the class of DOM elements + * @param DOMtype(optional) the type name of DOM element + * + * @remind this function must be called in page specific script + */ +function vc_custom_add_xpath_query(className, DOMtype) { + if (undefined == DOMtype) { + DOMtype = 'div'; + } + + vc_xpath_query += " | .//xhtml:" + DOMtype + "[contains(@class, '" + className + "')]"; +} + +/** + * vc_is_child check if element is a child of other element + * + * @param child child to check the parent of + * @param parent to test to + * + * @return true if elements are related, false otherwise + */ +function vc_is_child(child, parent) { + var node = child.parentNode; + while (node && node != document.body) { + if (node === parent) { + return true; + } + node = node.parentNode; + } + return false; +} + +/** + * vc_is_hidden function checks if the element given as parameter is hidden due to its CSS style + * + * @param elem element to be checked + * @param predecessor predecessor of elem to check visibility if elementFromPoint doesn't return elem + * + * @return true if element is invisible, false otherwise + */ +function vc_is_hidden(elem, predecessor) { + if (elem == undefined) { + return true; + } + + /* if predecesor is no found assume parent is one */ + if (!predecessor && elem.parentNode) { + predecessor = elem.parentNode; + } + /* check visibility in css style */ + var doc = elem.ownerDocument, + win = doc.defaultView, + computedStyle = win.getComputedStyle(elem, null); + + if (computedStyle.getPropertyValue('visibility') !== 'visible' || computedStyle.getPropertyValue('display') === 'none') { + return true; + } + + /* object is visible check if it is on current screen */ + var rect = elem.getBoundingClientRect(), + x = rect.left + (rect.width / 2), + y = rect.top + (rect.height / 2), + point; + + if (y < 0 && x >= 0) { + y = 0; + } else if (y < 0 && x < 0) { + x = y = 0; + } else if (y >= 0 && x < 0) { + x = 0; + } + + point = doc.elementFromPoint(x, y); + /* if elementFromPoint returns HTMLBodyElement it probably is a scroll left/right list and it is hidden */ + if (point === doc.body) { + return true; + } + /* if elementFromPoint returns predecessor it is visible */ + if (point && predecessor && vc_is_child(point, predecessor)) { + return false; + } + + return true; +} + +/** + * vc_is_visible function checks if the element provided as argument is currently visible on screen + * + * @param elem element to be checked + * @param scr screen information + * @param isTextLink determines if the element is text link + * + * @return true if visible false otherwise + */ +function vc_is_visible(elem, scr, isTextLink) { + if (elem == undefined) { + return false; + } + + if (!scr) { + scr = { + top : 0, + left : 0, + bottom : window.innerHeight, + right : window.innerWidth + }; + } + // if only two parameters passed to function assume isTextLink is false + isTextLink = typeof isTextLink !== 'undefined' ? isTextLink : false; + + var docy = elem.ownerDocument, + win = docy.defaultView, + computedStyle = win.getComputedStyle(elem, null); + + /* element is not visible if it is fully transparent except select box */ + if (parseFloat(computedStyle.getPropertyValue('opacity')) === 0.0) { + return false; + } + + var rect = elem.getBoundingClientRect(); + + /* element is not visible if it is not on screen */ + if (!rect || rect.top >= scr.bottom || rect.bottom <= scr.top || rect.left >= scr.right || + rect.right <= scr.left) { + return false; + } + + /* element is not visible if it is hidden and it is not a text hyperlink */ + if (!isTextLink && vc_is_hidden(elem, null)) { + return false; + } + + return true; +} + +/** + * vc_make_hint procedure creates invisible hint for element provided as argument + * + * @param elem element for which hint is to be created + * @param child if child is passed, the hint will be positioned according to position of child + */ +function vc_make_hint(elem, child) { + // vc_print_log(elem); + var rect; + + rect = child.getBoundingClientRect(); + + if (!rect || elem.vc_tag_index == vc_tag_index) { + return; + } + + elem.vc_tag_index = vc_tag_index; + + var doc = elem.ownerDocument; + + /** hint object holds clickable element, type of element and created hint (HTMLSpanElement) + * @member elem clickable target element. + * @member type the type of @elem. + * @member child child element of @elem + * @member span tooltip element of @elem + */ + var hint = { + elem : elem, + type : 'normal', + child : child, + span : null + }; + + if(elem instanceof HTMLAreaElement){ + hint.type = 'area'; + } + + /* not all input objects are supported */ + if (elem instanceof HTMLInputElement) { + if (elem.type === 'text' || elem.type === 'password' || elem.type === 'datetime' || elem.type === 'email' || + elem.type === 'search' || elem.type === 'number' || elem.type === 'url') { + hint.type = 'input'; + } else if (elem.type !== 'button' && elem.type !== 'submit' && elem.type !== 'color' && elem.type !== 'radio' && + elem.type !== 'checkbox' && elem.type !== 'file' && elem.type !== 'submit' && elem.type !== 'image') { + return; + } + /* consider TextArea element as input element */ + } else if (elem instanceof HTMLTextAreaElement) { + hint.type = 'input'; + } + + /** span object is the visual representation of hint */ + var span = doc.createElement('span'); + span.id = 'vc_tooltip'; + span.className = 'vc_number_tag_normal vc_tooltip_' + hint.type; + + if (hint.type == 'input') { + span.classList.add('vc_tip_tier_first'); + } else if (rect.height > 30) { + span.classList.add('vc_tip_tier_second'); + } else { + span.classList.add('vc_tip_tier_third'); + } + + hint.span = span; + + /* appends hint to vc_hint_div container */ + vc_hint_div.appendChild(hint.span); + vc_page_hints.push(hint); +} + +/** + * nsResolver function that is used for resolving namespace in webpage document + * + * @param prefix prefix of the namespace + * + * @return namespace url + */ +function nsResolver(prefix) { + var namespace = { + 'xhtml' : 'http://www.w3.org/1999/xhtml' //add other namespaces if needed + }; + return namespace[prefix] || null; +} + +/** + * vc_generate_hints function creates hints for all clickable elements on web page + * + * @param win window element (can be different if we analyse iframe) + */ +function vc_generate_hints(win) { + vc_print_log('== start generate hints' + win); + vc_tag_index++; + + /* if win is not passed assume the global window variable */ + if (!win) { + win = window; + } + + var doc; + + try { + doc = win.document; + } catch (e) { + vc_print_log('== some error occured in win.document' + e); + return; + } + + /* Custom pre process to generate hints */ + vc_custom_pre_generate_hints(); + + var start = vc_page_hints.length; + + vc_hint_div = doc.createElement('div'); + vc_hint_div.id = 'vc_tooltip_area'; + vc_hint_div.style.display = 'block'; + + vc_text_indicators = []; + + /* return result as snapshot not iterator, due to problems with undefined objects */ + vc_print_log('== start doc.evaluate'); + var snapshot = doc.evaluate(vc_xpath_query, doc.body, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + vc_print_log('== snapshot length : ' + snapshot.snapshotLength); + + /* set current screen dimensions */ + vc_scr = { + top : 0, + left : 0, + bottom : win.innerHeight, + right : win.innerWidth + }; + + /* analyse if a hint is needed for each returned object */ + for (var i = 0; i < snapshot.snapshotLength; i++) { + var element = snapshot.snapshotItem(i), + rect = element.getBoundingClientRect(); + + vc_made_hint = false; + /* executed very rarely */ + if (rect.width === 0 || rect.height === 0) { + for (var j = 0; j < element.childNodes.length; j++) { + if (element.childNodes[j].nodeType !== 1) { + continue; + } + + var style = doc.defaultView.getComputedStyle(element.childNodes[j], null); + if (style && style.getPropertyValue('float') !== 'none' && vc_is_visible(element.childNodes[j], vc_scr)) { + vc_make_hint(element, element.childNodes[j]); + vc_made_hint = true; + break; + } + } + + if (vc_made_hint) { + continue; + } + } + + if (vc_is_visible(element, vc_scr) && !element.attributes.disabled) { + if ((element instanceof HTMLButtonElement || element.classList.contains('ui-btn') || element.getAttribute('role') == 'button')) { + if (element.textContent.trim().replace(/[ `~!‘’@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '').length > 0) { + vc_text_indicators[vc_text_indicators.length] = element; + } else { + vc_make_hint(element, element); + vc_made_hint = true; + } + } else if (element.getAttribute('role') == 'radio' || element.getAttribute('role') == 'checkbox' || element.getAttribute('role') == 'combobox') { + vc_make_hint(element, element); + vc_made_hint = true; + /* if the element is an anchor without text, make hint */ + } else if (element instanceof HTMLAnchorElement) { + if (element.textContent.trim().length > 0) { + vc_text_indicators[vc_text_indicators.length] = element; + } + + var cStyle = window.getComputedStyle(element, false); + if (cStyle.backgroundImage !== 'none' && cStyle.backgroundImage !== 'inherit') { + vc_make_hint(element, element); + vc_made_hint = true; + } else if (element.textContent.trim().length == 0) { + /* if element has child with background or image element*/ + if (!vc_made_hint) { + var childs = element.querySelectorAll('*'); + for (var sp = 0; sp < childs.length; ++sp) { + if (vc_is_visible(childs[sp], vc_scr)) { + var cstyle = win.getComputedStyle(childs[sp], false); + if (childs[sp].nodeName == 'IMG' || cstyle.overflow == 'hidden' + || (cstyle.backgroundImage !== 'none' && cstyle.backgroundImage !== 'inherit')) { + vc_make_hint(element, childs[sp]); + vc_made_hint = true; + break; + } + } + } + } + + if (!vc_made_hint) { + vc_make_hint(element, element); + vc_made_hint = true; + continue; + } + } else { + if (isNaN(element.textContent.trim())) { + vc_find_img_element(element, null, element); + } + } + } else if (element instanceof HTMLAreaElement || element.nodeName == 'INPUT') { + vc_make_hint(element, element); + } else { + vc_find_img_element(element, null, element); + if (vc_made_hint == false) { + vc_make_hint(element, element); + } + } + } + } + + /* append whole div with hints to body */ + if (doc.body) { + doc.body.insertBefore(vc_hint_div, doc.body.firstChild); + + //append all text indicators to body + vc_docs.push({ + doc : doc, + start : start, + end : vc_page_hints.length - 1 + }); + } + + /* analyse iframes of the website */ + if (win === window) { + var frames = document.querySelectorAll('iframe'); + for (var f = 0; f < frames.length; f++) { + var elem; + try { + elem = frames[f].contentWindow.document; + } catch (e) { + vc_bad_iframes.push(frames[f]); + continue; + } + vc_scr = { + top : 0, + left : 0, + bottom : win.innerHeight, + right : win.innerWidth + }; + if (elem == undefined) { + vc_bad_iframes.push(frames[f]); + continue; + } + if (elem != undefined && !vc_is_visible(frames[f], vc_scr)) { + continue; + } + // vc_generate_hints(frames[f].contentWindow); + } + } + + vc_flag_hint_exist = true; + vc_print_log('== end'); +} + +/** + * vc_find_img_element function find element containing image. if found then call a function to make a tooltip. + * + * @param elem element to search for text + * @param parent parent element of the @elem + * @param snapshotElem the first @elem when vc_generate_hints calls this function + */ +function vc_find_img_element(elem, parent, snapshotElem) { + if (!vc_made_hint) { + if (elem.nodeType == 1) { + var cStyle = document.defaultView.getComputedStyle(elem, false); + if ((vc_is_visible(elem, vc_scr) && cStyle && cStyle.backgroundImage != 'none' && cStyle.backgroundImage != 'inherit' || elem.nodeName == 'IMG')) { + vc_make_hint(snapshotElem, elem); + vc_made_hint = true; + return; + } + } + + var children = elem.childNodes; + for (var ch = 0; !vc_made_hint && ch < children.length; ch++) { + vc_find_img_element(children[ch], elem, snapshotElem); + } + } + +} + +/** + * vc_show_hints function displays hints numbering from 1 + */ +function vc_show_hints() { + /* Custom pre process to show hints */ + vc_custom_pre_show_hints(); + + if (vc_flag_log == true) { + if (vc_asr_result == undefined) { + vc_asr_result = document.createElement('div'); + vc_asr_result.id = 'vc_asr_result'; + vc_asr_result.innerHTML = 'ASR Result'; + document.body.appendChild(vc_asr_result); + } + + if (vc_rec_result == undefined) { + vc_rec_result = document.createElement('div'); + vc_rec_result.id = 'vc_rec_result'; + document.body.appendChild(vc_rec_result); + } + } + + vc_hint_num = 1; + for (var d = 0; d < vc_docs.length; d++) { + var doc = vc_docs[d]; + for (var i = doc.start; i <= doc.end; i++) { + var win = doc.doc.defaultView, + hint = vc_page_hints[i], + rect = hint.child.getClientRects()[0]; + + vc_scr = { + top : 0, + left : 0, + bottom : win.innerHeight, + right : win.innerWidth + }; + + hint.span.style.display = 'block'; + + if (null == rect) { + continue; + } + + var leftPos, + topPos, + position_factor_top, + position_factor_left; + + if (vc_flag_conflict) { + position_factor_top = 18; + position_factor_left = 18; + hint.span.className = 'vc_text_indicator_conflict_normal vc_tooltip_normal vc_tip_tier_second'; + } else { + position_factor_top = 15; + position_factor_left = 15; + } + + if (rect.top < position_factor_top) { + topPos = win.pageYOffset; + } else { + topPos = rect.top + win.pageYOffset - position_factor_top; + } + + if (rect.left < position_factor_left) { + leftPos = win.pageXOffset; + } else { + leftPos = rect.left + win.pageXOffset - position_factor_left; + } + + if (hint.type == 'area') { + coord = hint.elem.coords.split(','); + leftPos = parseFloat(leftPos) + parseFloat(coord[0]); + topPos = parseFloat(topPos) + parseFloat(coord[1]); + } + hint.span.style.top = topPos + 'px'; + hint.span.style.left = leftPos + 'px'; + hint.span.textContent = vc_hint_num; + + hint.span.style.display = 'block'; + + if (-1 == vc_visible_hints.indexOf(hint)) { + vc_visible_hints.push(hint); + } + + vc_hint_num++; + } + } + + // for (var bf = 0; bf < vc_bad_iframes.length; ++bf) { + // vc_result_highlight(vc_bad_iframes[bf]); + // } +} + +/** + * vc_remove_hints function removes all hints in the web page. It deletes both visible and hidden hints + */ +function vc_remove_hints() { + /* Custom pre process to remove hints */ + vc_custom_pre_remove_hints(); + + vc_remove_highlight(); + + for (var d = 0; d < vc_docs.length; d++) { + var doc = vc_docs[d].doc; + var tooltipArea = doc.querySelector('#vc_tooltip_area'); + if (null !== tooltipArea && tooltipArea.parentNode) { + tooltipArea.parentNode.removeChild(tooltipArea); + } + } + vc_visible_hints = []; + vc_page_hints = []; + vc_docs = []; + + /** if the rotation occured refresh the window information */ + vc_scr = { + top : 0, + left : 0, + bottom : window.innerHeight, + right : window.innerWidth + }; + + vc_hint_div = null; + vc_flag_hint_exist = false; + vc_flag_conflict = false; +} + +/** + * vc_hide_hints function hides the hints from the screen. Hints elements still exist + */ +function vc_hide_hints() { + /* Custom pre process to hide hints */ + vc_custom_pre_hide_hints(); + + for (var i = 0; i < vc_visible_hints.length; i++) { + var hint = vc_visible_hints[i]; + if (hint.span.classList.contains('vc_tooltip_focus') == false) { + hint.span.style.display = 'none'; + hint.span.style.top = 0; + hint.span.style.left = 0; + } + } +} + +/** + * vc_remove_popup function removes popup. + * This function is safe to call if the popup doesnt exist. + */ +function vc_remove_popup() { + var node = document.querySelector('#vc_popup'); + if (null !== node && node.parentNode) { + node.parentNode.removeChild(node); + } +} + +/** + * vc_show_popup function displays a floating div over the web page with text in it for timout milliseconds + * + * @param text text to be shown on the popup + * @param timeout time for which the popup should be shown in milliseconds. If timeout is 0 popup must be deleted manually + */ +function vc_show_popup(text, timeout) { + /* remove any existing popup */ + vc_remove_popup(); + + /* overlay creates a light grey area over the whole website */ + var overlay = document.createElement('div'); + overlay.id = 'vc_popup'; + + /* vcpopup is the main popup window, that is centered over the overlay */ + var vcpopup = document.createElement('div'); + vcpopup.id = 'vc_popup_content'; + + /* content is used to display text and center it vertically inside vcpopup */ + var content = document.createElement('div'); + content.style.display = 'table-cell'; + content.style.fontFamily = '"Open Sans", "Helvetica", sans-serif'; + content.style.verticalAlign = 'middle'; + content.innerHTML = text; + + vcpopup.appendChild(content); + overlay.appendChild(vcpopup); + document.body.appendChild(overlay); + + /* if timeout is set to 0, display popup until not deleted manually */ + if (timeout !== 0) { + setTimeout(function () { + vc_remove_popup(); + }, timeout); + } + + return; +} + +/** + * vc_remove_highlight removes highlight from conflicting links + * + * @param selectedNum the number of target tooltip + */ +function vc_remove_highlight(selectedNum) { + if (selectedNum) { + var high = document.querySelectorAll('#vc_highlight'); + for (var cn = 0; cn < high.length; cn++) { + if ((cn + 1) != selectedNum) { + high[cn].parentNode.removeChild(high[cn]); + } + } + } else { + var highlightArea = document.querySelector('#vc_highlight_area'); + if (highlightArea) { + highlightArea.parentNode.removeChild(highlightArea); + } + } +} + +/** + * vc_result_highlight creates highlight box over elem + * + * @param elem element to be highlighted + */ +function vc_result_highlight(elem) { + var doc = elem.ownerDocument, + rect; + + if (vc_is_visible(elem) == false && elem.querySelector('img')) { + rect = elem.querySelector('img').getClientRects()[0]; + } else { + rect = elem.getClientRects()[0]; + } + + if (!rect) { + return; + } + + vc_high_div = document.querySelector('#vc_highlight_area'); + + if (!vc_high_div) { + vc_high_div = doc.createElement('div'); + vc_high_div.id = 'vc_highlight_area'; + doc.body.appendChild(vc_high_div); + } + + var high_span = doc.createElement('div'); + high_span.id = 'vc_highlight'; + + /* top left position of the element */ + var leftPos = (rect.left > vc_scr.left ? rect.left : vc_scr.left); + var topPos = (rect.top > vc_scr.top ? rect.top : vc_scr.top); + + /* adjust top left position with page scroll */ + topPos += window.pageYOffset - 3; + leftPos += window.pageXOffset - 3; + + high_span.style.left = leftPos + 'px'; + high_span.style.top = topPos + 'px'; + high_span.style.width = rect.width + 'px'; + high_span.style.height = (rect.height + 6) + 'px'; + + /* if element's dimensions are 0x0 create highlight for it's children */ + if (rect.width === 0 || rect.height === 0) { + var height = topPos + rect.height; + var width = leftPos + rect.width; + for (var i = 0; i < elem.children.length; i++) { + var child = elem.children[i]; + var chRect = child.getClientRects()[0]; + if (!chRect) { + continue; + } + if (chRect.top + chRect.height > height) { + height = chRect.top + chRect.height; + } + if (chRect.left + chRect.width > width) { + width = chRect.left + chRect.width; + } + } + high_span.style.height = height - topPos + 'px'; + high_span.style.width = width - leftPos + 'px'; + } + + vc_high_div.appendChild(high_span); +} + +/** + * vc_generate_conflict_hints function creates hints for all conflicting highlighted hints + */ +function vc_generate_conflict_hints() { + vc_hint_div = document.querySelector('#vc_tooltip_area'); + + if (!vc_hint_div) { + vc_hint_div = document.createElement('div'); + vc_hint_div.id = 'vc_tooltip_area'; + document.body.insertBefore(vc_hint_div, document.body.firstChild); + } + + vc_scr = { + top : 0, + left : 0, + bottom : window.innerHeight, + right : window.innerWidth + }; + + /* Conflict links */ + var conflicts = vc_conflict_links; + + for (var i = 0; i < conflicts.length; i++) { + var elem = conflicts[i]; + var rect; + + vc_result_highlight(elem); + if (vc_is_visible(elem, vc_scr) == false && elem.querySelector('img') != null) { + rect = elem.querySelector('img').getClientRects()[0]; + } else { + rect = elem.getClientRects()[0]; + } + + if (!rect) { + return; + } + + var leftPos, + topPos, + position_factor_top = 20, //[TODO]move at proper place + position_factor_left = 28, + hint = {}, + span = document.createElement('span'); + span.id = 'vc_tooltip'; + + if (rect.top < position_factor_top) { + topPos = window.pageYOffset + 'px'; + } else { + topPos = rect.top + window.pageYOffset - position_factor_top + 'px'; + } + + if (rect.left < position_factor_left) { + leftPos = window.pageXOffset + 'px'; + } else { + leftPos = rect.left + window.pageXOffset - position_factor_left + 'px'; + } + + /* tooltip style, !please keep styles sorted */ + hint.span = span; + hint.span.style.top = topPos; + hint.span.style.left = leftPos; + vc_hint_div.appendChild(hint.span); + + hint.elem = elem; + hint.child = vc_high_div.childNodes[i]; + vc_page_hints[vc_page_hints.length] = hint; + } + + vc_docs.push({ + doc : document, + start : 0, + end : vc_page_hints.length - 1 + }); + + vc_flag_hint_exist = true; + vc_flag_conflict = true; + vc_conflict_timer = setTimeout(vc_remove_hints, 5000); + + vc_show_hints(); +} + +/** + * vc_trigger_event function makes custom DOM event + * + * @param node target element taking event + * @param eventType type of the custom event + */ +function vc_trigger_event(node, eventType) { + try { + var clickEvent = new Event(eventType); + node.dispatchEvent(clickEvent); + } catch (e) { + console.log('error ouccured', e); + } +} + +/** + * vc_do_action function creates event for selected element. + * + * @param targetElem selected element + * @param numberTag number of selected tooltip + * @param value(option) text entered into selected input element + * + * @return array including coordinates of the @targetElem and the number of targetElem. + */ +function vc_do_action(targetElem, numberTag, value) { + vc_print_log('=== Inside vc_do_action'); + vc_print_log(targetElem.elem.outerHTML); + + var elem = targetElem.elem, + type = targetElem.type, + position, + effect; + + if (!targetElem) { + return; + } + + /* Create click effect element */ + effect = document.createElement('div'); + effect.id = 'vc_click_effect'; + + if (numberTag != undefined) { + position = numberTag.getBoundingClientRect(); + effect.style.left = (position.left + (position.width / 2) - 35) + 'px' + effect.style.top = (position.top + (position.height / 2) - 35) + 'px' + + numberTag.classList.add('vc_tooltip_focus'); + numberTag.style.display = 'block'; + + //conflicts + if (vc_flag_conflict) { + vc_remove_highlight(numberTag.textContent.trim()); + } + vc_hide_hints(); + } else { + position = elem.getClientRects()[0]; + effect.style.left = (position.left - 35) + 'px' + effect.style.top = (position.top - 35) + 'px' + + //text indicators + vc_result_highlight(elem); + elem.classList.add('vc_text_focus'); + + vc_text_indicators[vc_text_indicators.length] = elem; + } + + document.body.insertBefore(effect, document.body.firstChild); + + /* Occurring events */ + try { + if (type == 'input' && value != undefined) { + setTimeout(function () { + elem.value = value; + vc_trigger_event(elem, 'keyup'); + vc_trigger_event(elem, 'input'); + try { + elem.form.submit(); + } catch (e) { + console.log(e); + } + + document.body.removeChild(effect); + vc_remove_hints(); + }, 1500); + + return [0, 0, 0] + } + + setTimeout(function () { + if (vc_asr_result != undefined) { + vc_asr_result.parentElement.removeChild(vc_asr_result); + vc_asr_result = undefined; + } + if (vc_rec_result != undefined) { + vc_rec_result.parentElement.removeChild(vc_rec_result); + vc_rec_result = undefined; + } + + if (numberTag == undefined) { + elem.classList.remove('vc_text_focus'); + } + + document.body.removeChild(effect); + vc_remove_hints(); + + elem.blur(); + elem.focus(); + + vc_print_log('=== Click Finish'); + }, 2000); + + var rect = elem.getClientRects()[0], + x = rect.left + (rect.width / 2), + y = rect.top + (rect.height / 2); + if (null == rect) { + vc_print_log('[ERROR] Target element is not valid') + return [-1, 0, 0]; + } + + if (x < 0) { + x = 0; + } + if (y < 0) { + y = 0; + } + return [1, x / window.innerWidth, y / window.innerHeight]; + } catch (e) { + console.log(e); + return [-1, 0, 0]; + } +} + +/** + * vc_click function triggers click on element connected to cmd and param. Assumption is made that tooltips are numbers! + * + * @param cmd tooltip tag number from voice-control + * @param param text parameter from voice-control + */ +function vc_click(cmd, param) { + vc_print_log('== start click cmd = ' + cmd + ', param = ' + param + ', visible tags = ' + vc_visible_hints.length); + + if (vc_flag_log == true) { + if (vc_asr_result != undefined) { + vc_asr_result.innerHTML = param; + } + + setTimeout(function () { + if (vc_asr_result != undefined) { + vc_asr_result.parentElement.removeChild(vc_asr_result); + vc_asr_result = undefined; + } + if (vc_rec_result != undefined) { + vc_rec_result.parentElement.removeChild(vc_rec_result); + vc_rec_result = undefined; + } + }, 2500); + } + + /* pre-defined rule checker */ + if (vc_check_web_control(param) == true) { + vc_remove_hints(); + + return [0, 0, 0]; + } + + // if cmd has a numerical value + if (isNaN(cmd) == false) { + vc_print_log('== numbering tag click'); + + // when cmd is floating point value without parameter + // search exactly the same text + if (Number.isInteger(cmd) == false && param == '') { + for (var i = 0; i < vc_text_indicators.length; ++i) { + if (vc_text_indicators[i].textContent.trim().indexOf(cmd) != -1) { + return vc_do_action({ + elem : vc_text_indicators[i] + }); + } + } + + var list = vc_selector([cmd.toString()]); + + for (var i = 0; i < list.length; i++) { + if (list[i].childElementCount == 0 && list[i].textContent.trim().indexOf(cmd) != -1) { + return vc_do_action({ + elem : list[i] + }); + } + } + } + + // when cmd is valid integer value + if (Number.isInteger(cmd) == true && cmd <= vc_visible_hints.length) { + var hint = vc_visible_hints[cmd - 1]; + + // insert text into input element + if (hint.type === 'input' && param != '') { + return vc_do_action(hint, hint.span, param); + } else if (param == '') { + if (!vc_flag_conflict) { + vc_conflict_links = []; + vc_conflict_links.push(hint.elem); + + for (var i = 0; i < vc_text_indicators.length; ++i) { + if (vc_text_indicators[i].textContent.trim() == cmd) { + vc_conflict_links.push(vc_text_indicators[i]); + } + } + + if (vc_conflict_links.length > 1) { + vc_remove_hints(); + vc_generate_conflict_hints(); + return [vc_conflict_links.length, 0, 0]; + } + } + + return vc_do_action(hint, hint.span); + } + } + + // when there is no matched tooltip + vc_remove_hints(); + + return vc_search_text(cmd.toString().concat(' ', param).trim()); + } else { + vc_remove_hints(); + + return vc_search_text(param); + } + + return [-1, 0, 0]; +} + +/** + * vc_search_text function search the element that include same text with @param. + * + * @param param param from voice-control. + */ +function vc_search_text(param) { + /* phase 1. search full text in the webpage */ + /* first, compare with links in html documents */ + vc_print_log('=== start searching(text level)'); + + if (vc_flag_log == true) { + vc_rec_result.style.background = 'rgba(0, 200, 100, 1)'; + } + + vc_scr = { + top : 0, + left : 0, + bottom : window.innerHeight, + right : window.innerWidth + }; + + vc_conflict_links = []; + + for (var i = 0; i < vc_text_indicators.length; ++i) { + if (vc_is_visible(vc_text_indicators[i], vc_scr, true) && + vc_text_indicators[i].textContent.replace(/[ `~!‘’@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '').toLowerCase().indexOf(param.replace(/ /g, '').toLowerCase()) !== -1) { + vc_conflict_links.push(vc_text_indicators[i]); + } + } + + if (vc_conflict_links.length == 1) { + /* if there is one result */ + return vc_do_action({ + elem : vc_conflict_links[0] + }); + } else if (vc_conflict_links.length > 1) { + vc_generate_conflict_hints(); + + return [vc_conflict_links.length, 0, 0]; + } + + /* second, compare with whole text of elements in html documents */ + var resultTokenArr = param.replace(/[`~!‘’@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '').split(' '), + el = vc_selector(resultTokenArr), + preStr = '******'; + + for (var i = 0; el.length > i; i++) { + var temp = el[i]; + + if (temp.childElementCount === 0) { + var curStr = temp.textContent.replace(/[`~!‘’@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, ''); + if (curStr == preStr || vc_is_visible(temp) == false) { + continue; + } + + preStr = curStr; + + vc_conflict_links.push(temp); + } + } + + if (vc_conflict_links.length == 1) { + return vc_do_action({ + elem : vc_conflict_links[0] + }); + } else if (vc_conflict_links.length > 1) { + vc_generate_conflict_hints(); + + return [vc_conflict_links.length, 0, 0]; + } else { + /* + * vc_search_word depend on the voice control language. + * This function increase the accuracy of the searching result using word and letter level searching. + * If the language is changed, the definition of the function will be changed by reloading the .js file. + */ + temp = vc_search_word(param, true); + if (temp != undefined) { + return vc_do_action({ + elem : temp + }); + } else { + //vc_show_popup('There is no such kind of string.
(' + param + ')', 1500); + vc_remove_hints(); + + return [-1, 0, 0]; + } + } +} + +/** + * vc_scroll_event_firing function move the scroll. + * + * @param evt keyword to move the scroll. + */ +function vc_scroll_event_firing(evt) { + if (evt == 'DOWN') { + window.scrollBy(0, 500); + } else if (evt == 'UP') { + window.scrollBy(0, -500); + } else if (evt == 'TOP') { + window.scrollTo(0, 0); + } +} + +/** + * vc_print_html function print out log on vc_log_area and console for debug. + * If vc_flag_log is not true, then the logs will only print on console. + * + * @param text log text to print out. + */ +function vc_print_log(text) { + var d = new Date(); + var time = d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() + ':' + d.getMilliseconds(); + console.log(time, '[', vc_print_log.caller.name, ']', text); + + if (vc_flag_log == true) { + vc_log_area.value += time + ' [' + vc_print_log.caller.name + '] ' + text + '\n'; + } +} + +/** + * vc_print_html function print out raw source of the html document for debug. + */ +function vc_print_html() { + if (vc_html_area == undefined) { + vc_html_area = document.createElement('textarea'); + vc_html_area.disabled = true; + vc_html_area.id = 'vc_html_area'; + + document.body.appendChild(vc_html_area); + + vc_html_area.value = location.href.toString() + '\n' + document.querySelector('html').innerHTML; + } else { + document.body.removeChild(vc_html_area); + vc_html_area = undefined; + } +} + +/** + * vc_search_text function is called by voice-control-webview.cpp. + * this function call some group of functions refered to @phase. + * + * @param phase key value to call a group of functions. + * @param data function data from voice-control-webview.cpp. + */ +function vc_request_action(phase, data) { + vc_print_log('phase :' + phase + ',data :' + data); + if ('SHOW_TOOLTIP' == phase) { + clearTimeout(vc_conflict_timer); + + /* create & show tooltips to recognize the voice */ + if (!vc_flag_conflict && false == vc_flag_hint_exist) { + vc_remove_hints(); + vc_generate_hints(); + } + vc_show_hints(); + } else if ('HIDE_TOOLTIP' == phase) { + /* hide tooltips */ + if (!vc_flag_conflict) { + vc_hide_hints(); + } else { + clearTimeout(vc_conflict_timer); + vc_conflict_timer = setTimeout(vc_remove_hints, 5000); + } + } else if ('RESULT' == phase) { + /* do action by the result */ + vc_all_elem = document.querySelectorAll('body *:not(script)'); + var result = vc_correct_parameter(data); + + return vc_click(result.cmd, result.param); + } else if ('REMOVE_TOOLTIP' == phase) { + clearTimeout(vc_conflict_timer); + vc_remove_hints(); + } else { + return false; + } + return true; +} + +/** + * vc_selector function is a selector function to find the elements include whole text in @strArr. + * + * @param strArr group of strings to find elements. + * + * ex) vc_selector(['abc', 'def']); + */ +function vc_selector(strArr) { + var result = []; + for (var i = 0; i < vc_all_elem.length; i++) { + var temp = vc_all_elem[i].textContent.replace(/[`~!‘’@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '').toLowerCase(); + var j; + + for (j = 0; j < strArr.length; j++) { + if (temp.includes(strArr[j].toLowerCase()) == false) { + break; + } + } + + if (strArr.length == j && vc_is_visible(vc_all_elem[i], null, true)) { + result.push(vc_all_elem[i]); + } + } + + return result; +} + +/** + * vc_get_url_path function returns url path of the view. + * + * @return part of the url(host name + path name). it is used to read page specific script. + */ +function vc_get_url_path() { + var hostName = document.location.hostname; + var pathName = document.location.pathname; + + pathName = pathName.replace(/\//gi, '.'); + + return hostName + '/' + pathName; +} + +/** + * vc_get_conflict_status function returns the conflict status. + * + * @return 1 if vc_flag_conflict is true. Otherwise 0. + */ +function vc_get_conflict_status() { + if (vc_flag_conflict) { + return 1; + } + + return 0; +} + +/** + * vc_init function initialize some elements and styles to use voice control. + */ +function vc_init() { + /* log element initialize */ + if (vc_flag_log) { + vc_log_area.disabled = true; + vc_log_area.id = 'vc_log_area'; + vc_log_area.style.visibility = 'hidden'; + + document.body.appendChild(vc_log_area); + } + + /* add tooltip style element */ + var vc_style_node = document.createElement('style'); + var vc_style_text = document.createTextNode( + '#vc_tooltip_area {\ + left : 0px;\ + position : absolute;\ + pointer-events: none;\ + top : 0px;\ + z-index : 2147483647;\ + }\ + #vc_html_area, #vc_log_area {\ + background-color : rgba(0, 0, 0, 0.5) !important;\ + color : white !important;\ + font-size : 14px;\ + height : 600px;\ + left : 50px;\ + padding : 10px 10px 10px 10px;\ + position : fixed;\ + top : 50px;\ + width : 800px;\ + z-index : 2147483647;\ + }\ + #vc_popup_content {\ + background-color : #87cff7;\ + bottom : 0px;\ + border-radius : 20px;\ + box-shadow : 0 2px 6px rgba(0, 0, 0, 0.3), 0 3px 8px rgba(0, 0, 0, 0.2);\ + color : white;\ + display : table;\ + font-size : 1.5em;\ + height : 100px;\ + left : 0px;\ + line-height : 1.5em;\ + margin : auto;\ + opacity : 1;\ + overflow : hidden;\ + position : absolute;\ + right : 0px;\ + text-align : center;\ + top : 0px;\ + width : 500px;\ + z-index : 2147483647;\ + }\ + #vc_popup {\ + background-color : rgba(0,0,0,0.2);\ + bottom : 0px;\ + left : 0px;\ + position : fixed;\ + right : 0px;\ + top : 0px;\ + z-index : 2147483647;\ + }\ + #vc_highlight_area {\ + left : 0px;\ + position : absolute;\ + pointer-events: none;\ + top : 0px;\ + z-index : 2147483646;\ + }\ + #vc_highlight {\ + border-radius: 5px;\ + font-size : 20px;\ + padding : 0 3px;\ + position : absolute;\ + pointer-events: none;\ + word-break : normal;\ + background-color: rgba(0, 234, 255, 0.2);\ + }\ + #vc_click_effect {\ + border-radius: 70px;\ + width: 70px;\ + height: 70px;\ + pointer-events: none;\ + position: fixed;\ + z-index : 2147483647;\ + background: linear-gradient(141deg, rgba(15, 185, 175, 0.6) 0%, rgba(30, 200, 220, 0.6) 51%, rgba(45, 180, 230, 0.6) 75%);\ + }\ + .vc_number_tag_normal {\ + border : 2px solid;\ + border-color : rgba(255, 255, 255, 0);\ + border-radius : 5px;\ + box-shadow : 0px 2px 2px rgba(0, 0, 0, 0.3);\ + position: absolute;\ + pointer-events: none;\ + text-align: center;\ + font-family: "Samsung0ne600";\ + color: #ffffff;\ + z-index : 2147483647;\ + word-break : normal;\ + }\ + .vc_text_indicator_conflict_normal {\ + border: 2px solid;\ + border-color: rgba(255, 255, 255, 0);\ + border-radius: 20px;\ + box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);\ + position: absolute;\ + pointer-events: none;\ + text-align: center;\ + color: #ffffff;\ + font-family: "Samsung0ne600";\ + z-index: 2147483647;\ + }\ + .vc_tooltip_focus {\ + border-color: rgba(255, 255, 255, 1);\ + }\ + .vc_text_focus {\ + color: #ff0000 !important;\ + font-weight: bolder !important;\ + }\ + #vc_asr_result {\ + background : rgba(0, 0, 0, 0.8);\ + border-radius : 20px;\ + color : white;\ + font-size : 25px;\ + height : 35px;\ + padding-top : 10px;\ + padding-bottom : 10px;\ + position : absolute;\ + text-align : center;\ + top : 10px;\ + right : 10px;\ + width : 550px;\ + z-index : 2147483647;\ + }\ + #vc_rec_result {\ + border-radius : 40px;\ + height : 40px;\ + position : absolute;\ + top : 17px;\ + right : 510px;\ + width : 40px;\ + z-index : 2147483647;\ + }\ + .vc_tooltip_input {\ + background-color : rgba(60, 140, 250, 0.75);\ + }\ + .vc_tooltip_normal, .vc_tooltip_area {\ + background-color : rgba(60, 185, 165, 0.75);\ + }\ + .vc_tip_tier_first {\ + font-size: 22px;\ + line-height: 24px;\ + width: 32px;\ + height: 26px;\ + }\ + .vc_tip_tier_second {\ + font-size: 19px;\ + line-height: 22px;\ + width: 28px;\ + height: 22px;\ + }\ + .vc_tip_tier_third {\ + font-size: 14px;\ + line-height: 16px;\ + width: 21px;\ + height: 16px;\ + }\ + @keyframes vc_tooltip_show {\ + 0% {\ + opacity: 0;\ + }\ + 100% {\ + opacity: 1;\ + }\ + }\ + @keyframes vc_tooltip_click {\ + 0% {\ + box-shadow: 0 0 8px 6px rgba(60, 220, 180, 0), 0 0 0px 0px rgba(255,255,255,1), 0 0 0px 0px rgba(60, 220, 180, 0);\ + }\ + 10% {\ + box-shadow: 0 0 8px 6px rgba(60, 220, 180, 1), 0 0 3px 5px rgba(255,255,255,1), 0 0 6px 10px rgba(60, 220, 180, 1);\ + }\ + 100% {\ + box-shadow: 0 0 8px 6px rgba(60, 220, 180, 0), 0 0 0px 20px rgba(255,255,255,0.5), 0 0 0px 20px rgba(60, 220, 180, 0);\ + }\ + }\ + @keyframes vc_click_effect {\ + 0% {\ + transform: scale(0.3, 0.3);\ + opacity: 0;\ + }\ + 10% {\ + transform: scale(0.5, 0.5);\ + opacity: 0.5;\ + }\ + 100% {\ + transform: scale(1, 1);\ + opacity: 1;\ + }\ + }'); + vc_style_node.appendChild(vc_style_text); + document.head.appendChild(vc_style_node); + + var frames = window.frames; + + for (var i = 0; i < frames.length; i++) { + try { + var doc = frames[i].document; + var vc_frame_style_node = doc.createElement('style'); + var vc_frame_style_text = doc.createTextNode(vc_style_node.textContent); + + vc_frame_style_node.appendChild(vc_frame_style_text); + doc.head.appendChild(vc_frame_style_node); + } catch (e) { + continue; + } + } + + /* remove indicators on scroll */ + window.addEventListener('scroll', function () { //[TODO] need to remove the listener + vc_remove_hints(); + }, false); +} + +vc_init(); \ No newline at end of file diff --git a/js_custom/www.youtube.com.js b/js_custom/www.youtube.com.js new file mode 100755 index 0000000..d953234 --- /dev/null +++ b/js_custom/www.youtube.com.js @@ -0,0 +1,20 @@ +function vc_custom_pre_generate_hints() { + var player = document.querySelector('#movie_player'); + if (player != null) { + player.classList.remove('ytp-autohide'); + } +} + +function vc_custom_pre_show_hints() { + var player = document.querySelector('#movie_player'); + if (player != null) { + player.classList.remove('ytp-autohide'); + } +} + +function vc_custom_pre_hide_hints() { + var player = document.querySelector('#movie_player'); + if (player != null) { + player.classList.add('ytp-autohide'); + } +} \ No newline at end of file diff --git a/js_custom/www.youtube.com.tv.js b/js_custom/www.youtube.com.tv.js new file mode 100755 index 0000000..2857d6c --- /dev/null +++ b/js_custom/www.youtube.com.tv.js @@ -0,0 +1,39 @@ +function vc_custom_pre_generate_hints() { + var player = document.querySelector('#transport-controls'); + if (player != null) { + player.classList.remove('hidden'); + } + player = document.querySelector('.pivot-shelf-list'); + if (player != null) { + player.classList.remove('hidden'); + player.classList.add('animate-in'); + } +} + +function vc_custom_pre_show_hints() { + var player = document.querySelector('#transport-controls'); + if (player != null) { + player.classList.remove('hidden'); + } + player = document.querySelector('.pivot-shelf-list'); + if (player != null) { + player.classList.remove('hidden'); + player.classList.add('animate-in'); + } +} + +function vc_custom_pre_hide_hints() { + var player = document.querySelector('#transport-controls'); + if (player != null) { + player.classList.add('hidden'); + } + player = document.querySelector('.pivot-shelf-list'); + if (player != null) { + player.classList.add('hidden'); + } +} + +vc_custom_add_xpath_query('video-thumb'); +vc_custom_add_xpath_query('collapsed-guide-icons'); +vc_custom_add_xpath_query('guide-button-icon'); +vc_custom_add_xpath_query('icon-player'); \ No newline at end of file diff --git a/js_custom/www.youtube.com.tv_kids.js b/js_custom/www.youtube.com.tv_kids.js new file mode 100755 index 0000000..c01ce8e --- /dev/null +++ b/js_custom/www.youtube.com.tv_kids.js @@ -0,0 +1,39 @@ +function vc_custom_pre_generate_hints() { + var player = document.querySelector('.transport-controls'); + if (player != null) { + player.classList.remove('hidden'); + player.classList.add('focused'); + } + player = document.querySelector('#pivot-list'); + if (player != null) { + player.classList.remove('hidden'); + } +} + +function vc_custom_pre_show_hints() { + var player = document.querySelector('.transport-controls'); + if (player != null) { + player.classList.remove('hidden'); + player.classList.add('focused'); + } + player = document.querySelector('#pivot-list'); + if (player != null) { + player.classList.remove('hidden'); + } +} + +function vc_custom_pre_hide_hints() { + var player = document.querySelector('.transport-controls'); + if (player != null) { + player.classList.add('hidden'); + player.classList.remove('focused'); + } + player = document.querySelector('#pivot-list'); + if (player != null) { + player.classList.add('hidden'); + } +} + +vc_custom_add_xpath_query('anchor-icon'); +vc_custom_add_xpath_query('icon'); +vc_custom_add_xpath_query('scale-cont'); \ No newline at end of file diff --git a/packaging/vc-webview-js.manifest b/packaging/vc-webview-js.manifest new file mode 100755 index 0000000..a76fdba --- /dev/null +++ b/packaging/vc-webview-js.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/vc-webview-js.spec b/packaging/vc-webview-js.spec new file mode 100755 index 0000000..1769cb4 --- /dev/null +++ b/packaging/vc-webview-js.spec @@ -0,0 +1,45 @@ +Name: vc-webview-js +Summary: Web voice touch javascript source +Version: 0.0.1 +Release: 1 +Group: Graphics & UI Framework/Voice Framework +License: Flora-1.1 +Source0: %{name}-%{version}.tar.gz +Source1001: %{name}.manifest +BuildRequires: pkgconfig(libtzplatform-config) +BuildRequires: cmake + +%description +Javascript source package to run web voice touch function on web application runtime + +%prep +%setup -q +cp %{SOURCE1001} . + +%build +export LDFLAGS+="-Wl,--rpath=%{_libdir} -Wl,--hash-style=both -Wl,--as-needed,-lgcov" +rm -rf objdir +mkdir objdir + +cd objdir && cmake .. -DVERSION=%{version} \ + -DCMAKE_INSTALL_PREFIX=%{_prefix} \ + -DCMAKE_BUILD_TYPE=Debug \ + -DTZ_SYS_RO_SHARE=%TZ_SYS_RO_SHARE + +cd objdir && make %{?jobs:-j%jobs} + +%install +(cd objdir && +%make_install) + +%clean +rm -rf %{buildroot} + +%post + +%files +%license LICENSE +%manifest %{name}.manifest +%defattr(-,root,root,-) +%{TZ_SYS_RO_SHARE}/voice/vc-webview/res/js/* +%{TZ_SYS_RO_SHARE}/voice/vc-webview/res/js_custom/* \ No newline at end of file -- 2.7.4