├── .bowerrc ├── .gitignore ├── .jshintrc ├── Gruntfile.js ├── LICENSE ├── README.md ├── README_RU.md ├── bower.json ├── config.xml ├── css ├── img │ ├── buttons.png │ └── key_i.png ├── input.css ├── keyboard.css ├── legend.css ├── log.css └── voicelink.css ├── demo └── demoApp │ ├── README.md │ ├── config.xml │ ├── css │ ├── img │ │ ├── buttons.png │ │ └── key_i.png │ ├── input.css │ ├── keyboard.css │ ├── legend.css │ └── style.css │ ├── icon │ ├── lg_icon.png │ ├── sb_demo_115x95.png │ ├── sb_demo_85x70.png │ └── sb_demo_95x78.png │ ├── img │ ├── bg.jpg │ └── logo.png │ ├── index.html │ ├── js │ ├── app.js │ ├── legendTriggers.js │ ├── lib │ │ └── smartbox.js │ ├── scenes │ │ ├── input.js │ │ ├── navigation.js │ │ └── videos.js │ └── smartbox.js │ ├── philips.php │ ├── videos.js │ └── widget.info ├── dist ├── smartbox.css ├── smartbox.js └── smartbox.min.js ├── docs ├── en_input.md ├── en_keyboard.md ├── en_log.md ├── en_nav.md ├── en_nav_alg.md ├── en_nav_extended.md ├── en_platform.md ├── en_player.md ├── en_voice.md ├── img │ ├── legend.jpg │ ├── mag_11.png │ └── mag_20.png ├── nav_slides │ ├── slide1.png │ ├── slide2.png │ ├── slide3.png │ ├── slide4.png │ ├── slide5.png │ ├── slide6.png │ └── slide7.png ├── ru_input.md ├── ru_keyboard.md ├── ru_legend.md ├── ru_log.md ├── ru_mag.md ├── ru_nav.md ├── ru_nav_alg.md ├── ru_nav_extended.md ├── ru_platform.md ├── ru_player.md └── ru_voice.md ├── dune_plugin_sm_demo ├── dune_plugin.xml ├── icon.png └── main.php ├── examples ├── bootstrap.min.css ├── keyboard │ └── index.html ├── legend │ └── index.html ├── navigation │ ├── complex │ │ └── index.html │ ├── hello_world │ │ └── index.html │ ├── phantom │ │ └── index.html │ └── popup │ │ └── index.html └── player │ ├── index.html │ ├── player.css │ └── player.js ├── fonts ├── glyphicons-halflings-regular.eot ├── glyphicons-halflings-regular.svg ├── glyphicons-halflings-regular.ttf └── glyphicons-halflings-regular.woff ├── index.html ├── package.json ├── philips.php ├── src ├── libs │ ├── event_emitter.js │ ├── jquery-1.10.2.min.js │ ├── json2.js │ └── lodash.compat.min.js ├── platforms │ ├── _browser │ │ ├── player.browser.js │ │ ├── sb.platform.browser.js │ │ └── voicelink.browser.js │ ├── dune │ │ ├── player.dune.js │ │ └── sb.platform.dune.js │ ├── lg │ │ ├── player.lg.js │ │ └── sb.platform.lg.js │ ├── mag │ │ ├── player.mag.js │ │ └── sb.platform.mag.js │ ├── philips │ │ ├── player.philips.js │ │ └── sb.platform.philips.js │ └── samsung │ │ ├── localstorage.js │ │ ├── player.samsung.js │ │ ├── sb.platform.samsung.js │ │ └── voicelink.samsung.js ├── plugins │ ├── input.js │ ├── keyboard.js │ ├── legend.js │ ├── log.js │ ├── nav.js │ ├── player.js │ └── voicelink.js ├── sb.js └── sb.platform.js └── test ├── external.css ├── external.js ├── lib ├── jasmine-html.js ├── jasmine.css └── jasmine.js ├── platform.test.js ├── player.config.example.js ├── player.config.js ├── player.test.js ├── run_test.js └── voicelink.test.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "src/libs" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /node_modules/ 3 | /doc/ 4 | /nbproject/ -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": false, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": false, 9 | "immed": true, 10 | "indent": 4, 11 | "latedef": false, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": false, 18 | "strict": false, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "laxcomma": true, 22 | "laxbreak": true, 23 | "maxlen": 120, 24 | "predef": [ 25 | "window", 26 | "document", 27 | "console", 28 | "SB", 29 | "$", 30 | "$$log", 31 | "$$nav", 32 | "_", 33 | "sf", 34 | "Common" 35 | ] 36 | } -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function ( grunt ) { 2 | 3 | var globalConfig = { 4 | platform: '', 5 | defaultFiles: ['src/*.js', 'src/plugins/*.js'] 6 | }; 7 | 8 | // Project configuration. 9 | grunt.initConfig({ 10 | pkg: grunt.file.readJSON('package.json'), 11 | globalConfig : globalConfig, 12 | /* 13 | jsdoc: { 14 | dist : { 15 | src: ['src/*.js', 'src/plugins/*.js'], 16 | options: { 17 | destination: 'doc' 18 | } 19 | } 20 | }, 21 | */ 22 | jshint: { 23 | options: { 24 | jshintrc: '.jshintrc', 25 | reporter: require('jshint-stylish') 26 | }, 27 | all: [ 28 | 'src/*.js', 'src/plugins/*.js', 'src/platforms/{,*/}*.js' 29 | ] 30 | }, 31 | 32 | cssmin: { 33 | combine: { 34 | files: { 35 | 'dist/smartbox.css': ['css/*.css'] 36 | } 37 | } 38 | }, 39 | 40 | clean: { 41 | build: ['dist'] 42 | }, 43 | 44 | concat: { 45 | options: { 46 | platform: '' 47 | }, 48 | platform: { 49 | src: globalConfig.defaultFiles.concat(['src/platforms/<%= globalConfig.platform %>/*.js']), 50 | dest: 'dist/smartbox.js' 51 | }, 52 | all: { 53 | src: globalConfig.defaultFiles.concat(['src/platforms/{,*/}*.js']), 54 | dest: 'dist/smartbox.js' 55 | } 56 | }, 57 | 58 | uglify: { 59 | build: { 60 | files: { 61 | 'dist/smartbox.min.js' : 'dist/smartbox.js' 62 | } 63 | } 64 | }, 65 | 66 | watch: { 67 | all: { 68 | files: ['src/*.js', 'src/platforms/{,*/}*.js', 'src/plugins/*.js' ], 69 | tasks: ['build'] 70 | } 71 | } 72 | }); 73 | 74 | require('load-grunt-tasks')(grunt); 75 | 76 | grunt.registerTask('default', ['clean']); 77 | grunt.registerTask('build', 'Build Smartbox for platform', function ( target ) { 78 | var concatTask = 'concat:'; 79 | 80 | if (target && target !== 'all') { 81 | globalConfig.platform = target; 82 | concatTask += 'platform'; 83 | } else { 84 | concatTask += 'all'; 85 | } 86 | 87 | grunt.task.run('clean', concatTask ,'uglify', 'cssmin'); 88 | }); 89 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 SmartBox Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | * [Документация на русском](README_RU.md) 2 | 3 | # Demo 4 | * Full demo with plugins 5 | 6 | # Overview 7 | 8 | A smartbox library allows to start one application on a few smartTV platforms. 9 | 10 | Currently supported platforms: 11 | - Samsung SmartTv 2011+ 12 | - Lg SmartTv 13 | - Philips 2012+ 14 | - STB Infomir MAG200/250 ([MAG200/250 How-to](docs/ru_mag.md)). 15 | 16 | To add your own platform please see the platform [documentation](docs/en_platform.md). 17 | 18 | # Initialization 19 | 20 | Smartbox has three dependencies: 21 | - jQuery(1.8.1+) 22 | - Underscore(or lodash) 23 | - Event Emitter( Backbone or Frontbone ) for the player plugin 24 | 25 | The compiled version of the library is located in the directory [`/dist`](dist). 26 | 27 | # Library plugins 28 | 29 | * [Platforms' methods](docs/en_platform.md) 30 | * [Log console](docs/en_log.md) 31 | * [Legend](docs/ru_legend.md) 32 | * Navigation 33 | * [Initialization and methods](docs/en_nav.md) 34 | * [Algorithm](docs/en_nav_alg.md) 35 | * [Extended usage](docs/en_nav_extended.md) 36 | * [Input fields](docs/en_input.md) 37 | * [Keyboard(virtual)](docs/en_keyboard.md) 38 | * [Voice management](docs/en_voice.md) 39 | * Legend @todo_doc 40 | * [Player](docs/en_player.md) 41 | 42 | # How to use the library 43 | 44 | To use all plugins and functions of the library it's necessary to wrap a code as callback SB.ready 45 | SB.ready(function(){ 46 | // your code 47 | }); 48 | 49 | SB.ready will be executed after all callbacks jQuery.ready, $(function(){}), $.ready(function(){}); 50 | 51 | # Library's methods 52 | 53 | - isInited() - checking the library initialization. Returns true or false 54 | 55 | SB.isInited(); 56 | 57 | - ready(func) executes the code of the funtion func after the library has been initializated 58 | 59 | SB.ready(function(){ 60 | // your code 61 | }); 62 | 63 | - readyForPlatform(platform, cb) executes the code of the funtion func after the library has been initializated, 64 | if the current plaform - platform 65 | 66 | SB.readyForPlatform('samsung', function(){ 67 | // code for samsung 68 | }); 69 | 70 | # Library configuration 71 | 72 | All configurations of the library are located in the object SB.platform 73 | 74 | SB.config = { 75 | /** 76 | * Platform which will be used in case detectPlatform returns false 77 | * ex: browser, samsung, lg 78 | * @type: {String} 79 | */ 80 | defaultPlatform: 'browser' 81 | } 82 | 83 | # Autotests 84 | Jasmine autotest start 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /README_RU.md: -------------------------------------------------------------------------------- 1 | * [English documentation](README.md) 2 | 3 | # Демо 4 | * Полное демо с плагинами 5 | 6 | # Обзор 7 | 8 | Библиотека smartbox позволяет запускать одно приложение на нескольких платформах. 9 | На данный момент поддерживаются платформы: 10 | - Samsung SmartTv 2011+, 11 | - Lg SmartTv, 12 | - Philips 2012+, 13 | - STB Infomir MAG200/250 ([MAG200/250 How-to](docs/ru_mag.md)). 14 | 15 | Для добавления своей платформы смотрите [документацию по платформам](docs/ru_platform.md) 16 | 17 | # Инициализация 18 | 19 | Smartbox имеет три зависимости: 20 | - jQuery(1.8.1+) 21 | - Underscore(или lodash) 22 | - Event Emitter( Backbone или Frontbone ) для плагина плеера 23 | 24 | Собранная версия библиотеки находится в директории [`/dist`](dist). 25 | 26 | # Плагины библиотеки 27 | 28 | * [Методы отдельных платформ](docs/ru_platform.md) 29 | * [Консоль Лог](docs/ru_log.md) 30 | * [Легенда](docs/ru_legend.md) 31 | * Навигация 32 | * [Инициализация и методы](docs/ru_nav.md) 33 | * [Алгоритм](docs/ru_nav_alg.md) 34 | * [Продвинутое использование](docs/ru_nav_extended.md) 35 | * [Поля ввода](docs/ru_input.md) 36 | * [Клавиатура(виртуальная)](docs/ru_keyboard.md) 37 | * [Голосовое управление](docs/ru_voice.md) 38 | * Абстракция LocalStorage (хранение данных на клиентском устройстве) @todo_doc 39 | * [Плеер](docs/ru_player.md) 40 | 41 | # Как пользоваться библиотекой 42 | 43 | Для использования всех плагинов и функций библиотеки необходимо оборачивать код как callback SB.ready 44 | Также можно передавать коллбек в функцию SB() 45 | 46 | SB.ready(function(){ 47 | // your code 48 | }); 49 | 50 | то же самое, что и 51 | 52 | SB(function(){ 53 | // your code 54 | }); 55 | 56 | SB.ready будет выполнен после всех коллбеков jQuery.ready, $(function(){}), $.ready(function(){}); 57 | 58 | # Методы бибилиотеки 59 | 60 | - isInited() проверка инициализации библиотеки. Возвращает true или false 61 | 62 | SB.isInited(); 63 | 64 | - ready(func) выполняет код функции func после инициализации библиотеки 65 | 66 | SB.ready(function(){ 67 | // your code 68 | }); 69 | 70 | - readyForPlatform(platform, cb) выполняет код функции func после инициализации библиотеки, 71 | если текущая платформа - platform 72 | 73 | SB.readyForPlatform('samsung', function(){ 74 | // code for samsung 75 | }); 76 | 77 | также можно использовать функцию SB() 78 | 79 | SB('samsung', function(){ 80 | // code for samsung 81 | }) 82 | 83 | # Конфигурирование библиотеки 84 | 85 | Все конфигурации библиотеки находятся в объекте SB.config 86 | 87 | SB.config = {} 88 | 89 | ## `SB.config.DUID` 90 | 91 | *String*: shows which method is used to get DUID for application. By default: `real`. 92 | 93 | `real`: the method SB.Platform.getNativeDUID() is used 94 | 95 | `mac`: the method device MAC-address is used, available for LG and Samsung, 96 | 97 | `random`: a new DUID is generated each application starting 98 | 99 | `[other value]`: will be used as DUID. For example: 100 | 101 | ```js 102 | SB.config.DUID="fgsfds"; 103 | SB.ready(function(){ 104 | SB.currentPlatform.DUID;//=> "fgsfds" 105 | }); 106 | ``` 107 | 108 | # Автотесты 109 | Запуск автотестов Jasmine 110 | 111 | # Посты о Smartbox 112 | * Habr: Библиотека SmartBox для Samsung, LG, Philips и других 113 | * Habr: Создание кроссплатформенного Smart TV приложения на базе библиотеки SmartBox 114 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SmartBox", 3 | "version": "0.0.1", 4 | "homepage": "https://github.com/immosmart/smartbox", 5 | "authors": [], 6 | "description": "Cross-platform Smart TV framework", 7 | "main": "index.html", 8 | "license": "MIT", 9 | "private": true, 10 | "dependencies": { 11 | "lodash": "1.3.1", 12 | "jquery": "~1.10.0" 13 | }, 14 | "ignore": [ 15 | "**/.*", 16 | "node_modules", 17 | "bower_components", 18 | "test", 19 | "tests" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | y 4 | 5 | 6 | 7 | 8 | 9 | 0.1 10 | 11 | y 12 | y 13 | n 14 | n 15 | y 16 | y 17 | y 18 | Smartbox 19 | 20 | 1280 21 | 720 22 | 23 | IMMO 24 | info@smart-kino.com 25 | http://smart-kino.com 26 | IMMO 27 | 28 | 29 | 30 | icon/Platform_115x95.png 31 | icon/Platform_115x95.png 32 | icon/Platform_95x78.png 33 | icon/Platform_85x70.png 34 | 38 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /css/img/buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/css/img/buttons.png -------------------------------------------------------------------------------- /css/img/key_i.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/css/img/key_i.png -------------------------------------------------------------------------------- /css/input.css: -------------------------------------------------------------------------------- 1 | .smart_input-wrap { 2 | padding: 5px 10px; 3 | color: #000; 4 | font-size: 18px; 5 | position: relative; 6 | z-index: 1; 7 | height: 32px; 8 | overflow: hidden; 9 | line-height: 32px; 10 | border: 1px solid black; 11 | border-radius: 5px; 12 | } 13 | 14 | .focus .smart_input-wrap { 15 | border-color: #ff6b68; 16 | } 17 | 18 | .smart_input-container { 19 | position: relative; 20 | display: inline-block; 21 | width: 200px; 22 | vertical-align: middle; 23 | } 24 | 25 | .smart_input-text { 26 | white-space: pre; 27 | } 28 | 29 | .smart_input-cursor { 30 | border-right: 2px solid #000000; 31 | display: inline-block; 32 | height: 22px; 33 | margin-left: 1px; 34 | vertical-align: middle; 35 | white-space: pre; 36 | } 37 | 38 | .smart_input-cursor_hidden { 39 | visibility: hidden; 40 | } 41 | 42 | .smart_input-wrap_right .smart_input-text { 43 | position: absolute; 44 | right: 7px; 45 | top: 0; 46 | } 47 | 48 | .smart_input-wrap_right .smart_input-cursor { 49 | position: absolute; 50 | right: 0px; 51 | top: 5px; 52 | } -------------------------------------------------------------------------------- /css/keyboard.css: -------------------------------------------------------------------------------- 1 | 2 | *{ 3 | -webkit-user-select: none; 4 | } 5 | 6 | .keyboard_popup_wrapper { 7 | width: 416px; 8 | margin: 0 auto; 9 | } 10 | 11 | .keyboard_ru { 12 | width: 495px; 13 | } 14 | 15 | .keyboard_email { 16 | width: 455px; 17 | } 18 | 19 | .kb-wrap { 20 | position: relative; 21 | padding: 10px 5px 5px 10px; 22 | overflow: hidden; 23 | background: rgba(0,0,0,0.5); 24 | box-shadow: 0 3px 5px rgba(0, 0, 0, 0.3); 25 | border-radius: 10px; 26 | display: none; 27 | } 28 | 29 | .kbw-wrap { 30 | display: none; 31 | margin: 0 auto; 32 | } 33 | 34 | .kb-c { 35 | padding-bottom: 1px; 36 | display: none; 37 | } 38 | 39 | .kb-row { 40 | margin-bottom: 6px; 41 | height: 43px; 42 | } 43 | 44 | .kb-row:after { 45 | content: ''; 46 | clear: both; 47 | display: block; 48 | } 49 | 50 | .kbtn { 51 | float: left; 52 | width: 35px; 53 | height: 36px; 54 | text-align: center; 55 | font-size: 24px; 56 | color: #000; 57 | background: #fff; 58 | cursor: pointer; 59 | margin-right: 5px; 60 | padding-top: 7px; 61 | line-height: 31px; 62 | border-radius: 4px; 63 | } 64 | 65 | .kbtn:hover, 66 | .kbtn.focus { 67 | background: #1295e4; 68 | color: #fff; 69 | } 70 | 71 | .kbtn.nums, .kbtn.lang { 72 | display: none; 73 | } 74 | 75 | /*show lang button on multilanguage keyboards*/ 76 | .kb-multilang .kbtn.lang { 77 | display: block; 78 | } 79 | 80 | .kb-havenums .kbtn.nums { 81 | display: block; 82 | } 83 | 84 | .keyboard_fullnum .kbtn.nums { 85 | width: 115px; 86 | } 87 | 88 | .kbtn.shift, .kbtn.nums, .kbtn.lang { 89 | width: 55px; 90 | font-size: 14px; 91 | } 92 | 93 | .kbtn.shift { 94 | width: 75px; 95 | } 96 | 97 | .kbtn.delall { 98 | font-size: 16px; 99 | line-height: 18px; 100 | padding-top: 3px; 101 | height: 40px; 102 | } 103 | 104 | .kbtn.space { 105 | width: 315px; 106 | } 107 | 108 | .keyboard_ru .space { 109 | width: 395px; 110 | } 111 | 112 | .kb-multilang .space { 113 | width: 255px; 114 | } 115 | 116 | .keyboard_en .kb-multilang.kb-havenums .space { 117 | width: 195px; 118 | } 119 | 120 | .keyboard_fullnum .space{ 121 | width: 315px!important; 122 | } 123 | 124 | .keyboard_ru .kb-multilang .kbtn.space { 125 | width: 335px; 126 | } 127 | 128 | .keyboard_en .kb-havenums .space { 129 | width: 255px; 130 | } 131 | 132 | .keyboard_ru .kb-multilang.kb-havenums .kbtn.space { 133 | width: 275px; 134 | } 135 | 136 | .kbtn.complete { 137 | width: 75px; 138 | text-align: center; 139 | font-size: 16px; 140 | } 141 | 142 | .backspace_icon, .shift_icon { 143 | background: url("img/key_i.png") -27px -4px no-repeat; 144 | display: inline-block; 145 | vertical-align: top; 146 | width: 5px; 147 | height: 14px; 148 | } 149 | 150 | .backspace_icon { 151 | width: 14px; 152 | height: 6px; 153 | background-position: -2px -3px; 154 | margin-top: 10px; 155 | } 156 | 157 | .shift_icon { 158 | background-position: -36px -4px; 159 | margin-top: 6px; 160 | margin-right: 4px; 161 | } 162 | 163 | .shift_active .kbtn.shift { 164 | color: #1295e4; 165 | } 166 | 167 | .shift_active .kbtn { 168 | text-transform: uppercase; 169 | } 170 | 171 | #keyboard_overlay { 172 | display: none; 173 | position: absolute; 174 | left: 0; 175 | top: 0; 176 | width: 1280px; 177 | height: 720px; 178 | z-index: 20; 179 | } 180 | 181 | #keyboard_popup { 182 | position: absolute; 183 | z-index: 50; 184 | display: block; 185 | left: 200px; 186 | top: 200px; 187 | margin: 0 0 0 -5px; 188 | } 189 | 190 | .num_wrap { 191 | width: 226px; 192 | } 193 | .num_wrap .kbtn, 194 | .num_wrap .kbtn.complete { 195 | width: 65px; 196 | } -------------------------------------------------------------------------------- /css/legend.css: -------------------------------------------------------------------------------- 1 | .legend { 2 | position: absolute; 3 | left: 0; 4 | bottom: 0; 5 | height: 32px; 6 | padding: 2px 0 0; 7 | color: #ffffff; 8 | font-size: 16px; 9 | z-index: 20; 10 | line-height: 28px; 11 | width: 100%; 12 | display: none; 13 | } 14 | 15 | .legend-wrap { 16 | float: right; 17 | } 18 | 19 | .legend-item { 20 | float: left; 21 | position: relative; 22 | padding-left: 30px; 23 | padding-right: 10px; 24 | } 25 | 26 | .legend-item { 27 | display: none; 28 | } 29 | 30 | .legend-item.legend-item-visible { 31 | display: block; 32 | } 33 | 34 | .leg-icon { 35 | position: absolute; 36 | width: 25px; 37 | height: 28px; 38 | background: url('img/buttons.png'); 39 | left: 0; 40 | top: 1px; 41 | } 42 | 43 | .legend-clickable { 44 | cursor: pointer; 45 | } 46 | 47 | .leg-icon-green { 48 | background-position: -24px 0; 49 | } 50 | 51 | .leg-icon-yellow { 52 | background-position: -48px 0; 53 | } 54 | 55 | .leg-icon-blue { 56 | background-position: -72px 0; 57 | } 58 | 59 | .leg-icon-number { 60 | background-position: -480px 0; 61 | width: 50px; 62 | } 63 | 64 | .legend-item-number { 65 | padding-left: 60px; 66 | } 67 | 68 | .leg-icon-rew { 69 | background-position: -431px 0; 70 | } 71 | 72 | .leg-icon-play { 73 | background-position: -240px 0; 74 | } 75 | 76 | .leg-icon-pause { 77 | background-position: -288px 0; 78 | } 79 | 80 | .leg-icon-stop { 81 | background-position: -263px 0; 82 | } 83 | 84 | .leg-icon-ff { 85 | background-position: -455px 0; 86 | } 87 | 88 | .leg-icon-tools { 89 | background-position: -191px 0; 90 | } 91 | 92 | .leg-icon-left { 93 | background-position: -580px 0; 94 | } 95 | 96 | .leg-icon-right { 97 | background-position: -603px 0; 98 | } 99 | 100 | .leg-icon-up { 101 | background-position: -530px 0; 102 | } 103 | 104 | .leg-icon-down { 105 | background-position: -554px 0; 106 | } 107 | 108 | .leg-icon-leftright { 109 | background-position: -358px 0; 110 | } 111 | 112 | .leg-icon-updown { 113 | background-position: -334px 0; 114 | } 115 | 116 | .leg-icon-move { 117 | background-position: -312px 0; 118 | } 119 | 120 | .leg-icon-enter { 121 | background-position: -383px 0; 122 | } 123 | 124 | .leg-icon-ret { 125 | background-position: -407px 0; 126 | } 127 | 128 | .leg-icon-info { 129 | background-position: -168px 0; 130 | } -------------------------------------------------------------------------------- /css/log.css: -------------------------------------------------------------------------------- 1 | #log { 2 | position: absolute; 3 | left: 0; 4 | right: 0; 5 | top: 0; 6 | bottom: 0; 7 | z-index: 99; 8 | background: rgba(0, 0, 0, 0.75); 9 | display: none; 10 | color: #fff; 11 | padding: 20px; 12 | font-size: 18px; 13 | } 14 | 15 | .log_pane { 16 | display: none; 17 | } 18 | 19 | .log_content_wrap { 20 | margin: 15px; 21 | height: 500px; 22 | overflow-y: scroll; 23 | } 24 | 25 | .log_states { 26 | border-top: 2px solid #fff; 27 | padding-top: 20px; 28 | } -------------------------------------------------------------------------------- /css/voicelink.css: -------------------------------------------------------------------------------- 1 | #voice_buble 2 | { 3 | background: rgba(0,0,0,0.83); 4 | color: #fff; 5 | border: 2px solid #363737; 6 | border-radius: 20px; 7 | position: absolute; 8 | z-index: 80; 9 | left: 80px; 10 | bottom: 120px; 11 | right: 80px; 12 | padding: 0 0 15px 30px; 13 | display: none; 14 | font-size: 18px; 15 | } 16 | .voice_help_item 17 | { 18 | width: 320px; 19 | overflow: hidden; 20 | text-overflow: ellipsis; 21 | white-space: nowrap; 22 | margin-bottom: 5px; 23 | color: #fff; 24 | padding: 5px 15px 5px 25px; 25 | float: left; 26 | height: 22px; 27 | font-size: 18px; 28 | } 29 | .voice_group_head 30 | { 31 | padding-top: 17px; 32 | margin-bottom: 10px; 33 | font-size: 20px; 34 | color: #f0f0f0; 35 | clear: both; 36 | font-weight: bold; 37 | } 38 | .emul_voice_helpbar_wrap 39 | { 40 | position: absolute; 41 | left: 1300px; 42 | top: 20px; 43 | bottom: 20px; 44 | padding: 20px 0; 45 | background: rgba(0,0,0,0.8); 46 | border-radius: 10px; 47 | width: 340px; 48 | font-size: 14px; 49 | overflow: hidden; 50 | } 51 | #emul_voice_helpbar 52 | { 53 | padding: 0 20px; 54 | width: 320px; 55 | height: 100%; 56 | overflow-y: scroll; 57 | } 58 | .emul_voice_trigger 59 | { 60 | padding: 4px 10px; 61 | text-align: center; 62 | border-radius: 5px; 63 | box-shadow: 2px 2px 5px rgba(255,255,255,0.5) inset; 64 | cursor: pointer; 65 | margin-bottom: 10px; 66 | -moz-user-select: none; 67 | } 68 | .emul_voice_trigger:hover 69 | { 70 | background: #666; 71 | } 72 | .emul_voice_trigger:active { 73 | box-shadow: -2px -2px 3px rgba(255,255,255,0.5) inset; 74 | } 75 | .emul_voice_trigger.main 76 | { 77 | background: #8e0202; 78 | } 79 | .emul_voice_trigger.main:hover 80 | { 81 | background: #c60202; 82 | } -------------------------------------------------------------------------------- /demo/demoApp/README.md: -------------------------------------------------------------------------------- 1 | SmartTv-Demo-App 2 | ================ 3 | 4 | SmartTv demo app made with Smartbox https://github.com/immosmart/smartbox 5 | -------------------------------------------------------------------------------- /demo/demoApp/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | y 4 | 5 | 6 | 7 | 8 | 9 | 0.1 10 | 11 | y 12 | y 13 | n 14 | n 15 | y 16 | y 17 | y 18 | SmartBox DemoApp 19 | 20 | 1280 21 | 720 22 | 23 | Smart 24 | info@example.com 25 | https://github.com/immosmart/smartbox 26 | Smart 27 | 28 | 29 | 30 | icon/sb_demo_115x95.png 31 | icon/sb_demo_115x95.png 32 | icon/sb_demo_95x78.png 33 | icon/sb_demo_85x70.png 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /demo/demoApp/css/img/buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/demo/demoApp/css/img/buttons.png -------------------------------------------------------------------------------- /demo/demoApp/css/img/key_i.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/demo/demoApp/css/img/key_i.png -------------------------------------------------------------------------------- /demo/demoApp/css/input.css: -------------------------------------------------------------------------------- 1 | .smart_input-wrap { 2 | padding: 5px 10px; 3 | color: #000; 4 | font-size: 18px; 5 | position: relative; 6 | z-index: 1; 7 | height: 32px; 8 | overflow: hidden; 9 | line-height: 32px; 10 | border: 1px solid black; 11 | border-radius: 5px; 12 | background: #fff; 13 | } 14 | 15 | .focus .smart_input-wrap { 16 | border-color: #ff6b68; 17 | } 18 | 19 | .smart_input-container { 20 | position: relative; 21 | display: inline-block; 22 | width: 300px; 23 | vertical-align: middle; 24 | } 25 | 26 | .smart_input-text { 27 | white-space: pre; 28 | } 29 | 30 | .smart_input-cursor { 31 | border-right: 2px solid #000000; 32 | display: inline-block; 33 | height: 22px; 34 | margin-left: 1px; 35 | vertical-align: middle; 36 | white-space: pre; 37 | } 38 | 39 | .smart_input-cursor_hidden { 40 | visibility: hidden; 41 | } 42 | 43 | .smart_input-wrap_right .smart_input-text { 44 | position: absolute; 45 | right: 7px; 46 | top: 0; 47 | } 48 | 49 | .smart_input-wrap_right .smart_input-cursor { 50 | position: absolute; 51 | right: 0px; 52 | top: 5px; 53 | } -------------------------------------------------------------------------------- /demo/demoApp/css/keyboard.css: -------------------------------------------------------------------------------- 1 | /* 2 | *{ 3 | -webkit-user-select: none; 4 | }*/ 5 | 6 | .keyboard_popup_wrapper { 7 | width: 416px; 8 | margin: 0 auto; 9 | } 10 | 11 | .keyboard_ru { 12 | width: 495px; 13 | } 14 | 15 | .keyboard_email { 16 | width: 455px; 17 | } 18 | 19 | .kb-wrap { 20 | position: relative; 21 | padding: 10px 5px 5px 10px; 22 | overflow: hidden; 23 | background: rgba(0,0,0,0.5); 24 | box-shadow: 0 3px 5px rgba(0, 0, 0, 0.3); 25 | border-radius: 10px; 26 | display: none; 27 | } 28 | 29 | .kbw-wrap { 30 | display: none; 31 | margin: 0 auto; 32 | } 33 | 34 | .kb-c { 35 | padding-bottom: 1px; 36 | display: none; 37 | } 38 | 39 | .kb-row { 40 | margin-bottom: 6px; 41 | height: 43px; 42 | } 43 | 44 | .kb-row:after { 45 | content: ''; 46 | clear: both; 47 | display: block; 48 | } 49 | 50 | .kbtn { 51 | float: left; 52 | width: 35px; 53 | height: 36px; 54 | text-align: center; 55 | font-size: 24px; 56 | color: #000; 57 | background: #fff; 58 | cursor: pointer; 59 | margin-right: 5px; 60 | padding-top: 7px; 61 | line-height: 31px; 62 | border-radius: 4px; 63 | } 64 | 65 | .kbtn:hover, 66 | .kbtn.focus { 67 | background: #1295e4; 68 | color: #fff; 69 | } 70 | 71 | .kbtn.nums, .kbtn.lang { 72 | display: none; 73 | } 74 | 75 | /*show lang button on multilanguage keyboards*/ 76 | .kb-multilang .kbtn.lang { 77 | display: block; 78 | } 79 | 80 | .keyboard_ru .kb-multilang .kbtn.space { 81 | width: 335px; 82 | } 83 | 84 | .keyboard_ru .kb-multilang.kb-havenums .kbtn.space { 85 | width: 275px; 86 | } 87 | 88 | .kb-havenums .kbtn.nums { 89 | display: block; 90 | } 91 | 92 | .keyboard_fullnum .kbtn.nums { 93 | width: 115px; 94 | } 95 | 96 | .kbtn.shift, .kbtn.nums, .kbtn.lang { 97 | width: 55px; 98 | font-size: 14px; 99 | } 100 | 101 | .kbtn.shift { 102 | width: 75px; 103 | } 104 | 105 | .kbtn.delall { 106 | font-size: 16px; 107 | line-height: 18px; 108 | padding-top: 3px; 109 | height: 40px; 110 | } 111 | 112 | .kbtn.space { 113 | width: 255px; 114 | } 115 | 116 | .kb-multilang.kb-havenums .space { 117 | width: 195px; 118 | } 119 | 120 | .keyboard_fullnum .space{ 121 | width: 315px!important; 122 | } 123 | 124 | .kbtn.complete { 125 | width: 75px; 126 | text-align: center; 127 | font-size: 16px; 128 | } 129 | 130 | .backspace_icon, .shift_icon { 131 | background: url("img/key_i.png") -27px -4px no-repeat; 132 | display: inline-block; 133 | vertical-align: top; 134 | width: 5px; 135 | height: 14px; 136 | } 137 | 138 | .backspace_icon { 139 | width: 14px; 140 | height: 6px; 141 | background-position: -2px -3px; 142 | margin-top: 10px; 143 | } 144 | 145 | .shift_icon { 146 | background-position: -36px -4px; 147 | margin-top: 6px; 148 | margin-right: 4px; 149 | } 150 | 151 | .shift_active .kbtn.shift { 152 | color: #1295e4; 153 | } 154 | 155 | .shift_active .kbtn { 156 | text-transform: uppercase; 157 | } 158 | 159 | #keyboard_overlay { 160 | display: none; 161 | position: absolute; 162 | left: 0; 163 | top: 0; 164 | width: 1280px; 165 | height: 720px; 166 | z-index: 20; 167 | } 168 | 169 | #keyboard_popup { 170 | position: absolute; 171 | z-index: 50; 172 | display: block; 173 | left: 200px; 174 | top: 200px; 175 | margin: 0 0 0 -5px; 176 | } 177 | 178 | .num_wrap { 179 | width: 226px; 180 | } 181 | .num_wrap .kbtn, 182 | .num_wrap .kbtn.complete { 183 | width: 65px; 184 | } -------------------------------------------------------------------------------- /demo/demoApp/css/legend.css: -------------------------------------------------------------------------------- 1 | .legend { 2 | position: absolute; 3 | left: 0; 4 | bottom: 0; 5 | height: 32px; 6 | padding: 2px 0 0; 7 | color: #ffffff; 8 | font-size: 16px; 9 | z-index: 20; 10 | line-height: 28px; 11 | width: 100%; 12 | display: none; 13 | } 14 | 15 | .legend-wrap { 16 | float: right; 17 | } 18 | 19 | .legend-item { 20 | float: left; 21 | position: relative; 22 | padding-left: 30px; 23 | padding-right: 10px; 24 | } 25 | 26 | .legend-item { 27 | display: none; 28 | } 29 | 30 | .legend-item.legend-item-visible { 31 | display: block; 32 | } 33 | 34 | .leg-icon { 35 | position: absolute; 36 | width: 25px; 37 | height: 28px; 38 | background: url('img/buttons.png'); 39 | left: 0; 40 | top: 1px; 41 | } 42 | 43 | .legend-clickable { 44 | cursor: pointer; 45 | } 46 | 47 | .leg-icon-green { 48 | background-position: -24px 0; 49 | } 50 | 51 | .leg-icon-yellow { 52 | background-position: -48px 0; 53 | } 54 | 55 | .leg-icon-blue { 56 | background-position: -72px 0; 57 | } 58 | 59 | .leg-icon-number { 60 | background-position: -480px 0; 61 | width: 50px; 62 | } 63 | 64 | .legend-item-number { 65 | padding-left: 60px; 66 | } 67 | 68 | .leg-icon-rew { 69 | background-position: -431px 0; 70 | } 71 | 72 | .leg-icon-play { 73 | background-position: -240px 0; 74 | } 75 | 76 | .leg-icon-pause { 77 | background-position: -288px 0; 78 | } 79 | 80 | .leg-icon-stop { 81 | background-position: -263px 0; 82 | } 83 | 84 | .leg-icon-ff { 85 | background-position: -455px 0; 86 | } 87 | 88 | .leg-icon-tools { 89 | background-position: -191px 0; 90 | } 91 | 92 | .leg-icon-left { 93 | background-position: -580px 0; 94 | } 95 | 96 | .leg-icon-right { 97 | background-position: -603px 0; 98 | } 99 | 100 | .leg-icon-up { 101 | background-position: -530px 0; 102 | } 103 | 104 | .leg-icon-down { 105 | background-position: -554px 0; 106 | } 107 | 108 | .leg-icon-leftright { 109 | background-position: -358px 0; 110 | } 111 | 112 | .leg-icon-updown { 113 | background-position: -334px 0; 114 | } 115 | 116 | .leg-icon-move { 117 | background-position: -312px 0; 118 | } 119 | 120 | .leg-icon-enter { 121 | background-position: -383px 0; 122 | } 123 | 124 | .leg-icon-ret { 125 | background-position: -407px 0; 126 | } 127 | 128 | .leg-icon-info { 129 | background-position: -168px 0; 130 | } -------------------------------------------------------------------------------- /demo/demoApp/css/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 1280px; 3 | height: 720px; 4 | position: relative; 5 | overflow: hidden; 6 | margin: 0; 7 | padding: 0; 8 | color: #fff; 9 | font-family: Helvetica, Arial, Verdana, Sans-Serif; 10 | } 11 | 12 | h2 { 13 | margin: 5px 0; 14 | font-size: 20px; 15 | font-weight: normal; 16 | } 17 | 18 | .bg { 19 | position: absolute; 20 | left : 0; 21 | top: 0; 22 | width: 100%; 23 | height: 100%; 24 | background: url(../img/bg.jpg); 25 | z-index: -1; 26 | } 27 | 28 | .wrap { 29 | height: 100%; 30 | position: relative; 31 | z-index: 2; 32 | } 33 | 34 | #smart_player { 35 | width: 1280px; 36 | height: 720px; 37 | z-index: 0; 38 | } 39 | 40 | #log { 41 | position: absolute; 42 | left: 0; 43 | top: 0; 44 | width: 100%; 45 | height: 100%; 46 | color:#fff; 47 | background: rgba(0, 0, 0, 0.8); 48 | z-index: 10; 49 | display: none; 50 | } 51 | 52 | .menu, .legend, 53 | .scene { 54 | background: #2a2f36; /*for old tvs*/ 55 | background: rgba(42,47,54,0.85); 56 | } 57 | 58 | .menu, .scenes-wrapper { 59 | top: 0; 60 | bottom: 35px; /*legend height*/ 61 | } 62 | 63 | .menu { 64 | width: 250px; 65 | position: absolute; 66 | } 67 | 68 | .menu-items { 69 | list-style: none; 70 | padding: 0; 71 | } 72 | 73 | .menu-item, 74 | .video-item { 75 | color: #fff; 76 | height: 50px; 77 | line-height: 50px; 78 | font-size: 20px; 79 | cursor: pointer; 80 | } 81 | 82 | .menu-item { 83 | border-left: 15px solid #fff; 84 | padding-left: 10px; 85 | } 86 | 87 | .menu-item_green { 88 | border-color: #61c791; 89 | } 90 | 91 | .menu-item_green.focus, 92 | .video-item.focus { 93 | background: #61c791; 94 | } 95 | 96 | .menu-item_blue { 97 | border-color: #31797d; 98 | } 99 | 100 | .menu-item_blue.focus { 101 | background: #31797d; 102 | } 103 | 104 | .menu-item_red { 105 | border-color: #f23c55; 106 | } 107 | 108 | .menu-item_red.focus { 109 | background: #f23c55; 110 | } 111 | 112 | .video-item { 113 | padding-left: 35px; 114 | } 115 | 116 | .logo { 117 | margin: 10px 0 20px 15px; 118 | width: 153px; 119 | height: 30px; 120 | background: url(../img/logo.png) no-repeat; 121 | } 122 | 123 | .scenes-wrapper { 124 | position: absolute; 125 | left: 250px; 126 | width: 1030px; /*body width - left*/ 127 | } 128 | 129 | .scene { 130 | height: 100%; 131 | position: relative; 132 | display: none; 133 | overflow: hidden; 134 | } 135 | 136 | .scene_video { 137 | width: 380px; 138 | } 139 | 140 | .button { 141 | position: absolute; 142 | width: 80px; 143 | height: 80px; 144 | font-size: 38px; 145 | line-height: 80px; 146 | background: #e0ffb3; 147 | /*-webkit-border-radius: 10px; 148 | -moz-border-radius: 10px;*/ 149 | border-radius: 10px; 150 | text-align: center; 151 | } 152 | 153 | .button.focus { 154 | background: #f23c55; 155 | } 156 | 157 | .button-a { 158 | left: 350px; 159 | top: 140px; 160 | } 161 | 162 | .button-b { 163 | left: 550px; 164 | top: 250px; 165 | } 166 | 167 | .button-c { 168 | left: 350px; 169 | top: 400px; 170 | } 171 | 172 | .scene_input { 173 | padding: 0 50px; 174 | } 175 | 176 | .input-example { 177 | margin: 20px 10px; 178 | } 179 | 180 | .input-val { 181 | display: inline-block; 182 | margin-left: 10px; 183 | } 184 | 185 | .navigation-items { 186 | padding: 0; 187 | list-style-type: none; 188 | overflow: hidden; 189 | } 190 | 191 | .navigation-item { 192 | float: left; 193 | margin: 20px; 194 | width: 200px; 195 | height: 250px; 196 | background: #31797d; 197 | font-size: 36px; 198 | text-align: center; 199 | line-height: 250px; 200 | } 201 | 202 | .navigation-item.focus { 203 | background: #f23c55; 204 | } 205 | 206 | .navigation-info { 207 | text-align: center; 208 | font-size: 20px; 209 | } 210 | 211 | .legend { 212 | height: 35px; 213 | padding: 0; 214 | } -------------------------------------------------------------------------------- /demo/demoApp/icon/lg_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/demo/demoApp/icon/lg_icon.png -------------------------------------------------------------------------------- /demo/demoApp/icon/sb_demo_115x95.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/demo/demoApp/icon/sb_demo_115x95.png -------------------------------------------------------------------------------- /demo/demoApp/icon/sb_demo_85x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/demo/demoApp/icon/sb_demo_85x70.png -------------------------------------------------------------------------------- /demo/demoApp/icon/sb_demo_95x78.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/demo/demoApp/icon/sb_demo_95x78.png -------------------------------------------------------------------------------- /demo/demoApp/img/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/demo/demoApp/img/bg.jpg -------------------------------------------------------------------------------- /demo/demoApp/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/demo/demoApp/img/logo.png -------------------------------------------------------------------------------- /demo/demoApp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | Smartbox demo 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 | 41 | 42 |
43 |
44 |
45 |
46 |

Standart input

47 | 48 | 49 |
50 | Input value: 51 |
52 |
53 |
54 |

Input with email keyboard

55 | 56 |
57 |
58 |

Input with num keyboard and maximum 4 signs

59 | 60 |
61 |
62 |
63 | 73 | 74 |
75 |
76 |
77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /demo/demoApp/js/app.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | 4 | 5 | window.App = { 6 | currentScene: null, 7 | scenes: {}, 8 | isShown: true, 9 | 10 | initialize: function () { 11 | this.$wrap = $('.wrap'); 12 | 13 | $$legend.show(); 14 | 15 | this.setEvents(); 16 | 17 | // start navigation 18 | $$nav.on(); 19 | }, 20 | 21 | setEvents: function () { 22 | var self = this, 23 | $bg = $('.bg'); 24 | 25 | // click on menu item 26 | $('.menu').on('click', '.menu-item', function ( e ) { 27 | var scene = e.currentTarget.getAttribute('data-content'); 28 | self.showContent(scene); 29 | }); 30 | 31 | $(document.body).on({ 32 | // on keyboard 'd' by default 33 | 'nav_key:blue': _.bind(this.toggleView, this), 34 | 35 | // remote events 36 | 'nav_key:stop': function () { 37 | Player.stop(); 38 | }, 39 | 'nav_key:pause': function () { 40 | Player.togglePause(); 41 | }, 42 | 'nav_key:exit': function(){ 43 | SB.exit(); 44 | } 45 | }); 46 | 47 | // toggling background when player start/stop 48 | Player.on('ready', function () { 49 | $bg.hide(); 50 | $$log('player ready'); 51 | }); 52 | Player.on('stop', function () { 53 | $bg.show(); 54 | $$log('player stop'); 55 | }); 56 | 57 | 58 | }, 59 | 60 | toggleView: function () { 61 | if (this.isShown) { 62 | this.$wrap.hide(); 63 | $$legend.hide(); 64 | } else { 65 | this.$wrap.show(); 66 | $$legend.show(); 67 | } 68 | this.isShown = !this.isShown; 69 | }, 70 | 71 | showContent: function ( scene ) { 72 | var cur = this.currentScene, 73 | newScene = this.scenes[scene]; 74 | 75 | if ( cur !== newScene ) { 76 | if ( !newScene ) { 77 | $$error('Scene ' + scene + ' doesn\'t exist'); 78 | } else { 79 | if ( cur ) { 80 | cur.hide(); 81 | } 82 | newScene.show(); 83 | this.currentScene = newScene; 84 | } 85 | } 86 | } 87 | }; 88 | 89 | // main app initialize when smartbox ready 90 | SB(_.bind(App.initialize, App)); 91 | })(); -------------------------------------------------------------------------------- /demo/demoApp/js/legendTriggers.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | "use strict"; 3 | 4 | 5 | var $body = $(document.body), 6 | keys = window.$$legend.keys; 7 | 8 | $('.menu-item').on({ 9 | 'nav_focus': function () { 10 | keys.enter('Show content') 11 | }, 12 | 'nav_blur': function () { 13 | keys.enter('') 14 | } 15 | }); 16 | 17 | $body.on('nav_focus', '.input-item', function() { 18 | keys.enter('Show keyboard'); 19 | keys.number('Num input'); 20 | }); 21 | 22 | $body.on('nav_blur', '.input-item', function() { 23 | keys.enter(''); 24 | keys.number(''); 25 | }); 26 | 27 | $body.on('nav_focus', '#keyboard_popup', function () { 28 | 29 | keys.enter('Input'); 30 | keys.number('Num input'); 31 | keys.red('Remove symbol'); 32 | keys.green('Complete'); 33 | keys.ret('Hide keyboard'); 34 | }); 35 | 36 | $body.on('nav_blur', '#keyboard_popup', function () { 37 | 38 | keys.enter(''); 39 | keys.number(''); 40 | keys.red(''); 41 | keys.green(''); 42 | keys.ret(''); 43 | }); 44 | 45 | $body.on('nav_focus', '.navigation-item', function() { 46 | keys.move('Navigate'); 47 | }); 48 | 49 | $body.on('nav_blur', '.navigation-item', function() { 50 | keys.move(''); 51 | }); 52 | 53 | $body.on('nav_focus', '.video-item', function () { 54 | keys.enter('Play video'); 55 | }); 56 | 57 | $body.on('nav_blur', '.video-item', function () { 58 | keys.enter(''); 59 | }); 60 | }); -------------------------------------------------------------------------------- /demo/demoApp/js/scenes/input.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var _inited; 4 | 5 | window.App.scenes.input = { 6 | init: function () { 7 | this.$el = $('.js-scene-input'); 8 | 9 | var $valueText = this.$el.find('.js-input-1-val'); 10 | 11 | this.$el.find('.js-input-1') 12 | .on('change',function () { 13 | $valueText.html(this.value); 14 | }) 15 | .SBInput({ 16 | keyboard: { 17 | type: 'fulltext_ru_nums' 18 | } 19 | }); 20 | 21 | this.$el.find('.js-input-2').SBInput({ 22 | keyboard: { 23 | type: 'email' 24 | } 25 | }); 26 | 27 | this.$el.find('.js-input-3').SBInput({ 28 | keyboard: { 29 | type: 'num' 30 | }, 31 | max: 4 32 | }); 33 | 34 | 35 | _inited = true; 36 | }, 37 | show: function () { 38 | if (!_inited) { 39 | this.init(); 40 | } 41 | 42 | this.$el.show(); 43 | }, 44 | hide: function () { 45 | this.$el.hide(); 46 | } 47 | } 48 | })(); -------------------------------------------------------------------------------- /demo/demoApp/js/scenes/navigation.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var _inited; 4 | 5 | window.App.scenes.navigation = { 6 | init: function () { 7 | var $info; 8 | 9 | this.$el = $('.js-scene-navigation'); 10 | 11 | $info = this.$el.find('.navigation-info'); 12 | 13 | this.$el 14 | .find('.navigation-item') 15 | .on( 16 | { 17 | 'nav_focus': function () { 18 | $info.html('Item with text "' + this.innerHTML + '" focused'); 19 | }, 20 | 'nav_blur': function () { 21 | $info.html(''); 22 | } 23 | }); 24 | 25 | _inited = true; 26 | }, 27 | show: function () { 28 | if (!_inited) { 29 | this.init(); 30 | } 31 | 32 | this.$el.show(); 33 | }, 34 | hide: function () { 35 | this.$el.hide(); 36 | } 37 | } 38 | })(); -------------------------------------------------------------------------------- /demo/demoApp/js/scenes/videos.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | var _inited; 4 | _.templateSettings.interpolate = /\{\{([\s\S]+?)\}\}/g; 5 | 6 | var itemHtml = _.template(''); 7 | 8 | window.App.scenes.video = { 9 | 10 | init: function () { 11 | this.$el = $('.js-scene-video'); 12 | 13 | this.$el.on('click', '.video-item', this.onItemClick); 14 | 15 | this.renderItems(App.videos); 16 | 17 | _inited = true; 18 | }, 19 | 20 | show: function () { 21 | if (!_inited) { 22 | this.init(); 23 | } 24 | 25 | this.$el.show(); 26 | }, 27 | 28 | hide: function () { 29 | this.$el.hide(); 30 | }, 31 | 32 | // handler for click event 33 | onItemClick: function (e) { 34 | var url = e.currentTarget.getAttribute('data-url'); 35 | Player.play({ 36 | url: url, 37 | type: e.currentTarget.getAttribute('data-type') 38 | }); 39 | }, 40 | 41 | // showing items from videos.js 42 | renderItems: function (items) { 43 | var html = ''; 44 | 45 | // console.log(items, itemHtml.toString()) 46 | for ( var i = 0, len = items.length; i < len; i++ ) { 47 | html += itemHtml(items[i]); 48 | } 49 | 50 | this.$el 51 | .empty() 52 | .html(html); 53 | } 54 | 55 | } 56 | })(); -------------------------------------------------------------------------------- /demo/demoApp/philips.php: -------------------------------------------------------------------------------- 1 | ' + 33 | '
' + 34 | '' + 35 | '' + 36 | '
' + 37 | '', 38 | 39 | // main container class 40 | elClass: 'smart_input-container', 41 | wrapperClass: 'smart_input-wrap', 42 | cursorClass: 'smart_input-cursor', 43 | textClass: 'smart_input-text' 44 | }, 45 | 46 | // using native keyboard without virtual 47 | directKeyboardInput: true, 48 | 49 | // max symbols in input (0 if unlimited) 50 | max: 0, 51 | 52 | // next input element 53 | next: null 54 | } 55 | 56 | # Plugin's methods 57 | 58 | Method can be called after plugin initialisation 59 | 60 | 61 | - startBlink the start of a cursor blinking 62 | 63 | $(el).SBInput('startBlink'); 64 | 65 | - stopBlink to stop a cursor blinking 66 | 67 | $(el).SBInput('stopBlink'); 68 | 69 | - showKeyboard to display a keyboard 70 | 71 | $(el).SBInput('showKeyboard'); 72 | 73 | - hideKeyboard to hide a keyboard 74 | 75 | $(el).SBInput('hideKeyboard'); 76 | 77 | - changeKeyboard change current keyboard on input 78 | 79 | var keyboardOpt = { 80 | type: 'fulltext_ru', 81 | firstLayout: 'ru' 82 | }, 83 | $(el).SBInput('changeKeyboard', keyboardOpt); 84 | 85 | - setText set text in input 86 | 87 | $(el).SBInput('setText', 'text in input'); 88 | 89 | # Cursor blinking 90 | 91 | The cursor blinking implies the class adding up/deletion to the element cursor 92 | 93 | The class name is defined as: cursorClass + '_hidden' 94 | 95 | # Text formatting in an input 96 | 97 | If the function formatText is assigned in the options then it will be called after every printed symbol. 98 | The function should return a text for the input. 99 | 100 | 101 | # Input events 102 | 103 | - 'keyboard_show' 104 | - 'keyboard_hide' 105 | - 'keyboard_cancel' 106 | - 'keyboard_complete' 107 | 108 | # How to get to know the input's value? 109 | 110 | After the input's initialisation on the element 111 | 112 | $(el).SBInput(options); 113 | 114 | The value can be got to know for the element 115 | 116 | el.value; 117 | $(el).val(); 118 | 119 | -------------------------------------------------------------------------------- /docs/en_keyboard.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | The plugin allows to show a virtual keyboard on the screen. 4 | 5 | # Initialisation 6 | 7 | SBKeyboard - jQuery plugin which can be called on any jQuery element. 8 | The first plugin initialisation occurs in the following way: 9 | 10 | $(el).SBKeyboard(options); 11 | 12 | options = { 13 | // keyboard preset 14 | type: 'en', 15 | 16 | // first layout to show 17 | firstLayout: 'en' 18 | } 19 | 20 | # Plugin's methods 21 | 22 | The method can be called after plugin has been initialisated. 23 | 24 | - Adding another keyboard to the current element 25 | 26 | $(el).SBKeyboard('addKeyboard', options); 27 | options = { 28 | type: 'en', 29 | firstLayout: 'en' 30 | } 31 | 32 | - Switching active keyboard (if some exists) on the element 33 | 34 | $(el).SBKeyboard('changeKeyboard', type); // type from the options 35 | 36 | # Plugin layouts 37 | 38 | The plugin contains some layouts by default: 39 | 40 | - en - english layout 41 | - ru - russian layout 42 | - email - layout to enter email-address 43 | - num - numeric (0-9) layout 44 | - fulltext_ru - english and russian keyboard layout 45 | 46 | # Custom layout adding 47 | 48 | To add multilang layout the array should be defined in the object window.SB.keyboardPresets 49 | 50 | window.SB.keyboardPresets[multiKeyboardLayout] = ['en', 'ru']; 51 | 52 | To add a common layout the function returning the array should be defined in the object window.SB.keyboardPresets 53 | 54 | window.SB.keyboardPresets[keyboardLayout] = function(){ 55 | return [ 56 | // first keyboard row 57 | ['q','w','e','r','t','y','u','i','o','p'], 58 | // second keyboard row 59 | ['a','s','d','f','g','h','j','k','l'], 60 | ] 61 | }; 62 | 63 | Next keys should be defined in the layout: 64 | 65 | - lang{{}} - the key of layout changing 66 | - nums{{}} - the key for swithing to the numeric keyboard (if fullnun exists in the layout list) 67 | - space{{}} - the space key 68 | - complete{{}} - entering finish 69 | 70 | Keys that can be defined by request 71 | 72 | - shift{{}} - Capitalise letters 73 | - backspace{{}} - event generating 'backspace'(ex. for one symbol deleting) 74 | - delall{{}} - event generating 'delall'(ex. for a string erase) 75 | 76 | All keys are defined in the following way: 77 | 78 | keyName{{keyText}}, keyText - html or a text inside the key's container 79 | 80 | Usage example: 81 | 82 | backspace{{}} 83 | lang{{ru}} 84 | nums{{123}} 85 | 86 | Full layout example 87 | 88 | window.SB.keyboardPresets.en = function () { 89 | return [ 90 | 'qwertyuiop'.split(''), 91 | 'asdfghjkl'.split('').concat(['backspace{{}}']), 92 | ['shift{{Shift}}'].concat('zxcvbnm'.split('')).concat( 93 | ['delall{{Del
all
}}']), 94 | ['lang{{en}}', 'nums{{123}}', 'space{{}}', 'complete{{Complete}}'] 95 | ]; 96 | }; 97 | 98 | # Keyboard events 99 | 100 | - 'type' key entering event, the key is defined in the attribute: letter event 101 | 102 | var typeLetter = function(event) { 103 | console.log(event.letter); 104 | } 105 | 106 | - 'backspace' 107 | - 'delall' 108 | - 'complete' 109 | 110 | # CSS classes 111 | 112 | .kb-multilang - Some language layouts exist 113 | .kb-havenums - Numeric keyboard exists 114 | 115 | -------------------------------------------------------------------------------- /docs/en_log.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | A log allows to display messages from the code on a screen. 4 | 5 | # Initialisation 6 | 7 | The log is initialisated after the log plugin is on. 8 | The element is created
in the DOM 9 | 10 | # Message displaying 11 | 12 | The key tools is a button by default to display messages. 13 | When you press the button the next events happen: 14 | - log displaying 15 | - boards changing 16 | - log hiding 17 | 18 | # Plugin's methods 19 | 20 | The log's object is situated in the object SB.utils.log 21 | 22 | 1) SB.utils.log.log(msg, logName) 23 | window.$$log(msg, logName) 24 | 25 | Вывод сообщения msg в панели лога logName. 26 | msg message displaying in the log's board logName 27 | 2) show(logName) 28 | 29 | The displaying of the log's board logName 30 | 3) hide() 31 | 32 | Log hiding. 33 | 34 | 4) state( msg, state, logName ) 35 | 36 | The displaying of the message in the state log's board logName 37 | State is displayed belowe the log's board by default 38 | 39 | Example of the usage: The displaying of the current playing time 40 | 5) startProfile( profileName ); 41 | stopProfile: function ( profileName ) 42 | 43 | Profiling functions. Display a result on the profiler board. 44 | 45 | -------------------------------------------------------------------------------- /docs/en_nav_alg.md: -------------------------------------------------------------------------------- 1 | # Element seaching alghorithm 2 | 3 | The alghorithm is reasonable for any direction. "Upper edge" means an edge coincident with the movement direction. For the movement up - upper edge, down - bottom edge, right - right edge, left - left edge. 4 | 5 | Example: There are navigation elements, focused element is marked in red. 6 | 7 | ![slide1](nav_slides/slide1.png) 8 | 9 | User presses the button "Up". The first occuring thing is eliminating of elements that have upper edge lower then upper edge of the focused element. 10 | 11 | ![slide2](nav_slides/slide2.png) 12 | 13 | The elements that have edges that intersect along the axis perpendicular to the direction are found. If there is any of such element exists then their priority become higher and other elements are eliminated. 14 | 15 | ![slide3](nav_slides/slide3.png) 16 | 17 | The element that has upper edge closer to the upper edge of the focused element is searched for from the last elements. 18 | 19 | ![slide4](nav_slides/slide4.png) 20 | 21 | If on the second step intersected elements haven't been found the closest element is found. 22 | 23 | ![slide5](nav_slides/slide5.png) 24 | 25 | # It's not a bug, it's a feature! 26 | 27 | ![slide6](nav_slides/slide6.png) 28 | 29 | In this example moving vertically from the element **B** to the element **A** it's possible to transit the focus but not vise versa. 30 | It happens because of the bottom edge pf the element **A** is lower then the bottom edge of the element **B**. 31 | So **A** is higher and lower **B** at the same time. 32 | 33 | -------------------------------------------------------------------------------- /docs/en_nav_extended.md: -------------------------------------------------------------------------------- 1 | The element searching algorithm can be changed if different behaviour is needed. 2 | 3 | 4 | 5 | # Attribute "user defined" 6 | 7 | ``` 8 | 9 | ``` 10 | 11 | If the attribute is defined then the navigation stops to use the intelligent element search in some cases. In the above example when the key "DOWN" has been pressed the focus transits to the element #pfl_film_watch. 12 | Edges are defined separated by commas: up, right, down, left 13 | 14 | 15 | ``` 16 | 17 | ``` 18 | 19 | In this example the key "DOWN" pressing is ignored. 20 | 21 | You can also set each side separately 22 | 23 | ``` 24 | 25 | ``` 26 | 27 | In this example the key "UP" pressing is ignored. 28 | 29 | If you set attribute by .data(), it will work too. 30 | 31 | 32 | 33 | 34 | 35 | # Attribute entry point 36 | 37 | The attribute forbidden to enter in the element with specified direction. 38 | 39 | The enter points are defined in the attribute with commas separated 0 and 1. 40 | 41 | 0 - forbiddens to enter 42 | 43 | 1 - allows to enter 44 | 45 | The edges are defined in the CSS order - top, right, bottom, left 46 | 47 | ``` 48 | 49 | ``` 50 | In this example it's impossible to enter in the element from the bottom. 51 | 52 | 53 | 54 | 55 | 56 | # Phantoms 57 | 58 | A phantom is a `nav-item` which transits the focus on the another element as soon as the focus has come to this `nav-item`. It works only for a keyboard. It's ignored when gestures managing realize. 59 | ``` 60 | 61 | ``` 62 | 63 | In this example when the focus comes to the element it's transited to the element #card_owner. 64 | 65 | ![slide7](nav_slides/slide7.png) 66 | -------------------------------------------------------------------------------- /docs/en_platform.md: -------------------------------------------------------------------------------- 1 | # Platform 2 | 3 | Device native methods are called through the base `SB` object. You can extend it and add your own new platform. 4 | After `SB.ready` a current platform has been already defined and is kept in `SB`. 5 | 6 | # Properties and methods 7 | 8 | * `SB.platformName` 9 | * `SB.keys` 10 | * `SB.platformUserAgent` 11 | * `SB.getDUID` 12 | * `SB.getNativeDUID` 13 | * `SB.getSDI` 14 | * `SB.getRandomDUID` 15 | * `SB.getMac` 16 | * `SB.setPlugins` 17 | * `SB.setData(name, value)` 18 | * `SB.getData(name)` 19 | * `SB.removeData(name)` 20 | * `New platform adding` 21 | 22 | 23 | 24 | ### `SB.platformName` 25 | 26 | *String*: platform name (samsung, lg, philips, etc...) 27 | 28 | 29 | ### `SB.keys` 30 | 31 | *Plain object*: hash containing keys codes and them names. 32 | 33 | #### Default key names 34 | 35 | BLUE 36 | CH_DOWN 37 | CH_UP 38 | DOWN 39 | ENTER 40 | EXIT 41 | FF 42 | GREEN 43 | INFO 44 | LEFT 45 | N0 46 | N1 47 | N2 48 | N3 49 | N4 50 | N5 51 | N6 52 | N7 53 | N8 54 | N9 55 | NEXT 56 | PAUSE 57 | PLAY 58 | PRECH 59 | PREV 60 | REC 61 | RED 62 | RETURN 63 | RIGHT 64 | RW 65 | SMART 66 | STOP 67 | SUBT 68 | TOOLS 69 | UP 70 | YELLOW 71 | 72 | 73 | 74 | ### `SB.platformUserAgent` 75 | 76 | *String* unique string for platform. It's compare with environment userAgent in `SB.detect` method 77 | 78 | #### Example 79 | 80 | SB.platformUserAgent === 'netcast'; // for philips 81 | SB.platformUserAgent === 'maple'; // for samsung 82 | 83 | 84 | 85 | ### `SB.getDUID` 86 | 87 | *Function* return platform DUID in case of SB.config.DUID 88 | 89 | #### Returns 90 | 91 | *String* DUID 92 | 93 | 94 | 95 | ### `SB.getNativeDUID` 96 | 97 | *Function* return native DUID if exist 98 | 99 | #### Returns 100 | 101 | *String* DUID or empty string 102 | 103 | 104 | 105 | ### `SB.getSDI` 106 | 107 | *Function* return platform SDI if exist 108 | 109 | #### Returns 110 | 111 | *String* SDI or empty string 112 | 113 | 114 | 115 | ### `SB.getRandomDUID` 116 | 117 | *Function* return random DUID 118 | 119 | #### Returns 120 | 121 | *String* generated DUID, for example: "1446dcfb2ca1091" 122 | 123 | 124 | 125 | ### `SB.getMac` 126 | 127 | *Function* return platform MAC if exist 128 | 129 | #### Returns 130 | 131 | *String* MAC or empty string 132 | 133 | 134 | 135 | ### `SB.setPlugins` 136 | 137 | *Function* initialize & start plugins specific for platform 138 | function calls automatically with smartbox initialization 139 | 140 | 141 | 142 | ### `SB.setData(name, value)` 143 | 144 | *Function* save data in platform storage 145 | 146 | #### Arguments 147 | 148 | 1. `name` *String* data name 149 | 2. `value` *(*)* data value 150 | 151 | 152 | 153 | ### `SB.getData(name)` 154 | 155 | *Function* return value from platform storage 156 | 157 | #### Arguments 158 | 159 | 1. `name` *String* data name 160 | 161 | #### Returns 162 | 163 | *(*)* data value 164 | 165 | 166 | 167 | ### `SB.removeData(name)` 168 | 169 | *Function* remove data from platform storage 170 | 171 | #### Arguments 172 | 173 | 1. `name` *String* data name 174 | 175 | 176 | 177 | ## `New platform adding` 178 | 179 | You can add a new plaform using function SB.createPlatform(platformName, cb) 180 | 181 | ``` 182 | var platformName = 'myPlatform'; 183 | SB.createPlatform(platformName, { 184 | //platform methods 185 | }); 186 | ``` 187 | 188 | 189 | -------------------------------------------------------------------------------- /docs/en_player.md: -------------------------------------------------------------------------------- 1 | # Player 2 | 3 | ###Contents 4 | 5 | `Public events` 6 | * `ready` 7 | * `bufferingBegin, bufferingEnd` 8 | * `stop` 9 | * `complete` 10 | 11 | `Public properties` 12 | * `Player.state` 13 | * `Player.videoInfo.duration` 14 | * `Player.videoInfo.currentTime` 15 | * `Player.videoInfo.width` 16 | * `Player.videoInfo.height` 17 | * `Player.usePlayerObject` 18 | 19 | `Public methods` 20 | * `Player.play(options)` 21 | * `Player.stop([silent])` 22 | * `Player.pause()` 23 | * `Player.resume()` 24 | * `Player.togglePause()` 25 | * `Player.formatTime(seconds)` 26 | * `Player.seek(seconds)` 27 | * `Player.audio.get()` 28 | * `Player.audio.set(index)` 29 | * `Player.audio.cur()` 30 | 31 | 32 | ##Public events 33 | 34 | ###`ready` 35 | 36 | 'ready' is sent when player has recieved a steam info (duration, resolution, etc) and playing starts. 37 | 38 | #### Examples 39 | 40 | ```js 41 | Player.on('ready', function(){ 42 | $('#duration').html(Player.formatTime(Player.videoInfo.duration)); 43 | }); 44 | ``` 45 | 46 | * * * 47 | 48 | 49 | ###`bufferingBegin, bufferingEnd` 50 | 51 | Events are sent when a video buffering starts and ends, weak Internet connection or after rewinding. 52 | 53 | #### Examples 54 | 55 | ```js 56 | var $loadingIndicator=$('#loading_indicator'); 57 | Player.on('bufferingBegin', function(){ 58 | $loadingIndicator.show(); 59 | }); 60 | 61 | Player.on('bufferingEnd', function(){ 62 | $loadingIndicator.hide(); 63 | }); 64 | ``` 65 | 66 | * * * 67 | 68 | 69 | ###`stop` 70 | 71 | Sends when the playback has been stopped. 72 | 73 | #### Examples 74 | 75 | ```js 76 | Player.on('stop', function(){ 77 | $videoScene.hide(); 78 | }); 79 | ``` 80 | 81 | * * * 82 | 83 | ###`complete` 84 | 85 | is sent when the end of video file is reached and playing is stopped. 86 | 87 | #### Examples 88 | 89 | ```js 90 | Player.on('complete', function(){ 91 | playNextVideo(); 92 | }); 93 | ``` 94 | 95 | * * * 96 | 97 | 98 | ##Public properties 99 | 100 | ###`Player.state` 101 | 102 | *String*: Current player condition: 103 | 104 | 1. `play`: video is playing 105 | 2. `pause`: video is paused 106 | 3. `stop`: video is stopped 107 | 108 | * * * 109 | 110 | 111 | ###`Player.videoInfo.duration` 112 | 113 | *Number*: video file duration in seconds 114 | 115 | * * * 116 | 117 | ###`Player.videoInfo.currentTime` 118 | 119 | *Number*: current playing time in seconds 120 | 121 | * * * 122 | 123 | ###`Player.videoInfo.width` 124 | 125 | *Number*: video stream width in pixels 126 | 127 | * * * 128 | 129 | ###`Player.videoInfo.height` 130 | 131 | *Number*: video stream height in pixels 132 | 133 | * * * 134 | 135 | ###`Player.usePlayerObject` 136 | 137 | *Boolean*: defines: will or won't the or sef plugin be used. 138 | 139 | *Available only for the next platform: Samsung* 140 | 141 | * * * 142 | 143 | 144 | 145 | ## Public methods 146 | 147 | ###`Player.play(options)` 148 | 149 | The video starts playing. 150 | 151 | #### Arguments 152 | `options` *Plain object*: hash containing parametrs for the starting 153 | 154 | Or 155 | 156 | `url` *String*: the path to a video 157 | 158 | #### Examples 159 | ```js 160 | Player.play({ 161 | url: "movie.mp4" 162 | }); 163 | Player.play("movie.mp4"); 164 | //Both variants are the same 165 | 166 | Player.play({ 167 | url: "movie.mp4" 168 | from: 20 169 | });// starts video from the 20 sec point 170 | ``` 171 | 172 | * * * 173 | 174 | ###`Player.stop([silent])` 175 | 176 | Stops video playing. 177 | 178 | #### Arguments 179 | 1. `silent[optional]` *Boolean*: if flag `silent` is sent, then the event `stop` isn't called 180 | 181 | 182 | #### Examples 183 | ```js 184 | Player.stop(); 185 | 186 | App.onDestroy(function(){ 187 | Player.stop(true); 188 | }); // Stops playing and allows to avoid side effects 189 | ``` 190 | 191 | * * * 192 | 193 | ###`Player.pause()` 194 | 195 | Pauses video playing. 196 | 197 | 198 | #### Examples 199 | ```js 200 | Player.pause(); 201 | ``` 202 | 203 | * * * 204 | 205 | ###`Player.resume()` 206 | 207 | Resumes video playing after pause. 208 | 209 | #### Examples 210 | ```js 211 | Player.resume(); 212 | ``` 213 | 214 | * * * 215 | 216 | ###`Player.togglePause()` 217 | 218 | Switches pause/resume according to the current condition. 219 | 220 | #### Examples 221 | ```js 222 | Player.togglePause(); 223 | ``` 224 | 225 | * * * 226 | 227 | ###`Player.formatTime(seconds)` 228 | 229 | Converts time in seconds in the srting with a type H:MM:SS 230 | 231 | #### Arguments 232 | `seconds` *Number*: the time in seconds 233 | 234 | #### Returns 235 | *String*: result string 236 | 237 | 238 | #### Examples 239 | ```js 240 | Player.formatTime(PLayer.videoInfo.duration); // => "1:30:27" 241 | ``` 242 | 243 | * * * 244 | 245 | 246 | ###`Player.seek(seconds)` 247 | 248 | The transit on the set time in seconds. 249 | 250 | #### Arguments 251 | `seconds` *Number*: the time in seconds 252 | 253 | 254 | #### Examples 255 | ```js 256 | Player.seek(20);//перейти на 20 секунду 257 | 258 | Player.seek(Player.videoInfo.currentTime + 10);//прыжок на 10 секунд вперед 259 | ``` 260 | 261 | * * * 262 | 263 | 264 | ###`Player.audio.get()` 265 | 266 | Returns an array with codes of sound tracks languages. 267 | 268 | A list with all codes you can find here 269 | http://forum.doom9.org/showthread.php?t=155762 270 | 271 | *Available only for the next platform: Samsung* 272 | 273 | #### Returns 274 | *Array*: the array with codes 275 | 276 | 277 | #### Examples 278 | ```js 279 | var tracksArray=Player.audio.get();//=> [7501171, 6448492] 280 | var currentLang=array[Player.audio.cur()];//=> 7501171 281 | var currentLangString=Strings[currentLang];//=> "Russian" 282 | ``` 283 | 284 | * * * 285 | 286 | 287 | ###`Player.audio.set(index)` 288 | 289 | Defines the sound track in copliance with the index. 290 | 291 | *Available only for the next platform: Samsung* 292 | 293 | #### Arguments 294 | 295 | `index` *Number*: sound track index 296 | 297 | 298 | #### Examples 299 | ```js 300 | Player.audio.set(0); 301 | ``` 302 | 303 | * * * 304 | 305 | ###`Player.audio.cur()` 306 | 307 | Defines the sound track in copliance with the index. 308 | 309 | *Available only for the next platform: Samsung* 310 | 311 | 312 | #### Returns 313 | *Number*: Index of the current sound track 314 | 315 | #### Examples 316 | ```js 317 | Player.audio.cur(); //=> 1 318 | ``` 319 | 320 | * * * 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | -------------------------------------------------------------------------------- /docs/en_voice.md: -------------------------------------------------------------------------------- 1 | # Voicelink 2 | 3 | jQuery plugin for working with Smart TV voice management. 4 | 5 | The principle of operation: A phrase which the user should say is set on the elements with the defined class `voicelink` in the attribute `data-voice`. The plugin generates voice management native helpbar and adds a bar with remain commands in the body, it can be called with the phrase "More". (The phrase can be changed with the plugin adjustment) 6 | The voice management is emulated in a browser by the plugin generating an additional block with buttons and available phrases. 7 | 8 | ## Example 9 | 10 | 11 | A user says "Back" - the element `#backVoiceLink` sends the event `voice` 12 | 13 | 14 | html: 15 | ``` 16 | 17 | ``` 18 | 19 | js: 20 | ``` 21 | SB.ready(function(){ 22 | $('#scene').voiceLink(); 23 | $('#backVoiceLink').bind('voice', function(){ 24 | //user said "Back" 25 | }); 26 | }); 27 | ``` 28 | 29 | ## Attributes 30 | 31 | ### data-voice="[word]" 32 | 33 | The mandatory attribute is a hint in the Samsung's helpbar or in the "bubble" 34 | Note: "Bubble" is a block with grouped hints 35 | 36 | ### data-voice-group="[group_name]" 37 | 38 | The optional attribute is a group for the hint. If the group exists then the element is shown in the bubble, if not - in the Samsung's helpbar. 39 | 40 | 41 | ### data-voice-hidden="[true]" 42 | 43 | The optional attribute show that the word can be pronauced, but the hint won't be shown. 44 | The behavior depends on the value is empty or not, because TV11 haven't got the ability to save $().data(). 45 | 46 | Example 47 | 48 | ``` 49 | data-voice-hidden="true" = data-voice-hidden="abc" = data-voice-hidden="1" = data-voice-hidden="false" = true 50 | data-voice-hidden="" = false 51 | ``` 52 | 53 | 54 | ### data-voice-force-visible="[true]" 55 | 56 | Shows the element in the helpbar, even if it's hidden and useHidden=false 57 | 58 | 59 | 60 | ### Options 61 | 62 | In the hash options it's possible to set the selector class for voice links. 63 | 64 | ``` 65 | options={ 66 | selector: '.voicelink',// the selector for the searching 67 | moreText: 'More',//Text for the "bubble" showing 68 | eventName: 'voice',//The event is sent by element событие которое отправляет элемент 69 | useHidden: false//Filtering hidden links 70 | } 71 | 72 | $('#scene').voiceLink(options); 73 | ``` 74 | 75 | It's not necessary to set `options`: then options will be set by default (as written above) 76 | 77 | ``` 78 | $('#scene').voiceLink(); 79 | ``` 80 | 81 | ``` 82 | $$voice.setup(newDefaults);//sets new values for values by default 83 | ``` 84 | 85 | ## Methods 86 | 87 | ### $$voice.say(phrase); 88 | 89 | Emulates the phrase pronouncing 90 | 91 | ### $$voice.enabled(); 92 | 93 | Defines if a device supports the voice recognition function. Returns boolean 94 | 95 | ### $$voice.setup(options); 96 | 97 | Defines default option, in options - options hash 98 | 99 | ### $$voice.save(); 100 | 101 | Saves the current condition (container and options) in the stack. 102 | 103 | ### $$voice.restore(); 104 | 105 | Restores the last saved condition from the stack and then deletes it from the stack 106 | 107 | ### $$voice.fromServer(title,callback); 108 | 109 | The voice recognition with help of Samsung's server, title - a text which the user sees, callback - the function calling after successful text recognition, receives the result inside. 110 | The prompt message is displayed in the browser. 111 | 112 | Example: 113 | 114 | ``` 115 | $$voice.fromServer("Say the word to search",function(result){ 116 | //If user say "hello world" the result = "hello world" 117 | }); 118 | ``` 119 | 120 | ### $$voice.pause(); 121 | 122 | Suspends the plugin working 123 | 124 | ### $$voice.resume(); 125 | 126 | Resumes the plugin working 127 | 128 | ### $$voice.refresh(); 129 | 130 | Restarts voiceLink() with the current parametrs. It is used often to support the actual condition of the voice hints. 131 | If the active button with the voice hint stops to be visible then this button disappears from the helpbar and doesn't work if the attribute data-voice-force-visible isn't set or a flag $$voice.useHidden. 132 | 133 | 134 | ## Important 135 | 136 | The event voice spreads out up on the DOM tree, in such a way if one link put into another voice link and doesn't call e.preventDefault() then when subsidiary links is triggered the parent also receives the event. 137 | 138 | -------------------------------------------------------------------------------- /docs/img/legend.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/docs/img/legend.jpg -------------------------------------------------------------------------------- /docs/img/mag_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/docs/img/mag_11.png -------------------------------------------------------------------------------- /docs/img/mag_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/docs/img/mag_20.png -------------------------------------------------------------------------------- /docs/nav_slides/slide1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/docs/nav_slides/slide1.png -------------------------------------------------------------------------------- /docs/nav_slides/slide2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/docs/nav_slides/slide2.png -------------------------------------------------------------------------------- /docs/nav_slides/slide3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/docs/nav_slides/slide3.png -------------------------------------------------------------------------------- /docs/nav_slides/slide4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/docs/nav_slides/slide4.png -------------------------------------------------------------------------------- /docs/nav_slides/slide5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/docs/nav_slides/slide5.png -------------------------------------------------------------------------------- /docs/nav_slides/slide6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/docs/nav_slides/slide6.png -------------------------------------------------------------------------------- /docs/nav_slides/slide7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/docs/nav_slides/slide7.png -------------------------------------------------------------------------------- /docs/ru_input.md: -------------------------------------------------------------------------------- 1 | # Обзор 2 | 3 | Плагин создает input, предназначенный для работы с пультом дистанционного управления 4 | и виртуальной клавиатурой 5 | 6 | # Инициализация 7 | 8 | SBInput - jQuery плагин, вызываемый на элементе input. 9 | Первичная инициализация плагина произодится следующим образом: 10 | 11 | $(el).SBInput(options); 12 | 13 | options = { 14 | // virtual keyboard options 15 | keyboard: { 16 | type: 'fulltext_ru', 17 | firstLayout: 'ru' 18 | }, 19 | 20 | /** 21 | * Format function(if needed) 22 | * @param text 23 | */ 24 | formatText: function(text) { 25 | return text; 26 | }, 27 | 28 | // element of existing keyboard for input 29 | bindKeyboard: null, 30 | 31 | // input template options 32 | input: { 33 | template: '
' + 34 | '
' + 35 | '' + 36 | '' + 37 | '
' + 38 | '
', 39 | 40 | // main container class 41 | elClass: 'smart_input-container', 42 | wrapperClass: 'smart_input-wrap', 43 | cursorClass: 'smart_input-cursor', 44 | textClass: 'smart_input-text' 45 | }, 46 | 47 | // using native keyboard without virtual 48 | directKeyboardInput: true, 49 | 50 | // max symbols in input (0 if unlimited) 51 | max: 0, 52 | 53 | // next input element 54 | next: null 55 | } 56 | 57 | # Методы плагина 58 | 59 | Метод может быть вызван после инициализации плагина 60 | 61 | - startBlink старт мигания курсора 62 | 63 | $(el).SBInput('startBlink'); 64 | 65 | - stopBlink остановить мигания курсора 66 | 67 | $(el).SBInput('stopBlink'); 68 | 69 | - showKeyboard отобразить клавиатуру 70 | 71 | $(el).SBInput('showKeyboard'); 72 | 73 | - hideKeyboard скрытие клавиатуры 74 | 75 | $(el).SBInput('hideKeyboard'); 76 | 77 | - changeKeyboard смена клавиатуры на элементе 78 | 79 | var keyboardOpt = { 80 | type: 'fulltext_ru', 81 | firstLayout: 'ru' 82 | }, 83 | $(el).SBInput('changeKeyboard', keyboardOpt); 84 | 85 | - setText установка текста в инпут 86 | 87 | $(el).SBInput('setText', 'text in input'); 88 | 89 | # Мигание курсора 90 | 91 | Мигание курсора подразумевает добавление/удаление класса к элементу cursor 92 | 93 | Имя класса определяется следующим образом cursorClass + '_hidden' 94 | 95 | # Форматирование текста в инпуте 96 | 97 | Если в опциях передана функция formatText, она будет вызываться после каждого ввода символа. 98 | Функция должна возвращать текст для инпута 99 | 100 | # События инпута 101 | 102 | - 'keyboard_show' 103 | - 'keyboard_hide' 104 | - 'keyboard_cancel' 105 | - 'keyboard_complete' 106 | 107 | # Как узнать значение инпута? 108 | 109 | После инициализаци инпута на элементе 110 | 111 | $(el).SBInput(options); 112 | 113 | Значение можно узнать из элемента 114 | 115 | el.value; 116 | $(el).val(); 117 | 118 | -------------------------------------------------------------------------------- /docs/ru_keyboard.md: -------------------------------------------------------------------------------- 1 | # Обзор 2 | 3 | Плагин позволяет отобразить виртуальную клавиатуру на экране. 4 | 5 | # Инициализация 6 | 7 | SBKeyboard - jQuery плагин, который может быть вызван на любом jQuery элементе. 8 | Первичная инициализация плагина произодится следующим образом: 9 | 10 | $(el).SBKeyboard(options); 11 | 12 | options = { 13 | // keyboard preset 14 | type: 'en', 15 | 16 | // first layout to show 17 | firstLayout: 'en' 18 | } 19 | 20 | # Методы плагина 21 | 22 | Метод может быть вызван после инициализации плагина 23 | 24 | - Добавление другой клавиатуры к текущему элементу 25 | 26 | $(el).SBKeyboard('addKeyboard', options); 27 | options = { 28 | type: 'en', 29 | firstLayout: 'en' 30 | } 31 | 32 | - Переключение активной клавиатуры(при наличии нескольких) на элементе 33 | 34 | $(el).SBKeyboard('changeKeyboard', type); // type из опций 35 | 36 | # Раскладки плагина 37 | 38 | В плагине имеются несколько определенных по умолчанию раскладок: 39 | 40 | - en - английская раскладка 41 | - ru - русская раскладка 42 | - email - раскладка для набора email - адреса 43 | - num - раскладка для цифр от 0 до 9 44 | - fulltext_ru - раскладка английской и русской клавиатуры 45 | 46 | # Добавление своей раскладки 47 | 48 | Для добавление мультиязычной раскладки необходимо определить массив в объекте window.SB.keyboardPresets 49 | 50 | window.SB.keyboardPresets[multiKeyboardLayout] = ['en', 'ru']; 51 | 52 | Для добавления простой раскладки необходимо определить функцию в объекте window.SB.keyboardPresets, которая 53 | будет возвращать массив 54 | 55 | window.SB.keyboardPresets[keyboardLayout] = function(){ 56 | return [ 57 | // first keyboard row 58 | ['q','w','e','r','t','y','u','i','o','p'], 59 | // second keyboard row 60 | ['a','s','d','f','g','h','j','k','l'], 61 | ] 62 | }; 63 | 64 | В раскладке необходимо определить следующие клавиши 65 | 66 | - lang{{}} - клавиша смены раскладки 67 | - nums{{}} - клавиша для переключению на цифровую клавиатуру(если в списке раскладок присутствует fullnum) 68 | - space{{}} - клавиша пробела 69 | - complete{{}} - окончание ввода 70 | 71 | Клавиши, которые могут быть определены по желанию 72 | 73 | - shift{{}} - делает буквы прописными 74 | - backspace{{}} - генерация события 'backspace'(пр. для удаления одного символа) 75 | - delall{{}} - генерация события 'delall'(пр. для очистки строки) 76 | 77 | Все клавиши определяются следующим образом 78 | 79 | keyName{{keyText}}, keyText - html или текст внутри контейнера клавиши 80 | 81 | Примеры использования: 82 | 83 | backspace{{}} 84 | lang{{ru}} 85 | nums{{123}} 86 | 87 | Пример полной раскладки 88 | 89 | window.SB.keyboardPresets.en = function () { 90 | return [ 91 | 'qwertyuiop'.split(''), 92 | 'asdfghjkl'.split('').concat(['backspace{{}}']), 93 | ['shift{{Shift}}'].concat('zxcvbnm'.split('')).concat( 94 | ['delall{{Del
all
}}']), 95 | ['lang{{en}}', 'nums{{123}}', 'space{{}}', 'complete{{Complete}}'] 96 | ]; 97 | }; 98 | 99 | # События клавиатуры 100 | 101 | - 'type' событие ввода клавиши, клавиша определена в свойстве letter события 102 | 103 | var typeLetter = function(event) { 104 | console.log(event.letter); 105 | } 106 | 107 | - 'backspace' 108 | - 'delall' 109 | - 'complete' 110 | 111 | # CSS классы 112 | 113 | .kb-multilang - имеются несколько языковых раскладок 114 | .kb-havenums - имеется цифровая кливиатура 115 | 116 | -------------------------------------------------------------------------------- /docs/ru_legend.md: -------------------------------------------------------------------------------- 1 | # Обзор 2 | 3 | Легенда позволяет отображать подсказки для действий клавиш внизу экрана 4 | 5 | # Инициализация 6 | 7 | При подключении плагина в body создается элемент
8 | Объект легенды можно вызвать через window.$$legend 9 | 10 | # Отображение элементов в легенде 11 | 12 | Все элементы легенды располагаются в объекте $$legend.keys. 13 | Элементы в легенде расположены в соответствии с рекомендациями Samsung. 14 | Элементы, реагирующие на нажатия в легенде, имеют класс legend-clickable. 15 | При нажатии на такой элемент возникает событие nav_key:(Имя элемента) в body 16 | По умолчанию в легенде присутствуют элементы: 17 | 18 | ![legend](img/legend.jpg) 19 | 20 | # Скрытие/отображение элемента 21 | 22 | Каждый элемент легенды является функцией. 23 | 24 | Для отображения элемента необходимо передать в него непустой текст 25 | 26 | $$legend.keys.move('Навигация'); 27 | $$legend.keys.blue('Полный экран'); 28 | 29 | Для скрытия элемента необходимо передать в его функцию пустой текст 30 | 31 | $$legend.keys.move(''); 32 | $$legend.keys.blue(''); 33 | 34 | Для получения текущего значения элемента необходимо просто вызвать его функцию 35 | 36 | $$legend.keys.move(); 37 | $$legend.keys.blue(); 38 | 39 | # Методы плагина 40 | 41 | 1) Очистка легенды. 42 | 43 | $$legend.clear(); 44 | 45 | 2) Отображение легенды 46 | 47 | $$legend.show(); 48 | 49 | 3) Скрытие легенды 50 | 51 | $$legend.hide(); 52 | 53 | 4) Сохранение состояния легенды 54 | 55 | $$legend.save(); 56 | 57 | 5) Восстановление последнего сохраненного состояния легенды 58 | 59 | $$legend.restore(); 60 | 61 | 6) Добавление новой клавиши в легенду 62 | 63 | // keyName - название клавиши 64 | // isClickable - можно ли кликнуть по клавише в легенде(по умолчанию true) 65 | $$legend.addKey(keyName, isClickable); 66 | 67 | -------------------------------------------------------------------------------- /docs/ru_log.md: -------------------------------------------------------------------------------- 1 | # Обзор 2 | 3 | Лог позволяет отобразить сообщения из кода на экране. 4 | 5 | # Инициализация 6 | 7 | Лог инициализируется при подключении плагина. 8 | В DOM создается элемент
9 | 10 | # Отображение сообщений 11 | 12 | По умолчанию кнопка для отображения сообщений tools. 13 | При клике по кнопке происходят события 14 | - отображение лога 15 | - смена панелей 16 | - скрытие лога 17 | 18 | # Методы плагина 19 | 20 | Объект лога находится в объекте SB.utils.log 21 | 22 | 1) SB.utils.log.log(msg, logName) 23 | window.$$log(msg, logName) 24 | 25 | Вывод сообщения msg в панели лога logName. 26 | 27 | 2) show(logName) 28 | 29 | Отображение панели лога logName. 30 | 31 | 3) hide() 32 | 33 | Скрытие лога. 34 | 35 | 4) state( msg, state, logName ) 36 | 37 | Отображение сообщения в панели state лога logName 38 | По умолчанию state отображается внизу панели лога. 39 | Пример использования: вывод текущего времени проигрывания. 40 | 41 | 5) startProfile( profileName ); 42 | stopProfile: function ( profileName ) 43 | 44 | Функции профилирования. Выводят результат на панели profiler. 45 | 46 | -------------------------------------------------------------------------------- /docs/ru_mag.md: -------------------------------------------------------------------------------- 1 | > English documentation will be soon... 2 | 3 | # STB Infomir MAG200/250 How-to 4 | 5 | ## Как запустить демку на устройстве MAG250 6 | 7 | * Заходим на устройство по ssh ([инструкция](http://wiki.infomir.eu/doku.php/faq:second_bootloader_mag200_250)). Могут быть проблемы. Если нет доступа до 22-го порта - вам нужно перепрошить устройство на публичную версию (через Bootstarp&USB). Далее входим с паролем `930920`: 8 | 9 | ``` 10 | ssh root@192.168.0.XXX 11 | ``` 12 | 13 | * Запускаем нашу демку: 14 | 15 | ``` 16 | killall stbapp 17 | /usr/share/qt-4.6.0/stbapp -qws -display directfb http://immosmart.github.io/smartbox/demo/demoApp/ & 18 | 19 | killall stbapp 20 | /usr/share/qt-4.6.0/stbapp -qws -display directfb http://immosmart.github.io/smartbox/ & 21 | ``` 22 | 23 | * Можно сделать скриншот (инструкция http://wiki.infomir.eu/doku.php/stalker:faq). На MAG250 нормально работает только рецепт с fbdump. Правда видео не захватывается. 24 | 25 | ``` 26 | wget -O /tmp/fbdump.zip http://wiki.infomir.eu/lib/exe/fetch.php/files:fbdump.zip 27 | unzip /tmp/fbdump.zip -d /tmp && rm /tmp/fbdump.zip 28 | mv /tmp/fbdump /usr/bin/ 29 | chmod a+x /usr/bin/fbdump 30 | 31 | cd /root 32 | fbdump ss.3.png 33 | 34 | # Выгружаем как-то так: 35 | scp root@192.168.0.XXX:/root/ss* . 36 | ``` 37 | 38 | ## Скриншоты 39 | 40 | ### Демка на MAG250 41 | 42 | ![Демка на MAG250](img/mag_11.png) 43 | 44 | ### Прогон тестов 45 | 46 | ![Alt text](img/mag_20.png) 47 | 48 | ### Видео 49 | 50 | [![MAG250 в действии](http://img.youtube.com/vi/Wd69d_WsD0Q/0.jpg)](http://www.youtube.com/watch?v=Wd69d_WsD0Q) 51 | -------------------------------------------------------------------------------- /docs/ru_nav_alg.md: -------------------------------------------------------------------------------- 1 | # Алгоритм поиска элемента 2 | 3 | Алоритм справледлив для любого направления. Говоря ниже "верхняя грань" имеется в виду грань совпадающая с направлением движения. Для движения вверх - верхняя, вниз - нижняя, вправо - правая, влево - левая. 4 | 5 | Пример: есть элементы навигации, элемент в фокусе обозначен красным цветом. 6 | 7 | ![slide1](nav_slides/slide1.png) 8 | 9 | Юзер нижмает кнопку вверх. Первое что происходит - отсеиваютя элементы верхняя грань которых ниже чем верхняя грань элемента в фокусе. 10 | 11 | ![slide2](nav_slides/slide2.png) 12 | 13 | Находятся элементы, грани которых пересекаются по оси перпендикулярной направлению. Если такие элементы находятся, их приоритет становится выше и остальные отсеиваются. 14 | 15 | ![slide3](nav_slides/slide3.png) 16 | 17 | Из оставшихся элементов находится тот чья верхняя грань ближе всего к верхней грани элемента в фокусе. Это и есть искомый элемент. 18 | 19 | ![slide4](nav_slides/slide4.png) 20 | 21 | 22 | Если на втором шаге пересекающихся элементов не было найдено с оставшимися элементами происходит тоже самое. Находится самый ближайший. 23 | 24 | ![slide5](nav_slides/slide5.png) 25 | 26 | # Это не баг, это фича. 27 | 28 | 29 | ![slide6](nav_slides/slide6.png) 30 | 31 | В этом примере двигаясь по вертикали из элемента **B** в элемент **A** перейти можно, но не наоборот. 32 | Это происходит потому что нижняя грань элемента **A**, ниже нижней грани элемента **B**. 33 | Таким образом **A** одновременно и выше и ниже **B** 34 | -------------------------------------------------------------------------------- /docs/ru_nav_extended.md: -------------------------------------------------------------------------------- 1 | Алгоритм поиска элемента можно изменить в случаях когда требуется иное поведение. 2 | 3 | 4 | 5 | 6 | # Атрибут user defined 7 | 8 | ``` 9 | 10 | ``` 11 | 12 | Если атрибут задан, то навигация перестает в некоторых случаях использовать интеллектуальный поиск элемента. В примере выше при нажатии "DOWN" фокус перейдет на элемент #pfl_film_watch 13 | Стороны указываются через запятую: up, right, down, left 14 | 15 | ``` 16 | 17 | ``` 18 | 19 | В этом примере нажатие кнопки "DOWN" будет игнорироваться. 20 | 21 | Так же можно указывать каждую сторону отдельно 22 | 23 | ``` 24 | 25 | ``` 26 | 27 | В этом примере нажатие кнопки "UP" будет игнорироваться. 28 | 29 | Если атрибут будет задан через .data(), то он будет как же использоваться. 30 | 31 | 32 | 33 | 34 | 35 | # Атрибут entry point 36 | 37 | Атрибут который запрещает входить в элемент с определенного направления. 38 | 39 | Точки входа задаются в атрибуте с помощью 0 и 1 через запятые 40 | 41 | 0 - входить нельзя 42 | 43 | 1 - входить можно 44 | 45 | Стороны указываются в порядке CSS - top, right, bottom, left 46 | 47 | ``` 48 | 49 | ``` 50 | В этом примере в элемент нельзя войти снизу. 51 | 52 | 53 | 54 | 55 | 56 | # Фантомы 57 | 58 | Фантом это такой `nav-item`, который при попадании в фокус переводит фокус на другой элемент. Работает только для клавиатуры. При управлении жестами игнорируется. 59 | 60 | ``` 61 | 62 | ``` 63 | 64 | В этом примере при попытке перейти из **С** в **A** фокус будет переведен на элемент **B** 65 | 66 | http://immosmart.github.io/smartbox/examples/navigation/phantom/index.html 67 | 68 | ![slide7](nav_slides/slide7.png) 69 | 70 | -------------------------------------------------------------------------------- /docs/ru_platform.md: -------------------------------------------------------------------------------- 1 | # Платформа 2 | 3 | Все методы платформы вызывается через SB.methodName 4 | Название платформы хранится в SB.platformName 5 | 6 | # Свойства и методы 7 | 8 | * `SB.platformName` 9 | * `SB.keys` 10 | * `SB.platformUserAgent` 11 | * `SB.getDUID` 12 | * `SB.getNativeDUID` 13 | * `SB.getSDI` 14 | * `SB.getRandomDUID` 15 | * `SB.getMac` 16 | * `SB.setPlugins` 17 | * `SB.setData(name, value)` 18 | * `SB.getData(name)` 19 | * `SB.removeData(name)` 20 | * `Добавление своей платформы` 21 | 22 | 23 | 24 | ### `SB.platformName` 25 | 26 | *String*: название платформы(browser/samsung/mag/philips/lg) 27 | 28 | 29 | ### `SB.keys` 30 | 31 | *Plain object*: хэш, содержащий названия и коды клавиш 32 | 33 | #### Стандартные названия клавиш 34 | 35 | BLUE 36 | CH_DOWN 37 | CH_UP 38 | DOWN 39 | ENTER 40 | EXIT 41 | FF 42 | GREEN 43 | INFO 44 | LEFT 45 | N0 46 | N1 47 | N2 48 | N3 49 | N4 50 | N5 51 | N6 52 | N7 53 | N8 54 | N9 55 | NEXT 56 | PAUSE 57 | PLAY 58 | PRECH 59 | PREV 60 | REC 61 | RED 62 | RETURN 63 | RIGHT 64 | RW 65 | SMART 66 | STOP 67 | SUBT 68 | TOOLS 69 | UP 70 | YELLOW 71 | 72 | 73 | 74 | ### `SB.platformUserAgent` 75 | 76 | *String* уникальная строка, которая проверяется на вхождение в userAgent запущенной среды 77 | 78 | #### Пример 79 | 80 | SB.platformUserAgent === 'netcast'; // for philips 81 | SB.platformUserAgent === 'maple'; // for samsung 82 | 83 | 84 | 85 | ### `SB.getDUID` 86 | 87 | *Function* возвращает DUID устройства, в зависимости от параметра SB.config.DUID 88 | 89 | #### Returns 90 | 91 | *String* DUID 92 | 93 | 94 | 95 | ### `SB.getNativeDUID` 96 | 97 | *Function* возвращает внутренний DUID устройства при наличии 98 | 99 | #### Returns 100 | 101 | *String* DUID устройства или пустая строка 102 | 103 | 104 | 105 | ### `SB.getSDI` 106 | 107 | *Function* возвращает SDI устройства при наличии 108 | 109 | #### Returns 110 | 111 | *String* SDI устройства или пустая строка 112 | 113 | 114 | 115 | ### `SB.getRandomDUID` 116 | 117 | *Function* возвращает сгенерированный DUID устройства 118 | 119 | #### Returns 120 | 121 | *String* DUID, например: "1446dcfb2ca1091" 122 | 123 | 124 | 125 | ### `SB.getMac` 126 | 127 | *Function* возвращает MAC устройства при наличии 128 | 129 | #### Returns 130 | 131 | *String* MAC или пустая строка 132 | 133 | 134 | 135 | ### `SB.setPlugins` 136 | 137 | *Function* инициализация и запуск плагинов, специфичных для платформы 138 | Функция вызывается автоматически при инициализации библиотеки 139 | 140 | 141 | 142 | ### `SB.setData(name, value)` 143 | 144 | *Function* сохранение информации в локальном хранилище платформы 145 | 146 | #### Arguments 147 | 148 | 1. `name` *String* Название параметра 149 | 2. `value` *(*)* Значение параметра 150 | 151 | 152 | 153 | ### `SB.getData(name)` 154 | 155 | *Function* возвращает значение из локального хранилища 156 | 157 | #### Arguments 158 | 159 | 1. `name` *String* Название параметра 160 | 161 | #### Returns 162 | 163 | *(*)* Значение параметра 164 | 165 | 166 | 167 | ### `SB.removeData(name)` 168 | 169 | *Function* удаление параметра и значения из локального хранилища 170 | 171 | #### Arguments 172 | 173 | 1. `name` *String* Название параметра для удаления 174 | 175 | 176 | 177 | ## `Добавление своей платформы` 178 | 179 | Для добавления своей платформы необходимо воспользоваться функцией SB.createPlatform 180 | 181 | ``` 182 | var platformName = 'myPlatform'; 183 | SB.createPlatform(platformName, { 184 | //функции платформы 185 | }); 186 | ``` 187 | 188 | 189 | -------------------------------------------------------------------------------- /docs/ru_player.md: -------------------------------------------------------------------------------- 1 | # Плеер 2 | 3 | 4 | ###Оглавление 5 | 6 | `Публичные события` 7 | * `ready` 8 | * `bufferingBegin, bufferingEnd` 9 | * `stop` 10 | * `complete` 11 | 12 | `Публичные свойства` 13 | * `Player.state` 14 | * `Player.videoInfo.duration` 15 | * `Player.videoInfo.currentTime` 16 | * `Player.videoInfo.width` 17 | * `Player.videoInfo.height` 18 | * `Player.usePlayerObject` 19 | 20 | `Публичные методы` 21 | * `Player.play(options)` 22 | * `Player.stop([silent])` 23 | * `Player.pause()` 24 | * `Player.resume()` 25 | * `Player.togglePause()` 26 | * `Player.formatTime(seconds)` 27 | * `Player.seek(seconds)` 28 | * `Player.audio.get()` 29 | * `Player.audio.set(index)` 30 | * `Player.audio.cur()` 31 | 32 | 33 | ##Публичные события 34 | 35 | ###`ready` 36 | 37 | Отправляется когда плеер получает информацию о потоке(duration, resolution, etc) и начинает воспроизведение. 38 | 39 | #### Пример 40 | 41 | ```js 42 | Player.on('ready', function(){ 43 | $('#duration').html(Player.formatTime(Player.videoInfo.duration)); 44 | }); 45 | ``` 46 | 47 | * * * 48 | 49 | 50 | ###`bufferingBegin, bufferingEnd` 51 | 52 | События отправляются когда начинается и заканчивается буферизация видео, при слабом коннекте или после перемотки. 53 | 54 | #### Пример 55 | 56 | ```js 57 | var $loadingIndicator=$('#loading_indicator'); 58 | Player.on('bufferingBegin', function(){ 59 | $loadingIndicator.show(); 60 | }); 61 | 62 | Player.on('bufferingEnd', function(){ 63 | $loadingIndicator.hide(); 64 | }); 65 | ``` 66 | 67 | * * * 68 | 69 | 70 | ###`stop` 71 | 72 | Отправляется когда было остановлено воспроизведение. 73 | 74 | #### Пример 75 | 76 | ```js 77 | Player.on('stop', function(){ 78 | $videoScene.hide(); 79 | }); 80 | ``` 81 | 82 | * * * 83 | 84 | ###`complete` 85 | 86 | Отправляется когда видео файл доиграл до конца и остановился. 87 | 88 | #### Пример 89 | 90 | ```js 91 | Player.on('complete', function(){ 92 | playNextVideo(); 93 | }); 94 | ``` 95 | 96 | * * * 97 | 98 | 99 | ##Публичные свойства 100 | 101 | ###`Player.state` 102 | 103 | *String*: Текущее состояние плеера: 104 | 105 | 1. `play`: видео воспроизводится 106 | 2. `pause`: видео приостановлено 107 | 3. `stop`: видео остановлено 108 | 109 | * * * 110 | 111 | 112 | ###`Player.videoInfo.duration` 113 | 114 | *Number*: продолжительность видео файла в секундах 115 | 116 | * * * 117 | 118 | ###`Player.videoInfo.currentTime` 119 | 120 | *Number*: текущее время воспроизведения в секундах 121 | 122 | * * * 123 | 124 | ###`Player.videoInfo.width` 125 | 126 | *Number*: ширина потока видео в пикселях 127 | 128 | * * * 129 | 130 | ###`Player.videoInfo.height` 131 | 132 | *Number*: высота потока видео в пикселях 133 | 134 | * * * 135 | 136 | ###`Player.usePlayerObject` 137 | 138 | *Boolean*: определяет будет использоваться или sef плагин. 139 | 140 | *Доступно только для следующей платформы: Samsung* 141 | 142 | * * * 143 | 144 | 145 | 146 | ## Публичные методы 147 | 148 | ###`Player.play(options)` 149 | 150 | Начинает воспроизведение видео. 151 | 152 | #### Аргументы 153 | `options` *Plain object*: хеш содержащий параметры для запуска 154 | 155 | Или 156 | 157 | `url` *String*: путь к видео 158 | 159 | #### Примеры 160 | ```js 161 | Player.play({ 162 | url: "movie.mp4" 163 | }); 164 | Player.play("movie.mp4"); 165 | //оба варианта одинаковы 166 | 167 | Player.play({ 168 | url: "movie.mp4" 169 | from: 20 170 | });// запускает видео с 20 секунды 171 | ``` 172 | 173 | * * * 174 | 175 | ###`Player.stop([silent])` 176 | 177 | Останавливает воспроизведение видео. 178 | 179 | #### Аргументы 180 | 1. `silent[optional]` *Boolean*: если передан флаг `silent`, то не будет вызвано событие `stop` 181 | 182 | 183 | #### Примеры 184 | ```js 185 | Player.stop(); 186 | 187 | App.onDestroy(function(){ 188 | Player.stop(true); 189 | }); // Останавливает воспроизведение и позволяет избежать побочных эффектов 190 | ``` 191 | 192 | * * * 193 | 194 | ###`Player.pause()` 195 | 196 | Приостанавливает воспроизведение видео. 197 | 198 | 199 | 200 | #### Примеры 201 | ```js 202 | Player.pause(); 203 | ``` 204 | 205 | * * * 206 | 207 | ###`Player.resume()` 208 | 209 | Возобновляет воспроизведение видео после паузы. 210 | 211 | 212 | #### Примеры 213 | ```js 214 | Player.resume(); 215 | ``` 216 | 217 | * * * 218 | 219 | ###`Player.togglePause()` 220 | 221 | Переключает pause/resume в зависимости от текущего состояния. 222 | 223 | #### Примеры 224 | ```js 225 | Player.togglePause(); 226 | ``` 227 | 228 | * * * 229 | 230 | ###`Player.formatTime(seconds)` 231 | 232 | Конвертирует время в секундах в строку вида H:MM:SS 233 | 234 | #### Аргументы 235 | `seconds` *Number*: время в секундах 236 | 237 | #### Возвращает 238 | *String*: реультирующая строка 239 | 240 | 241 | #### Примеры 242 | ```js 243 | Player.formatTime(PLayer.videoInfo.duration); // => "1:30:27" 244 | ``` 245 | 246 | * * * 247 | 248 | 249 | ###`Player.seek(seconds)` 250 | 251 | Переход на заданное время в секундах. 252 | 253 | #### Аргументы 254 | `seconds` *Number*: время в секундах 255 | 256 | 257 | #### Примеры 258 | ```js 259 | Player.seek(20);//перейти на 20 секунду 260 | 261 | Player.seek(Player.videoInfo.currentTime + 10);//прыжок на 10 секунд вперед 262 | ``` 263 | 264 | * * * 265 | 266 | 267 | ###`Player.audio.get()` 268 | 269 | Возвращает массив с кодами языков звуковых дорожек. 270 | 271 | Список всех кодов можно найти тут 272 | http://forum.doom9.org/showthread.php?t=155762 273 | 274 | *Доступно только для следующей платформы: Samsung* 275 | 276 | #### Возвращает 277 | *Array*: массив с кодами 278 | 279 | 280 | #### Примеры 281 | ```js 282 | var tracksArray=Player.audio.get();//=> [7501171, 6448492] 283 | var currentLang=array[Player.audio.cur()];//=> 7501171 284 | var currentLangString=Strings[currentLang];//=> "Russian" 285 | ``` 286 | 287 | * * * 288 | 289 | 290 | ###`Player.audio.set(index)` 291 | 292 | Задает звуковую дорожку согласно индексу. 293 | 294 | *Доступно только для следующей платформы: Samsung* 295 | 296 | #### Аррргументы 297 | 298 | `index` *Number*: индекс звуковой дорожки 299 | 300 | 301 | #### Примеры 302 | ```js 303 | Player.audio.set(0); 304 | ``` 305 | 306 | * * * 307 | 308 | ###`Player.audio.cur()` 309 | 310 | Задает звуковую дорожку согласно индексу. 311 | 312 | *Доступно только для следующей платформы: Samsung* 313 | 314 | 315 | #### Возвращает 316 | *Number*: индекс текущей звуковой дорожки. 317 | 318 | #### Примеры 319 | ```js 320 | Player.audio.cur(); //=> 1 321 | ``` 322 | 323 | * * * 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | -------------------------------------------------------------------------------- /docs/ru_voice.md: -------------------------------------------------------------------------------- 1 | # Voicelink 2 | 3 | jQuery плагин для работы с голосовым упралвением в smart tv. 4 | 5 | Принцип работы: на элементах с указанным классом `voicelink` в атрибуте `data-voice` указывается фраза, которую должен произнести юзер. 6 | После произнесения фразы заданной в атрибуте `data-voice` элемент отправляет событие `voice`. 7 | Плагин генерирует нативный хелпбар голосового управления и добаляет в body свой который может быть вызван фразой "Еще" (фраза может быть измерена настройками плагина) 8 | В браузере плагин эмулирует голосовое управление генерируя дополнительный блок с кнопками с досупными фразами. 9 | 10 | ## Пример 11 | 12 | Юзер говорит "Назад" - элемент `#backVoiceLink` отправляет событие `voice` 13 | 14 | 15 | html: 16 | ``` 17 | 18 | ``` 19 | 20 | js: 21 | ``` 22 | SB.ready(function(){ 23 | $('#scene').voiceLink(); 24 | $('#backVoiceLink').bind('voice', function(){ 25 | //юзер сказал "Назад" 26 | }); 27 | }); 28 | ``` 29 | 30 | ## Атрибуты 31 | 32 | ### data-voice="[слово]" 33 | 34 | обязательный атрибут - подсказка в хелпбаре самсунга или в "пузыре" 35 | Примечание: "пузырь" - блок с группированными подсказками. 36 | 37 | ### data-voice-group="[название_группы]" 38 | 39 | опциональный атрибут - группа для подсказки. Если группа есть то элемент показывается в пузыре. Если нету в хелпбаре самсунга. 40 | 41 | ### data-voice-hidden="[true]" 42 | 43 | опциональный атрибут - показывает что слово произнести можно, но подсказка не будет нигде отображаться. 44 | Поведение зависит только от того пустое значение или нет, т.к. в 11 тв нет возможности сохранять $().data(). 45 | Пример 46 | 47 | ``` 48 | data-voice-hidden="true" = data-voice-hidden="abc" = data-voice-hidden="1" = data-voice-hidden="false" = true 49 | data-voice-hidden="" = false 50 | ``` 51 | 52 | 53 | ### data-voice-force-visible="[true]" 54 | 55 | Показывает элемент в хелпбаре, даже если он скрыт и useHidden=false 56 | 57 | 58 | 59 | ### Настройки 60 | 61 | В хэше options можно задавать класс селектор для голосовых ссылок. 62 | ``` 63 | options={ 64 | selector: '.voicelink',//селектор для поиска 65 | moreText: 'More',//текст для показа "пузыря" 66 | eventName: 'voice',//событие которое отправляет элемент 67 | useHidden: false//фильтровать невидимые ссылки 68 | } 69 | 70 | $('#scene').voiceLink(options); 71 | ``` 72 | 73 | `options` можно не задавать тогда настройки встанут по умолчанию, какие описаны выше 74 | 75 | ``` 76 | $('#scene').voiceLink(); 77 | ``` 78 | 79 | ``` 80 | $$voice.setup(newDefaults);//задает новые умолчания 81 | ``` 82 | 83 | ## Методы 84 | 85 | ### $$voice.say(phrase); 86 | 87 | Эмулирует произнесение фразы. 88 | 89 | ### $$voice.enabled(); 90 | 91 | Определяет поддерживается ли голосове управление устройством. Возвращает boolean 92 | 93 | ### $$voice.setup(options); 94 | 95 | Задает умолчания, в options - хэш настроек 96 | 97 | ### $$voice.save(); 98 | 99 | Сохраняет текущее состояние (контейнер и options) в стек. 100 | 101 | ### $$voice.restore(); 102 | 103 | Восстанавливает последнее сохраненное состояние в стеке и удаляет его из стека 104 | 105 | ### $$voice.fromServer(title,callback); 106 | 107 | Распознавание голоса с помощью сервера самсунга, title - текст который видит пользователь, callback - функция которая вызывается после успешного распознавания текста, принимает в себя результат. 108 | В браузере выводится prompt сообщение. 109 | 110 | Пример: 111 | 112 | ``` 113 | $$voice.fromServer("скажите слово для поиска",function(result){ 114 | //если юзер сказал "привет мир" result = "привет мир" 115 | }); 116 | ``` 117 | 118 | ### $$voice.pause(); 119 | 120 | Приостанавливает работу плагина. 121 | 122 | ### $$voice.resume(); 123 | 124 | Возобновляет работу плагина. 125 | 126 | ### $$voice.refresh(); 127 | 128 | Перезапускает voiceLink() с текущими параметрами. Используется часто для того чтобы поддерживать актуальное состояние голосвых подсказок. 129 | Если активная кнопка с голосовой подсказкой перестала быть видимой, то фраза исчезает из хелпбара и не работает, если не задан атрибут `data-voice-force-visible` или флаг $$voice.useHidden 130 | 131 | 132 | ## Важно 133 | 134 | Событие voice распространяется вверх по DOM дереву, таким образом если вложить одну голосовую ссылку в другую и не вызвать e.preventDefault() при срабатывании дочерней ссылки родительская тоже получит событие. 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /dune_plugin_sm_demo/dune_plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | smartbox_demo 3 | Smartbox demoApp 4 | 5 | 6 | root://tv 7 | smartbox_demo 8 | Smartbox demoApp 9 | plugin_file://icon.png 10 | 11 | 12 | launch_media_url 13 | 14 | www://http://your-http-server/smartbox/demo/demoApp/:::fullscreen=1&webapp_keys=1&zoom_level=100&osd_size=1280x720&overscan=0&background_color=black&input_handler=0 15 | 16 | 17 | 18 | yes 19 | yes 20 | 21 | 22 | php 23 | 24 | -------------------------------------------------------------------------------- /dune_plugin_sm_demo/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/dune_plugin_sm_demo/icon.png -------------------------------------------------------------------------------- /dune_plugin_sm_demo/main.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 60 | 61 | 62 | 63 |
64 |
65 | Input with default settings: 66 |
67 |
68 | 69 |
70 |
71 |
 72 |             $('#input').SBInput({});
 73 |         
74 |
75 |
76 | 77 |
78 |
79 | Input with email keyboard and predefined value 80 |
81 |
82 | 83 |
84 |
85 |
 86 |         $('#input2').SBInput({
 87 |             keyboard: {
 88 |               type: 'email'
 89 |             }
 90 |         });
 91 |         
92 |
93 |
94 | 95 |
96 |
97 | Input with maximum 4 symbols 98 |
99 |
100 | 101 |
102 |
103 |
104 |         $('#input3').SBInput({
105 |             max: 4
106 |         });
107 |         
108 |
109 |
110 | 111 |
112 |
113 | Input with num keyboard 114 |
115 |
116 | 117 |
118 |
119 |
120 |         $('#input4').SBInput({
121 |             keyboard: {
122 |                 type: 'num'
123 |             }
124 |         });
125 |         
126 |
127 |
128 | 129 |
130 |
131 | Input with formatText function 132 |
133 |
134 | 135 |
136 |
137 |
138 |         $('#input5').SBInput({
139 |             formatText: function ( text ) {
140 |                 return text.toUpperCase();
141 |             }
142 |         });
143 |         
144 |
145 |
146 | 147 | 148 | 149 | 150 | 151 | 179 | 180 | -------------------------------------------------------------------------------- /examples/legend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 53 | 54 | -------------------------------------------------------------------------------- /examples/navigation/complex/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 44 | 45 | 46 | 47 | 48 | 65 | 66 | 67 | 68 | 73 | 74 |
75 |
76 |
77 | 78 | 79 | 80 | 81 |
82 |
83 |
84 |
85 | 86 | 87 | 88 | 89 |
90 |
91 |
92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |
109 |
110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /examples/navigation/hello_world/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 21 | 22 | 23 | 24 | 25 | 35 | 36 | 37 | 38 |

Use keyboard, Luke

39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/navigation/phantom/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 63 | 64 | 65 | 66 | 67 | 84 | 85 | 86 | 87 |

Use keyboard, Luke

88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
Toggle phantom
97 |
Toggle phantom opacity
98 | 99 | 100 | -------------------------------------------------------------------------------- /examples/navigation/popup/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 40 | 41 | 42 | 43 | 44 | 63 | 64 | 65 | 66 |
67 | 68 |
69 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /examples/player/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 |
29 |
30 |
00:00
31 |
00:00
32 | 33 |
34 | 35 | 36 | 37 |
38 | 39 |
40 | 41 | 42 |
43 | 44 | -------------------------------------------------------------------------------- /examples/player/player.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 20px; 3 | } 4 | 5 | .page { 6 | z-index: 1; 7 | position: absolute; 8 | width: 100%; 9 | height: 100%; 10 | left: 0; 11 | top: 0; 12 | } 13 | 14 | .bottom_bar { 15 | background: rgba(255, 255, 255, 0.86); 16 | position: absolute; 17 | left: 0; 18 | width: 100%; 19 | bottom: 0; 20 | padding: 20px; 21 | height: 120px; 22 | color: #6a6a6a; 23 | } 24 | 25 | .player_controls { 26 | position: absolute; 27 | left: 0; 28 | top: 54px; 29 | width: 100%; 30 | text-align: center; 31 | } 32 | 33 | /*button highlight style*/ 34 | .btn.focus, .tile.focus { 35 | background-color: #006DCC; 36 | background-image: linear-gradient(to bottom, #0088CC, #0044CC); 37 | background-repeat: repeat-x; 38 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 39 | color: #FFFFFF; 40 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 41 | } 42 | 43 | .tiles { 44 | width: 1000px; 45 | } 46 | 47 | .tile { 48 | float: left; 49 | width: 100px; 50 | height: 150px; 51 | margin-left: 20px; 52 | margin-bottom: 20px; 53 | border: 1px solid #C9C9C9; 54 | border-radius: 5px; 55 | } 56 | 57 | .nav-tabs .nav-item.focus a { 58 | background: #006DCC; 59 | color: #fff; 60 | } 61 | 62 | .content > div { 63 | display: none; 64 | } 65 | 66 | .current-time, .duration { 67 | position: absolute; 68 | top: 54px; 69 | font-size: 18px; 70 | } 71 | 72 | .current-time{ 73 | left: 20px; 74 | } 75 | 76 | .duration{ 77 | right: 20px; 78 | } 79 | 80 | .progress-bar { 81 | transition: none; 82 | } 83 | 84 | .btn { 85 | padding: 15px 20px; 86 | } -------------------------------------------------------------------------------- /examples/player/player.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | //init player and navigation 4 | Player.init(); 5 | $('input').smartInput({ 6 | 7 | }); 8 | $$nav.on(); 9 | 10 | //shortcuts for controls 11 | var $progress = $('.progress'), 12 | $progressBar = $('.progress-bar'), 13 | $currentTime = $('.current-time'), 14 | $pausePlayButton = $('.button_play_pause'), 15 | $duration = $('.duration'); 16 | 17 | 18 | 19 | $('.set_video_url').click(function () { 20 | Player.play({ 21 | url: $('input').val() 22 | }); 23 | }); 24 | 25 | //set progress bar position while video playing 26 | Player.on("update", function () { 27 | var currentTime = Player.videoInfo.currentTime; 28 | $progressBar.css({ 29 | width: currentTime / Player.videoInfo.duration * 100 + "%" 30 | }); 31 | $currentTime.html(Player.formatTime(currentTime)); 32 | }); 33 | 34 | //after player reads stream info duration indicator needs refresh 35 | Player.on("ready", function () { 36 | $currentTime.html(Player.formatTime(0)); 37 | $duration.html(Player.formatTime(Player.videoInfo.duration)); 38 | }); 39 | 40 | //rewind to start after complete 41 | Player.on("complete", function () { 42 | Player.seek(0); 43 | $progressBar.css({ 44 | width: 0 45 | }); 46 | $pausePlayButton.find('.glyphicon').removeClass("glyphicon-pause"); 47 | }); 48 | 49 | //handle click on progress bar 50 | var progressOffset = $progress.offset().left; 51 | var progressWidth = $progress.width(); 52 | $progress.click(function (e) { 53 | var x = e.pageX - progressOffset; 54 | Player.seek(Player.videoInfo.duration * (x / progressWidth)); 55 | }); 56 | 57 | 58 | $pausePlayButton.click(function () { 59 | Player.togglePause(); 60 | $pausePlayButton.find('.glyphicon').toggleClass("glyphicon-pause"); 61 | }); 62 | 63 | //jump backward to 10 seconds 64 | $('.button_rw').click(function () { 65 | Player.seek(Player.videoInfo.currentTime - 10); 66 | }); 67 | 68 | //jump forward to 10 seconds 69 | $('.button_ff').click(function () { 70 | Player.seek(Player.videoInfo.currentTime + 10); 71 | }); 72 | 73 | }); -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/immosmart/smartbox/d52521666acb7d438c871e61d571bd3b1d1d5d26/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | Smart Box Unit Tests 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 47 | 48 | 49 |

50 | 51 |
52 | 57 | 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "smartbox", 3 | "version": "0.0.0", 4 | "description": "smartbox\r ========", 5 | "main": "Gruntfile.js", 6 | "directories": { 7 | "doc": "docs", 8 | "example": "examples", 9 | "test": "test" 10 | }, 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/immosmart/smartbox.git" 17 | }, 18 | "author": "", 19 | "license": "BSD", 20 | "bugs": { 21 | "url": "https://github.com/immosmart/smartbox/issues" 22 | }, 23 | "dependencies": { 24 | "grunt": "~0.4.2", 25 | "grunt-contrib-uglify": "~0.2.7", 26 | "grunt-contrib-jshint": "~0.7.2", 27 | "jshint-stylish": "~0.1.4", 28 | "load-grunt-tasks": "~0.2.1", 29 | "grunt-contrib-clean": "~0.5.0", 30 | "grunt-contrib-concat": "~0.3.0" 31 | }, 32 | "devDependencies": { 33 | "grunt-contrib-watch": "~0.5.3", 34 | "grunt-contrib-cssmin": "~0.7.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /philips.php: -------------------------------------------------------------------------------- 1 |
'); 9 | // клики по кнопкам эмулятора голоса 10 | $('#emul_voice_helpbar').on('click', '.emul_voice_trigger', function () { 11 | $$voice.say(this.innerHTML); 12 | }); 13 | }, 14 | _nativeTurnOff: function () { 15 | $('#emul_voice_helpbar').empty(); 16 | }, 17 | _nativeFromServer: function (title, callback) { 18 | var text = prompt(title); 19 | callback(text || ''); 20 | }, 21 | _nativeCheckSupport: function () { 22 | return true; 23 | }, 24 | _setVoiceHelp: function (voicehelp) { 25 | var $bar = $('#emul_voice_helpbar'); 26 | $bar.empty(); 27 | if (voicehelp.helpbarItemsList) { 28 | $.each(voicehelp.helpbarItemsList, function (key, val) { 29 | $('
', { 30 | 'attr': { 31 | 'class': "emul_voice_trigger main" 32 | }, 33 | html: val.itemText, 34 | appendTo: $bar 35 | }); 36 | }); 37 | } 38 | if (voicehelp.candidateList) { 39 | $.each(voicehelp.candidateList, function (key, val) { 40 | $('
', { 41 | 'attr': { 42 | 'class': "emul_voice_trigger" 43 | }, 44 | html: val.candidate, 45 | appendTo: $bar 46 | }); 47 | }); 48 | } 49 | } 50 | }); 51 | }); 52 | })(jQuery); -------------------------------------------------------------------------------- /src/platforms/dune/player.dune.js: -------------------------------------------------------------------------------- 1 | SB.readyForPlatform('dune', function () { 2 | 3 | var updateInterval; 4 | var startUpdate = function () { 5 | var lastTime = 0; 6 | updateInterval = setInterval(function () { 7 | var position = stb.getPositionInSeconds(); 8 | //if (position != lastTime) { 9 | Player.videoInfo.currentTime = position; 10 | Player.trigger('update'); 11 | SB.utils.log.state(position, 'position', 'player'); 12 | //} 13 | //lastTime = position; 14 | }, 500); 15 | } 16 | var stopUpdate = function () { 17 | clearInterval(updateInterval); 18 | } 19 | 20 | function handle_event(pstate, cstate, lastEvent){ 21 | data = cstate; 22 | data += ''; 23 | if (data == '4') { 24 | Player.trigger('complete'); 25 | } else if (data == '3') { 26 | if (!stb) return; 27 | if (stb.hasLength()){ 28 | Player.videoInfo.duration = stb.getLengthInSeconds() + 1; 29 | Player.videoInfo.currentTime = 0; 30 | Player.trigger('ready'); 31 | } 32 | } 33 | } 34 | 35 | function getStb(){ 36 | return $('body > div > object'); 37 | } 38 | 39 | var stb = getStb(); 40 | 41 | Player.extend({ 42 | _init: function () { 43 | //stb.SetViewport(1280, 720, 0, 0); 44 | //stb.SetTopWin(0); 45 | }, 46 | _play: function (options) { 47 | stb.play(options.url); 48 | startUpdate(); 49 | Player.trigger('bufferingBegin'); 50 | }, 51 | _stop: function () { 52 | stb.stop(); 53 | stopUpdate(); 54 | }, 55 | pause: function () { 56 | stb.pause(); 57 | this.state = "pause"; 58 | stopUpdate(); 59 | }, 60 | resume: function () { 61 | stb.resume(); 62 | this.state = "play"; 63 | startUpdate(); 64 | }, 65 | seek: function (time) { 66 | stb.setPositionInSeconds(time) 67 | }, 68 | audio: { 69 | set: function (index) { 70 | stb.setAudioTrack(index); 71 | }, 72 | get: function () { 73 | return stb.getAudioTracksDescription(); 74 | }, 75 | cur: function () { 76 | return stb.getAudioTrack(); 77 | } 78 | }, 79 | subtitle: { 80 | set: function (index) { 81 | stb.setSubtitleTrack(index); 82 | }, 83 | get: function () { 84 | var subtitles = []; 85 | _.each(stb.getSubtitleTracksDescription(), function (self) { 86 | subtitles.push({index: self.index, language: self.lang[1]}); 87 | }); 88 | return subtitles; 89 | }, 90 | cur: function () { 91 | return stb.getSubtitleTrack(); 92 | } 93 | } 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /src/platforms/dune/sb.platform.dune.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | var stb; 4 | /** 5 | * Dune set top box platform description 6 | */ 7 | SB.createPlatform('dune', { 8 | keys: { 9 | RIGHT: 39, 10 | LEFT: 37, 11 | DOWN: 40, 12 | UP: 38, 13 | RETURN: 8, 14 | EXIT: 27, 15 | TOOLS: 122, 16 | FF: 205, 17 | RW: 204, 18 | NEXT: 176, 19 | PREV: 177, 20 | ENTER: 13, 21 | RED: 193, 22 | GREEN: 194, 23 | YELLOW: 195, 24 | BLUE: 196, 25 | CH_UP: 33, 26 | CH_DOWN: 34, 27 | N0: 48, 28 | N1: 49, 29 | N2: 50, 30 | N3: 51, 31 | N4: 52, 32 | N5: 53, 33 | N6: 54, 34 | N7: 55, 35 | N8: 56, 36 | N9: 57, 37 | //PRECH: 116, 38 | //POWER: 85, 39 | //SMART: 36, 40 | PLAY: 218, 41 | STOP: 178, 42 | DUNE: 209, 43 | //PAUSE: 99, 44 | //SUBT: 76, 45 | INFO: 199 46 | //REC: 82 47 | }, 48 | 49 | onDetect: function () { 50 | 51 | var isStandBy = false; 52 | 53 | // prohibition of keyboard showing on click keyboard button 54 | //stb.EnableVKButton(false); 55 | 56 | // window.moveTo(0, 0); 57 | // window.resizeTo(1280, 720); 58 | 59 | SB(function () { 60 | var $body = $(document.body); 61 | 62 | stb = this.createDunePlugin(); 63 | 64 | if (stb) { 65 | stb.init(); 66 | }else{ 67 | $$log('unable to init stb'); 68 | } 69 | 70 | $body.on('nav_key:dune', function () { 71 | if (stb) stb.launchNativeUi(); 72 | }); 73 | }); 74 | 75 | }, 76 | 77 | createDunePlugin: function () { 78 | try { 79 | var parentNode = document.getElementsByTagName("body")[0]; 80 | console.log(parentNode); 81 | var obj = document.createElement("div"); 82 | obj.innerHTML = ''; 83 | parentNode.appendChild(obj); 84 | return obj.getElementsByTagName("object")[0]; 85 | } catch (e) { 86 | return undefined; 87 | } 88 | }, 89 | 90 | detect: function () { 91 | return !!stb; 92 | }, 93 | 94 | exit: function () { 95 | $$log('try to location change'); 96 | Player.stop(true); 97 | if (stb){ 98 | stb.launchNativeUi(); 99 | } 100 | }, 101 | 102 | sendReturn: function () { 103 | this.exit(); 104 | }, 105 | 106 | getMac: function () { 107 | return stb.getMacAddress(); 108 | }, 109 | 110 | getNativeDUID: function () { 111 | return stb.getSerialNumber(); 112 | } 113 | }); 114 | 115 | }()); 116 | 117 | -------------------------------------------------------------------------------- /src/platforms/lg/player.lg.js: -------------------------------------------------------------------------------- 1 | SB.readyForPlatform('lg', function () { 2 | var updateInterval; 3 | 4 | var isReady = false; 5 | 6 | Player.extend({ 7 | updateDelay: 500, 8 | _init: function () { 9 | var self = this; 10 | $('body').append(''); 11 | this.plugin = $('#pluginPlayer')[0]; 12 | this.$plugin = $(this.plugin); 13 | this.plugin.onPlayStateChange = function () { 14 | self.onEvent.apply(self, arguments); 15 | } 16 | this.plugin.onBuffering = function () { 17 | self.onBuffering.apply(self, arguments); 18 | } 19 | }, 20 | onEvent: function(){ 21 | if(this.plugin.playState=='5'){ 22 | this.state='stop'; 23 | this.trigger('complete'); 24 | } 25 | }, 26 | _update: function () { 27 | var info = this.plugin.mediaPlayInfo(); 28 | 29 | if (info && !isReady) { 30 | //$('#log').append('
'+info.duration+'
'); 31 | isReady = true; 32 | 33 | this.trigger('ready'); 34 | this.videoInfo = { 35 | duration: info.duration / 1000 36 | }; 37 | } 38 | 39 | 40 | this.videoInfo.currentTime=info.currentPosition/1000; 41 | 42 | 43 | this.trigger('update'); 44 | }, 45 | onBuffering: function (isStarted) { 46 | this.trigger(isStarted ? 'bufferingBegin' : 'bufferingEnd'); 47 | }, 48 | _play: function (options) { 49 | clearInterval(updateInterval); 50 | updateInterval = setInterval(function () { 51 | 52 | Player._update(); 53 | }, this.updateDelay); 54 | isReady = false; 55 | this.plugin.data = options.url; 56 | this.plugin.play(1); 57 | }, 58 | pause: function(){ 59 | this.plugin.play(0); 60 | this.state="pause"; 61 | }, 62 | resume: function(){ 63 | this.plugin.play(1); 64 | this.state="play"; 65 | }, 66 | _stop: function () { 67 | this.plugin.stop(); 68 | this.state="stop"; 69 | }, 70 | seek: function(time){ 71 | this.plugin.seek(time*1000); 72 | } 73 | }); 74 | }); -------------------------------------------------------------------------------- /src/platforms/lg/sb.platform.lg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * LG platform 3 | */ 4 | 5 | SB.createPlatform('lg', { 6 | platformUserAgent: 'netcast', 7 | 8 | keys: { 9 | ENTER: 13, 10 | PAUSE: 19, 11 | LEFT: 37, 12 | UP: 38, 13 | RIGHT: 39, 14 | DOWN: 40, 15 | N0: 48, 16 | N1: 49, 17 | N2: 50, 18 | N3: 51, 19 | N4: 52, 20 | N5: 53, 21 | N6: 54, 22 | N7: 55, 23 | N8: 56, 24 | N9: 57, 25 | RED: 403, 26 | GREEN: 404, 27 | YELLOW: 405, 28 | BLUE: 406, 29 | RW: 412, 30 | STOP: 413, 31 | PLAY: 415, 32 | FF: 417, 33 | RETURN: 461, 34 | CH_UP: 33, 35 | CH_DOWN: 34 36 | }, 37 | 38 | getNativeDUID: function () { 39 | return this.device.serialNumber; 40 | }, 41 | 42 | getMac: function () { 43 | return this.device.net_macAddress.replace(/:/g, ''); 44 | }, 45 | 46 | getSDI: $.noop, 47 | 48 | setPlugins: function () { 49 | //this._listenGestureEvent(); 50 | 51 | $('body').append(''); 52 | this.device = $('#device')[0]; 53 | 54 | this.modelCode = this.device.version; 55 | this.productCode = this.device.modelName; 56 | 57 | this.getDUID(); 58 | 59 | 60 | //Log.show('default'); 61 | setInterval(function () { 62 | //Log.show('default'); 63 | var usedMemorySize; 64 | if (window.NetCastGetUsedMemorySize) { 65 | usedMemorySize = window.NetCastGetUsedMemorySize(); 66 | } 67 | //Log.state(Math.floor(usedMemorySize * 100 / (1024 * 1024)) / 100, 'memory', 'profiler'); 68 | }, 5000); 69 | 70 | 71 | if (Player && Player.setPlugin) { 72 | Player.setPlugin(); 73 | } 74 | }, 75 | 76 | sendReturn: function () { 77 | if (Player) { 78 | Player.stop(true); 79 | } 80 | window.NetCastBack(); 81 | }, 82 | 83 | exit: function () { 84 | Player && Player.stop(true); 85 | window.NetCastExit(); 86 | }, 87 | 88 | getUsedMemory: function () { 89 | return window.NetCastGetUsedMemorySize(); 90 | }, 91 | getChildlockPin: function () { 92 | return 1234; 93 | } 94 | }); -------------------------------------------------------------------------------- /src/platforms/mag/player.mag.js: -------------------------------------------------------------------------------- 1 | SB.readyForPlatform('mag', function () { 2 | 3 | var updateInterval; 4 | var startUpdate = function () { 5 | var lastTime = 0; 6 | updateInterval = setInterval(function () { 7 | var position = stb.GetPosTime(); 8 | //if (position != lastTime) { 9 | Player.videoInfo.currentTime = position; 10 | Player.trigger('update'); 11 | SB.utils.log.state(position, 'position', 'player'); 12 | //} 13 | //lastTime = position; 14 | }, 500); 15 | } 16 | var stopUpdate = function () { 17 | clearInterval(updateInterval); 18 | } 19 | 20 | window.stbEvent = 21 | { 22 | 23 | onEvent: function (data) { 24 | 25 | data += ''; 26 | if (data == '1') { 27 | Player.trigger('complete'); 28 | } else if (data == '2') { 29 | Player.videoInfo.duration = stb.GetMediaLen() + 1; 30 | Player.videoInfo.currentTime = 0; 31 | Player.trigger('ready'); 32 | } 33 | else if (data == '4') { 34 | Player.trigger('bufferingEnd'); 35 | } 36 | else if (data == '7') { 37 | var vi = eval(stb.GetVideoInfo()); 38 | Player.videoInfo.width = vi.pictureWidth; 39 | Player.videoInfo.height = vi.pictureHeight; 40 | } 41 | }, 42 | event: 0 43 | }; 44 | 45 | 46 | var stb = window.gSTB; 47 | Player.extend({ 48 | _init: function () { 49 | stb.InitPlayer(); 50 | stb.SetViewport(1280, 720, 0, 0); 51 | stb.SetTopWin(0); 52 | }, 53 | _play: function (options) { 54 | stb.Play(options.url); 55 | startUpdate(); 56 | Player.trigger('bufferingBegin'); 57 | }, 58 | _stop: function () { 59 | stb.Stop(); 60 | stopUpdate(); 61 | }, 62 | pause: function () { 63 | stb.Pause(); 64 | this.state = "pause"; 65 | stopUpdate(); 66 | }, 67 | resume: function () { 68 | stb.Continue(); 69 | this.state = "play"; 70 | startUpdate(); 71 | }, 72 | seek: function (time) { 73 | stb.SetPosTime(time) 74 | }, 75 | audio: { 76 | set: function (index) { 77 | stb.SetAudioPID(index); 78 | }, 79 | get: function () { 80 | return stb.GetAudioPIDs(); 81 | }, 82 | cur: function () { 83 | return stb.GetAudioPID(); 84 | } 85 | }, 86 | subtitle: { 87 | set: function (index) { 88 | stb.SetSubtitlePID(index); 89 | }, 90 | get: function () { 91 | var subtitles = []; 92 | _.each(stb.GetSubtitlePIDs(), function (self) { 93 | subtitles.push({index: self.pid, language: self.lang[1]}); 94 | }); 95 | return subtitles; 96 | }, 97 | cur: function () { 98 | return stb.GetSubtitlePID(); 99 | } 100 | } 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /src/platforms/mag/sb.platform.mag.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | var stb; 4 | /** 5 | * Mag set top box platform description 6 | */ 7 | SB.createPlatform('mag', { 8 | keys: { 9 | RIGHT: 39, 10 | LEFT: 37, 11 | DOWN: 40, 12 | UP: 38, 13 | RETURN: 8, 14 | EXIT: 27, 15 | TOOLS: 122, 16 | FF: 70, 17 | RW: 66, 18 | NEXT: 34, 19 | PREV: 33, 20 | ENTER: 13, 21 | RED: 112, 22 | GREEN: 113, 23 | YELLOW: 114, 24 | BLUE: 115, 25 | CH_UP: 9, 26 | CH_DOWN: 9, 27 | N0: 48, 28 | N1: 49, 29 | N2: 50, 30 | N3: 51, 31 | N4: 52, 32 | N5: 53, 33 | N6: 54, 34 | N7: 55, 35 | N8: 56, 36 | N9: 57, 37 | PRECH: 116, 38 | POWER: 85, 39 | //SMART: 36, 40 | PLAY: 82, 41 | STOP: 83, 42 | //PAUSE: 99, 43 | //SUBT: 76, 44 | INFO: 89 45 | //REC: 82 46 | }, 47 | 48 | onDetect: function () { 49 | 50 | var isStandBy = false; 51 | 52 | stb = window.gSTB; 53 | 54 | // prohibition of keyboard showing on click keyboard button 55 | stb.EnableVKButton(false); 56 | 57 | window.moveTo(0, 0); 58 | window.resizeTo(1280, 720); 59 | 60 | SB(function () { 61 | var $body = $(document.body); 62 | $body.on('nav_key:power', function () { 63 | var eventName = 'standby_'; 64 | isStandBy = !isStandBy; 65 | 66 | eventName += isStandBy ? 'set' : 'unset'; 67 | stb.StandBy(isStandBy); 68 | 69 | // TODO: trigger events on SB 70 | $$log('trigger standby event ' + eventName, 'standby'); 71 | $body.trigger(eventName); 72 | }); 73 | }); 74 | 75 | window.localStorage = { 76 | setItem: function ( name, data ) { 77 | if (typeof data === 'object') { 78 | data = JSON.stringify(data); 79 | } 80 | stb.SaveUserData(name, data); 81 | }, 82 | clear: function () { 83 | 84 | }, 85 | getItem: function (name) { 86 | return stb.LoadUserData(name); 87 | }, 88 | removeItem: function (name) { 89 | stb.SaveUserData(name, null); 90 | } 91 | } 92 | }, 93 | 94 | detect: function () { 95 | return !!window.gSTB; 96 | }, 97 | 98 | exit: function () { 99 | $$log('try to location change'); 100 | Player.stop(true); 101 | gSTB.DeinitPlayer(); 102 | window.location = 'file:///home/web/services.html'; 103 | }, 104 | 105 | sendReturn: function () { 106 | this.exit(); 107 | }, 108 | 109 | getMac: function () { 110 | return stb.GetDeviceMacAddress(); 111 | }, 112 | 113 | getNativeDUID: function () { 114 | return stb.GetDeviceSerialNumber(); 115 | } 116 | }); 117 | 118 | }()); 119 | 120 | -------------------------------------------------------------------------------- /src/platforms/philips/player.philips.js: -------------------------------------------------------------------------------- 1 | SB.readyForPlatform('philips', function () { 2 | var video; 3 | 4 | 5 | var updateInterval; 6 | var ready = false; 7 | 8 | var startUpdate = function () { 9 | var lastTime = 0; 10 | updateInterval = setInterval(function () { 11 | if (video.playPosition != lastTime) { 12 | Player.videoInfo.currentTime = video.playPosition / 1000; 13 | Player.trigger('update'); 14 | } 15 | lastTime = video.playPosition; 16 | }, 500); 17 | } 18 | 19 | var stopUpdate = function () { 20 | clearInterval(updateInterval); 21 | } 22 | 23 | function checkPlayState() { 24 | //$('#log').append('
' + video.playState + '
'); 25 | 26 | 27 | //some hack 28 | //in my tv player can sent lesser than 1 time, and correct time after 29 | if (video.playTime > 1) { 30 | 31 | if (!ready) { 32 | //+1 for test pass 33 | Player.videoInfo.duration = (video.playTime / 1000)+1; 34 | Player.trigger('ready'); 35 | ready = true; 36 | } 37 | } 38 | 39 | switch (video.playState) { 40 | case 5: // finished 41 | Player.trigger('complete'); 42 | stopUpdate(); 43 | Player.state = "stop"; 44 | break; 45 | case 0: // stopped 46 | Player.state = "stop"; 47 | break; 48 | case 6: // error 49 | Player.trigger('error'); 50 | break; 51 | case 1: // playing 52 | Player.trigger('bufferingEnd'); 53 | startUpdate(); 54 | break; 55 | case 2: // paused 56 | 57 | case 3: // connecting 58 | 59 | case 4: // buffering 60 | Player.trigger('bufferingBegin'); 61 | stopUpdate(); 62 | break; 63 | default: 64 | // do nothing 65 | break; 66 | } 67 | } 68 | 69 | Player.extend({ 70 | _init: function () { 71 | $('body').append('
\n\ 72 | \n\ 73 | '); 74 | video = document.getElementById('videoPhilips'); 75 | video.onPlayStateChange = checkPlayState; 76 | }, 77 | _play: function (options) { 78 | video.data = options.url; 79 | video.play(1); 80 | ready = false; 81 | Player.trigger('bufferingBegin'); 82 | }, 83 | _stop: function () { 84 | video.stop(); 85 | stopUpdate(); 86 | }, 87 | pause: function () { 88 | video.play(0); 89 | this.state = "pause"; 90 | stopUpdate(); 91 | }, 92 | resume: function () { 93 | video.play(1); 94 | this.state = "play"; 95 | startUpdate(); 96 | }, 97 | seek: function (time) { 98 | //-10 for test pass 99 | video.seek((time - 10) * 1000); 100 | } 101 | }); 102 | }); -------------------------------------------------------------------------------- /src/platforms/philips/sb.platform.philips.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Philips platform 3 | */ 4 | SB.createPlatform('philips', { 5 | platformUserAgent: 'nettv', 6 | setPlugins: function () { 7 | this.keys = { 8 | ENTER: VK_ENTER, 9 | PAUSE: VK_PAUSE, 10 | LEFT: VK_LEFT, 11 | UP: VK_UP, 12 | RIGHT: VK_RIGHT, 13 | DOWN: VK_DOWN, 14 | N0: VK_0, 15 | N1: VK_1, 16 | N2: VK_2, 17 | N3: VK_3, 18 | N4: VK_4, 19 | N5: VK_5, 20 | N6: VK_6, 21 | N7: VK_7, 22 | N8: VK_8, 23 | N9: VK_9, 24 | RED: VK_RED, 25 | GREEN: VK_GREEN, 26 | YELLOW: VK_YELLOW, 27 | BLUE: VK_BLUE, 28 | RW: VK_REWIND, 29 | STOP: VK_STOP, 30 | PLAY: VK_PLAY, 31 | FF: VK_FAST_FWD, 32 | RETURN: VK_BACK, 33 | CH_UP: VK_PAGE_UP, 34 | CH_DOWN: VK_PAGE_DOWN 35 | }; 36 | } 37 | }); -------------------------------------------------------------------------------- /src/platforms/samsung/localstorage.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | var localStorage = window.localStorage, 4 | fileSysObj, 5 | commonDir, 6 | fileName, 7 | fileObj; 8 | 9 | //if Samsung 11 10 | 11 | if (_.isFunction(window.FileSystem)) { 12 | 13 | fileSysObj = new FileSystem(); 14 | commonDir = fileSysObj.isValidCommonPath(curWidget.id); 15 | 16 | if ( !commonDir ) { 17 | fileSysObj.createCommonDir(curWidget.id); 18 | } 19 | fileName = curWidget.id + "_localStorage.db"; 20 | fileObj = fileSysObj.openCommonFile(fileName, "r+"); 21 | 22 | if ( fileObj ) { 23 | try { 24 | JSON.parse(fileObj.readAll()); 25 | } catch (e) { 26 | localStorage && localStorage.clear(); 27 | } 28 | } else { 29 | fileObj = fileSysObj.openCommonFile(fileName, "w"); 30 | fileObj.writeAll("{}"); 31 | } 32 | fileSysObj.closeCommonFile(fileObj); 33 | 34 | if ( !localStorage) { 35 | var lStorage = {}, 36 | changed = false; 37 | 38 | var saveStorage = _.debounce(function saveStorage() { 39 | if (changed) { 40 | fileObj = fileSysObj.openCommonFile(fileName, "w"); 41 | fileObj.writeAll(JSON.stringify(window.localStorage)); 42 | fileSysObj.closeCommonFile(fileObj); 43 | changed = false; 44 | } 45 | },100); 46 | 47 | 48 | lStorage.setItem = function ( key, value ) { 49 | changed = true; 50 | this[key] = value; 51 | saveStorage(); 52 | return this[key]; 53 | }; 54 | lStorage.getItem = function ( key ) { 55 | return this[key]; 56 | }; 57 | lStorage.removeItem = function ( key ) { 58 | delete this[key]; 59 | saveStorage(); 60 | }; 61 | lStorage.clear = function () { 62 | var self = this; 63 | for ( var key in self ) { 64 | if ( typeof self[key] != 'function' ) { 65 | delete self[key]; 66 | } 67 | } 68 | saveStorage(); 69 | }; 70 | window.localStorage = lStorage; 71 | } 72 | } 73 | }()); -------------------------------------------------------------------------------- /src/platforms/samsung/sb.platform.samsung.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Samsung platform 3 | */ 4 | !(function (window, undefined) { 5 | 6 | 7 | var 8 | document=window.document, 9 | /** 10 | * Native plugins 11 | * id: clsid (DOM element id : CLSID) 12 | * @type {{object}} 13 | */ 14 | plugins = { 15 | audio: 'SAMSUNG-INFOLINK-AUDIO', 16 | pluginObjectTV: 'SAMSUNG-INFOLINK-TV', 17 | pluginObjectTVMW: 'SAMSUNG-INFOLINK-TVMW', 18 | pluginObjectNetwork: 'SAMSUNG-INFOLINK-NETWORK', 19 | pluginObjectNNavi: 'SAMSUNG-INFOLINK-NNAVI', 20 | pluginPlayer: 'SAMSUNG-INFOLINK-PLAYER' 21 | }, 22 | samsungFiles = [ 23 | '$MANAGER_WIDGET/Common/af/../webapi/1.0/deviceapis.js', 24 | '$MANAGER_WIDGET/Common/af/../webapi/1.0/serviceapis.js', 25 | '$MANAGER_WIDGET/Common/af/2.0.0/extlib/jquery.tmpl.js', 26 | '$MANAGER_WIDGET/Common/Define.js', 27 | '$MANAGER_WIDGET/Common/af/2.0.0/sf.min.js', 28 | '$MANAGER_WIDGET/Common/API/Plugin.js', 29 | '$MANAGER_WIDGET/Common/API/Widget.js', 30 | '$MANAGER_WIDGET/Common/API/TVKeyValue.js', 31 | 'src/platforms/samsung/localstorage.js' 32 | ]; 33 | 34 | SB.createPlatform('samsung', { 35 | 36 | $plugins: {}, 37 | platformUserAgent: 'maple', 38 | 39 | onDetect: function () { 40 | // non-standart inserting objects in DOM (i'm looking at you 2011 version) 41 | // in 2011 samsung smart tv's we can't add objects if document is ready 42 | 43 | var htmlString = ''; 44 | for (var i = 0; i < samsungFiles.length; i++) { 45 | htmlString += ''; 46 | } 47 | for (var id in plugins) { 48 | htmlString += ''; 49 | } 50 | document.write(htmlString); 51 | }, 52 | 53 | getNativeDUID: function () { 54 | return this.$plugins.pluginObjectNNavi.GetDUID(this.getMac()); 55 | }, 56 | 57 | getMac: function () { 58 | return this.$plugins.pluginObjectNetwork.GetMAC(); 59 | }, 60 | 61 | getSDI: function () { 62 | this.SDI = this.$plugins.SDIPlugin.Execute('GetSDI_ID'); 63 | return this.SDI; 64 | }, 65 | 66 | /** 67 | * Return hardware version for 2013 samsung only 68 | * @returns {*} 69 | */ 70 | getHardwareVersion: function () { 71 | var version = this.firmware.match(/\d{4}/) || []; 72 | if (version[0] === '2013') { 73 | this.hardwareVersion = sf.core.sefplugin('Device').Execute('Firmware'); 74 | } else { 75 | this.hardwareVersion = null; 76 | } 77 | return this.hardwareVersion; 78 | }, 79 | 80 | setPlugins: function () { 81 | var self = this, 82 | PL_NNAVI_STATE_BANNER_NONE = 0, 83 | PL_NNAVI_STATE_BANNER_VOL = 1, 84 | PL_NNAVI_STATE_BANNER_VOL_CH = 2, 85 | tvKey; 86 | 87 | _.each(plugins, function (clsid, id) { 88 | self.$plugins[id] = document.getElementById(id); 89 | }); 90 | 91 | this.$plugins.SDIPlugin = sf.core.sefplugin('ExternalWidgetInterface'); 92 | this.$plugins.tvKey = new Common.API.TVKeyValue(); 93 | 94 | var NNAVIPlugin = this.$plugins.pluginObjectNNavi, 95 | TVPlugin = this.$plugins.pluginObjectTV; 96 | 97 | this.modelCode = NNAVIPlugin.GetModelCode(); 98 | this.firmware = NNAVIPlugin.GetFirmware(); 99 | this.systemVersion = NNAVIPlugin.GetSystemVersion(0); 100 | this.productCode = TVPlugin.GetProductCode(1); 101 | 102 | this.pluginAPI = new Common.API.Plugin(); 103 | this.widgetAPI = new Common.API.Widget(); 104 | 105 | tvKey = new Common.API.TVKeyValue(); 106 | this.productType = TVPlugin.GetProductType(); 107 | 108 | this.setKeys(); 109 | 110 | if(this.pluginAPI.SetBannerState){ 111 | NNAVIPlugin.SetBannerState(PL_NNAVI_STATE_BANNER_VOL_CH); 112 | } 113 | 114 | function unregisterKey(key){ 115 | try{ 116 | self.pluginAPI.unregistKey(tvKey['KEY_'+key]); 117 | }catch(e){ 118 | $$error(e); 119 | } 120 | } 121 | 122 | unregisterKey('VOL_UP'); 123 | unregisterKey('VOL_DOWN'); 124 | unregisterKey('MUTE'); 125 | 126 | this.widgetAPI.sendReadyEvent(); 127 | }, 128 | 129 | /** 130 | * Set keys for samsung platform 131 | */ 132 | setKeys: function () { 133 | 134 | this.keys = sf.key; 135 | 136 | document.body.onkeydown = function ( event ) { 137 | var keyCode = event.keyCode; 138 | $$log('keyDown ' + keyCode); 139 | 140 | switch ( keyCode ) { 141 | case sf.key.RETURN: 142 | //case sf.key.EXIT: 143 | case 147: 144 | case 261: 145 | sf.key.preventDefault(); 146 | break; 147 | default: 148 | break; 149 | } 150 | } 151 | }, 152 | 153 | /** 154 | * Start screensaver 155 | * @param time 156 | */ 157 | enableScreenSaver: function (time) { 158 | time = time || false; 159 | sf.service.setScreenSaver(true, time); 160 | }, 161 | 162 | /** 163 | * Disable screensaver 164 | */ 165 | disableScreenSaver: function () { 166 | sf.service.setScreenSaver(false); 167 | }, 168 | 169 | exit: function () { 170 | sf.core.exit(false); 171 | }, 172 | 173 | sendReturn: function () { 174 | sf.core.exit(true); 175 | }, 176 | 177 | blockNavigation: function () { 178 | sf.key.preventDefault(); 179 | } 180 | }); 181 | 182 | })(this); -------------------------------------------------------------------------------- /src/platforms/samsung/voicelink.samsung.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | "use strict"; 3 | 4 | 5 | 6 | SB.readyForPlatform('samsung', function(){ 7 | var voiceServer; 8 | 9 | /** 10 | * Обработка нативных событий распознавания голоса 11 | * @param evt событие от самсунга 12 | */ 13 | var handleRecognitionEvent = function (evt) { 14 | 15 | switch (evt.eventtype) { 16 | case "EVENT_VOICE_END_MONITOR": 17 | //не работает в телевизоре 18 | break; 19 | case "EVENT_VOICE_BEGIN_MONITOR": 20 | case "EVENT_VOICE_BTSOUND_START": 21 | //this.updateVoiceKeyHelp(); 22 | /*if (paused) { 23 | break; 24 | } 25 | $('body').trigger('voiceStart'); 26 | if (helperWasShowed < defaults.showHelperTimes) { 27 | helperWasShowed++; 28 | $helpBubble.html(defaults.helpText).show(); 29 | }*/ 30 | 31 | 32 | $$voice.refresh(); 33 | 34 | $$voice._resetVisibilityTimeout(); 35 | 36 | /* 37 | if ($curTarget) { 38 | doAll.call($curTarget, curOptions); 39 | }*/ 40 | break; 41 | case "EVENT_VOICE_RECOG_RESULT": 42 | 43 | var result = evt.result.toLowerCase(); 44 | //если не голосовой поиск 45 | if (typeof voiceServer != 'function') { 46 | $$voice.say(result); 47 | } 48 | else { 49 | voiceServer(result); 50 | voiceServer = false; 51 | $$voice.restore(); 52 | } 53 | break; 54 | } 55 | }; 56 | _.extend($$voice, { 57 | _init: function(){ 58 | deviceapis.recognition.SubscribeExEvent(deviceapis.recognition.PL_RECOGNITION_TYPE_VOICE, "Smartbox", function (evt) { 59 | handleRecognitionEvent(evt); 60 | }); 61 | deviceapis.recognition.SetVoiceTimeout(this.voiceTimeout); 62 | $('body').append('
'); 63 | }, 64 | _nativeCheckSupport: function(){ 65 | var enabled=false; 66 | try { 67 | enabled = deviceapis.recognition.IsRecognitionSupported(); 68 | } catch (e) { 69 | } 70 | return enabled; 71 | }, 72 | _nativeFromServer: function(title, callback){ 73 | voiceServer = callback; 74 | var describeHelpbar = { 75 | helpbarType: "HELPBAR_TYPE_VOICE_SERVER_GUIDE_RETURN", 76 | guideText: title 77 | }; 78 | 79 | deviceapis.recognition.SetVoiceHelpbarInfo(JSON.stringify(describeHelpbar)); 80 | }, 81 | _setVoiceHelp: function(voicehelp){ 82 | deviceapis.recognition.SetVoiceHelpbarInfo(JSON.stringify(voicehelp)); 83 | }, 84 | _nativeTurnOff: function(){ 85 | deviceapis.recognition.SetVoiceHelpbarInfo(JSON.stringify({ 86 | helpbarType: "HELPBAR_TYPE_VOICE_CUSTOMIZE", 87 | bKeepCurrentInfo: "false", 88 | helpbarItemsList: [] 89 | })); 90 | } 91 | }); 92 | }); 93 | })(jQuery); -------------------------------------------------------------------------------- /src/plugins/legend.js: -------------------------------------------------------------------------------- 1 | (function (window) { 2 | "use strict"; 3 | /*globals _, ViewModel,$,Events,document, Observable, Computed, Lang, nav*/ 4 | var icons = ['info','red', 'green', 'yellow', 'blue', 'rew', 'play', 'pause', 'stop', 'ff', 'tools', 'left', 5 | 'right', 'up', 'down', 'leftright', 'updown', 'move', 'number', 'enter', 'ret'], 6 | 7 | notClickableKeys= ['leftright', 'left', 'right', 'up', 'down', 'updown', 'move', 'number'], 8 | _isInited, 9 | LegendKey, 10 | savedLegend = [], 11 | Legend; 12 | 13 | function isClickable( key ) { 14 | return (notClickableKeys.indexOf(key) === -1) 15 | } 16 | 17 | function renderKey( key ) { 18 | var clickableClass = isClickable(key) ? ' legend-clickable' : ''; 19 | return '
' + 20 | '' + 21 | '' + 22 | '
'; 23 | } 24 | 25 | function _renderLegend() { 26 | var legendEl, 27 | wrap, 28 | allKeysHtml = ''; 29 | 30 | for (var i = 0, len = icons.length; i', { 16 | id: 'log' 17 | }); 18 | 19 | $(function () { 20 | $logWrap.appendTo(document.body); 21 | }); 22 | 23 | $logRow = $('
', { 24 | 'class': 'log-row' 25 | }); 26 | 27 | /** 28 | * LogPanel constructor 29 | * @param logName {String} name of log panel 30 | * @constructor 31 | */ 32 | LogPanel = function ( logName ) { 33 | this.name = logName; 34 | this.logs = 0; 35 | this.states = {}; 36 | 37 | var $wrapper = $logWrap.find('#log_' + this.name); 38 | 39 | this.$content = $wrapper.find('.log_content'); 40 | this.$state = $wrapper.find('.log_states'); 41 | 42 | // no need anymore 43 | $wrapper = null; 44 | }; 45 | 46 | _.extend(LogPanel.prototype, { 47 | log: function log ( msg ) { 48 | var logRow = $logRow.clone(), 49 | $rows, length; 50 | this.logs++; 51 | msg = _.escape(msg); 52 | 53 | logRow.html(msg).appendTo(this.$content); 54 | if ( this.logs > maxLogCount ) { 55 | $rows = this.$content.find(".log-row"); 56 | length = $rows.length; 57 | $rows.slice(0, length - maxLogCount).remove(); 58 | this.logs = $rows.length; 59 | } 60 | }, 61 | 62 | state: function state ( value, stateName ) { 63 | var state = this.states[stateName] || this.createState(stateName); 64 | state.textContent = stateName + ': ' + value; 65 | }, 66 | 67 | createState: function ( stateName ) { 68 | var $state = document.createElement('div'); 69 | $state.id = '#log_' + this.name + '_' + stateName; 70 | this.states[stateName] = $state; 71 | this.$state.append($state); 72 | 73 | return $state; 74 | } 75 | }); 76 | 77 | var logPanelTemplate = '
' + 78 | '
Log: <%=name%>
' + 79 | '
' + 80 | '
' + 81 | '
' + 82 | '
' + 83 | '
'; 84 | 85 | Log = { 86 | 87 | create: function ( logName ) { 88 | var logHtml = logPanelTemplate.replace(/<%=name%>/g, logName); 89 | $logWrap.append(logHtml); 90 | logs[logName] = new LogPanel(logName); 91 | logNames.push(logName); 92 | return logs[logName]; 93 | }, 94 | 95 | getPanel: function ( logName ) { 96 | logName = logName || 'default'; 97 | return (logs[logName] || this.create(logName)); 98 | } 99 | }; 100 | 101 | /** 102 | * Public log API 103 | */ 104 | window.SB.utils.log = { 105 | log: function ( msg, logName ) { 106 | Log.getPanel(logName).log(msg); 107 | }, 108 | 109 | state: function ( msg, state, logName ) { 110 | Log.getPanel(logName).state(msg, state); 111 | }, 112 | 113 | show: function ( logName ) { 114 | logName = logName || logNames[curPanelIndex]; 115 | 116 | if ( !logName ) { 117 | curPanelIndex = 0; 118 | this.hide(); 119 | } else { 120 | curPanelIndex++; 121 | $logWrap.show(); 122 | $('.log_pane').hide(); 123 | $('#log_' + logName).show(); 124 | } 125 | }, 126 | 127 | hide: function () { 128 | $logWrap.hide(); 129 | }, 130 | 131 | startProfile: function ( profileName ) { 132 | if ( profileName ) { 133 | profiles[profileName] = (new Date()).getTime(); 134 | } 135 | }, 136 | 137 | stopProfile: function ( profileName ) { 138 | if ( profiles[profileName] ) { 139 | this.log(profileName + ': ' + ((new Date()).getTime() - profiles[profileName]) + 'ms', 'profiler'); 140 | delete profiles[profileName]; 141 | } 142 | } 143 | }; 144 | window.$$log = SB.utils.log.log; 145 | window.$$error = SB.utils.error; 146 | 147 | })(this); 148 | 149 | $(function () { 150 | var logKey = SB.config.logKey || 'tools'; 151 | $(document.body).on('nav_key:' + logKey, function () { 152 | SB.utils.log.show(); 153 | }); 154 | }); 155 | 156 | -------------------------------------------------------------------------------- /src/sb.js: -------------------------------------------------------------------------------- 1 | ;(function () { 2 | var Smartbox, 3 | _ready = false, 4 | readyCallbacks = [], 5 | userAgent = navigator.userAgent.toLowerCase(), 6 | SmartboxAPI; 7 | 8 | //private func for applying all ready callbacks 9 | var onReady = function () { 10 | _ready = true; 11 | 12 | for ( var i = 0, len = readyCallbacks.length; i < len; i++ ) { 13 | if (typeof readyCallbacks[i] === 'function') { 14 | readyCallbacks[i].call(this); 15 | } 16 | } 17 | // no need anymore 18 | readyCallbacks = null; 19 | }; 20 | 21 | /** 22 | * Detecting current platform 23 | * @returns {boolean} true if running on current platform 24 | */ 25 | function detect ( slug ) { 26 | return userAgent.indexOf(slug) !== -1; 27 | } 28 | 29 | var initialise = function() { 30 | Smartbox.setPlugins(); 31 | Smartbox.getDUID(); 32 | 33 | // wait for calling others $() 34 | setTimeout(function () { 35 | onReady(); 36 | onReady = null; 37 | }, 10); 38 | }; 39 | 40 | Smartbox = function ( platform, cb ) { 41 | if ( typeof platform === 'string' ) { 42 | Smartbox.readyForPlatform(platform, cb); 43 | } else if ( typeof platform === 'function' ) { 44 | // first arg - function 45 | Smartbox.ready(platform); 46 | } 47 | }; 48 | 49 | //public smartbox API 50 | SmartboxAPI = { 51 | version: 0.1, 52 | platformName: '', 53 | 54 | userAgent: userAgent, 55 | 56 | createPlatform: function ( platformName, platformApi ) { 57 | var isCurrent = platformApi.detect && platformApi.detect(); 58 | 59 | if ( isCurrent || detect(platformApi.platformUserAgent) ) { 60 | this.platformName = platformName; 61 | _.extend(this, platformApi); 62 | 63 | if (typeof platformApi.onDetect === 'function') { 64 | this.onDetect(); 65 | } 66 | } 67 | }, 68 | 69 | // calling cb after library initialise 70 | ready: function ( cb ) { 71 | if ( _ready ) { 72 | cb.call(this); 73 | } else { 74 | readyCallbacks.push(cb); 75 | } 76 | }, 77 | 78 | // calling cb after library initialise if platform is current 79 | readyForPlatform: function ( platform, cb ) { 80 | var self = this; 81 | this.ready(function () { 82 | if ( platform == self.platformName ) { 83 | cb.call(self); 84 | } 85 | }); 86 | }, 87 | 88 | utils: { 89 | 90 | /** 91 | * Show error message 92 | * @param msg 93 | */ 94 | error: function ( msg ) { 95 | $$log(msg, 'error'); 96 | }, 97 | 98 | /** 99 | * Show messages in log 100 | * all functionality in main.js 101 | */ 102 | log: { 103 | log: $.noop, 104 | state: $.noop, 105 | show: $.noop, 106 | hide: $.noop, 107 | startProfile: $.noop, 108 | stopProfile: $.noop 109 | }, 110 | 111 | /** 112 | * Asynchroniosly adding javascript files 113 | * @param filesArray {Array} array of sources of javascript files 114 | * @param cb {Function} callback on load javascript files 115 | */ 116 | addExternalJS: function ( filesArray, cb ) { 117 | var $externalJsContainer, 118 | loadedScripts = 0, 119 | len = filesArray.length, 120 | el, 121 | scriptEl; 122 | 123 | function onloadScript () { 124 | loadedScripts++; 125 | 126 | if ( loadedScripts === len ) { 127 | cb && cb.call(); 128 | } 129 | } 130 | 131 | if ( filesArray.length ) { 132 | 133 | $externalJsContainer = document.createDocumentFragment(); 134 | el = document.createElement('script'); 135 | el.type = 'text/javascript'; 136 | el.onload = onloadScript; 137 | 138 | for ( var i = 0; i < len; i++ ) { 139 | scriptEl = el.cloneNode(); 140 | scriptEl.src = filesArray[i]; 141 | $externalJsContainer.appendChild(scriptEl); 142 | } 143 | 144 | document.body.appendChild($externalJsContainer); 145 | } else { 146 | 147 | // if no external js simple call cb 148 | cb && cb.call(this); 149 | } 150 | }, 151 | 152 | addExternalCss: function ( filesArray ) { 153 | var $externalCssContainer; 154 | 155 | if ( filesArray.length ) { 156 | $externalCssContainer = document.createDocumentFragment(); 157 | _.each(filesArray, function ( src ) { 158 | 159 | var el = document.createElement('link'); 160 | 161 | el.rel = 'stylesheet'; 162 | el.href = src; 163 | 164 | $externalCssContainer.appendChild(el); 165 | }); 166 | 167 | document.body.appendChild($externalCssContainer); 168 | } 169 | }, 170 | 171 | addExternalFiles: function ( cb ) { 172 | if ( this.externalJs.length ) { 173 | this.addExternalJS(this.externalJs, cb); 174 | } 175 | if ( this.externalCss.length ) { 176 | this.addExternalCss(this.externalCss); 177 | } 178 | } 179 | } 180 | }; 181 | 182 | Smartbox.config = { 183 | DUID: 'real' 184 | }; 185 | 186 | _.extend(Smartbox, SmartboxAPI); 187 | 188 | // exporting library to global 189 | window.SB = Smartbox; 190 | 191 | // initialize library 192 | window.onload = function () { 193 | initialise(); 194 | 195 | // we don't need initialise func anymore 196 | initialise = null; 197 | }; 198 | })(); -------------------------------------------------------------------------------- /src/sb.platform.js: -------------------------------------------------------------------------------- 1 | // global SB 2 | !(function ( window, undefined ) { 3 | 4 | var PlatformApi = { 5 | externalCss: [], 6 | externalJs: [], 7 | keys: {}, 8 | 9 | DUID: '', 10 | 11 | platformUserAgent: 'not found', 12 | 13 | /** 14 | * Get DUID in case of Config 15 | * @return {string} DUID 16 | */ 17 | getDUID: function () { 18 | switch ( SB.config.DUID ) { 19 | case 'real': 20 | this.DUID = this.getNativeDUID(); 21 | break; 22 | case 'mac': 23 | this.DUID = this.getMac(); 24 | break; 25 | case 'random': 26 | this.DUID = this.getRandomDUID(); 27 | break; 28 | /*case 'local_random': 29 | this.DUID = this.getLocalRandomDUID(); 30 | break;*/ 31 | default: 32 | this.DUID = Config.DUIDSettings; 33 | break; 34 | } 35 | 36 | return this.DUID; 37 | }, 38 | 39 | getSDI: function () { 40 | return ''; 41 | }, 42 | 43 | /** 44 | * Returns random DUID for platform 45 | * @returns {string} 46 | */ 47 | getRandomDUID: function () { 48 | return (new Date()).getTime().toString(16) + Math.floor(Math.random() * parseInt("10000", 16)).toString(16); 49 | }, 50 | 51 | /** 52 | * Returns MAC for platform if exist 53 | * @returns {string} 54 | */ 55 | getMac: function () { 56 | return ''; 57 | }, 58 | 59 | /** 60 | * Returns native DUID for platform if exist 61 | * @returns {string} 62 | */ 63 | getNativeDUID: function () { 64 | return ''; 65 | }, 66 | 67 | /** 68 | * Set custom plugins for platform 69 | */ 70 | setPlugins: $.noop, 71 | 72 | // TODO: volume for all platforms 73 | volumeUp: $.noop, 74 | volumeDown: $.noop, 75 | getVolume: $.noop, 76 | exit: $.noop, 77 | sendReturn: $.noop, 78 | setData: function ( name, val ) { 79 | // save data in string format 80 | localStorage.setItem(name, JSON.stringify(val)); 81 | }, 82 | 83 | getData: function ( name ) { 84 | var result; 85 | try { 86 | result = JSON.parse(localStorage.getItem(name)); 87 | } catch (e) { 88 | } 89 | 90 | return result; 91 | }, 92 | 93 | removeData: function ( name ) { 94 | localStorage.removeItem(name); 95 | } 96 | }; 97 | 98 | _.extend(SB, PlatformApi); 99 | })(this); -------------------------------------------------------------------------------- /test/external.css: -------------------------------------------------------------------------------- 1 | .apply_check{ 2 | left: 123px; 3 | top: 321px; 4 | width: 100px; 5 | height: 100px; 6 | position: absolute; 7 | } -------------------------------------------------------------------------------- /test/external.js: -------------------------------------------------------------------------------- 1 | var nonExists=1; -------------------------------------------------------------------------------- /test/lib/jasmine.css: -------------------------------------------------------------------------------- 1 | body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } 2 | 3 | #HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } 4 | #HTMLReporter a { text-decoration: none; } 5 | #HTMLReporter a:hover { text-decoration: underline; } 6 | #HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } 7 | #HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } 8 | #HTMLReporter #jasmine_content { position: fixed; right: 100%; } 9 | #HTMLReporter .version { color: #aaaaaa; } 10 | #HTMLReporter .banner { margin-top: 14px; } 11 | #HTMLReporter .duration { color: #aaaaaa; float: right; } 12 | #HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } 13 | #HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } 14 | #HTMLReporter .symbolSummary li.passed { font-size: 14px; } 15 | #HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } 16 | #HTMLReporter .symbolSummary li.failed { line-height: 9px; } 17 | #HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } 18 | #HTMLReporter .symbolSummary li.skipped { font-size: 14px; } 19 | #HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } 20 | #HTMLReporter .symbolSummary li.pending { line-height: 11px; } 21 | #HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } 22 | #HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } 23 | #HTMLReporter .runningAlert { background-color: #666666; } 24 | #HTMLReporter .skippedAlert { background-color: #aaaaaa; } 25 | #HTMLReporter .skippedAlert:first-child { background-color: #333333; } 26 | #HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } 27 | #HTMLReporter .passingAlert { background-color: #a6b779; } 28 | #HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } 29 | #HTMLReporter .failingAlert { background-color: #cf867e; } 30 | #HTMLReporter .failingAlert:first-child { background-color: #b03911; } 31 | #HTMLReporter .results { margin-top: 14px; } 32 | #HTMLReporter #details { display: none; } 33 | #HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } 34 | #HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } 35 | #HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } 36 | #HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } 37 | #HTMLReporter.showDetails .summary { display: none; } 38 | #HTMLReporter.showDetails #details { display: block; } 39 | #HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } 40 | #HTMLReporter .summary { margin-top: 14px; } 41 | #HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } 42 | #HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } 43 | #HTMLReporter .summary .specSummary.failed a { color: #b03911; } 44 | #HTMLReporter .description + .suite { margin-top: 0; } 45 | #HTMLReporter .suite { margin-top: 14px; } 46 | #HTMLReporter .suite a { color: #333333; } 47 | #HTMLReporter #details .specDetail { margin-bottom: 28px; } 48 | #HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } 49 | #HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } 50 | #HTMLReporter .resultMessage span.result { display: block; } 51 | #HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } 52 | 53 | #TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } 54 | #TrivialReporter a:visited, #TrivialReporter a { color: #303; } 55 | #TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } 56 | #TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } 57 | #TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } 58 | #TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } 59 | #TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } 60 | #TrivialReporter .runner.running { background-color: yellow; } 61 | #TrivialReporter .options { text-align: right; font-size: .8em; } 62 | #TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } 63 | #TrivialReporter .suite .suite { margin: 5px; } 64 | #TrivialReporter .suite.passed { background-color: #dfd; } 65 | #TrivialReporter .suite.failed { background-color: #fdd; } 66 | #TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } 67 | #TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } 68 | #TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } 69 | #TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } 70 | #TrivialReporter .spec.skipped { background-color: #bbb; } 71 | #TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } 72 | #TrivialReporter .passed { background-color: #cfc; display: none; } 73 | #TrivialReporter .failed { background-color: #fbb; } 74 | #TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } 75 | #TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } 76 | #TrivialReporter .resultMessage .mismatch { color: black; } 77 | #TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } 78 | #TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } 79 | #TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } 80 | #TrivialReporter #jasmine_content { position: fixed; right: 100%; } 81 | #TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } 82 | -------------------------------------------------------------------------------- /test/platform.test.js: -------------------------------------------------------------------------------- 1 | describe('Platform', function () { 2 | 3 | it('Should supports ready', function () { 4 | var spy1=jasmine.createSpy(), spy2=jasmine.createSpy(); 5 | runs(function(){ 6 | SB.ready(function(){ 7 | spy1(); 8 | $$nav.on(); 9 | $$log('log'); 10 | }); 11 | 12 | }); 13 | 14 | waitsFor(function(){ 15 | return spy1.calls.length!=0; 16 | }, 2000); 17 | 18 | 19 | runs(function(){ 20 | SB.ready(spy2); 21 | expect(spy2).toHaveBeenCalled(); 22 | }); 23 | 24 | }); 25 | 26 | it('Should detect native DUID', function () { 27 | expect(SB.getNativeDUID()).not.toBe(''); 28 | }); 29 | 30 | xit('Should detect platform', function () { 31 | $('body').prepend('
' + navigator.userAgent + '
') 32 | expect(SB.platform).toBe('lg'); 33 | }); 34 | 35 | describe('Local storage', function () { 36 | 37 | afterEach(function () { 38 | localStorage.clear(); 39 | }); 40 | 41 | xit('Should have local Storage', function () { 42 | expect(window.localStorage).toBeDefined(); 43 | }); 44 | 45 | it('Shold save data', function () { 46 | localStorage.setItem('key', 'value'); 47 | expect(localStorage.getItem('key')).toBe('value'); 48 | }); 49 | 50 | it('Should delete data', function () { 51 | localStorage.setItem('key', 'value'); 52 | localStorage.removeItem('key'); 53 | expect(localStorage.getItem('key')).toBeFalsy(); 54 | }); 55 | }); 56 | 57 | }); -------------------------------------------------------------------------------- /test/player.config.example.js: -------------------------------------------------------------------------------- 1 | var Config={ 2 | trailer: 'path to short video', 3 | trailerHeight: 180, 4 | trailerWidth: 320, 5 | trailerDuration: '02:11', 6 | movie: 'path to long video', 7 | hls: 'path to stream' 8 | } 9 | -------------------------------------------------------------------------------- /test/player.config.js: -------------------------------------------------------------------------------- 1 | var Config={ 2 | trailer: 'http://ua-cnt.smart-kino.com//trailers/russkiy_reportazh/korporatsiya_svyatye_motory.mp4', 3 | trailerWidth: 640, 4 | trailerHeight: 360, 5 | trailerDuration: '00:31', 6 | movie: 'https://archive.org/download/ElephantsDream/ed_1024_512kb.mp4', 7 | movieAudioTracksLength: 2, 8 | hls: 'http://phone.pik-tv.com/live/mp4:piktv3pik3tv/playlist.m3u8' 9 | } -------------------------------------------------------------------------------- /test/player.test.js: -------------------------------------------------------------------------------- 1 | describe('Player', function () { 2 | 3 | var currentURL = Config.trailer; 4 | var currentType = ''; 5 | 6 | 7 | describe('basic support', function () { 8 | 9 | 10 | 11 | it('supports ready', function () { 12 | var spy = jasmine.createSpy('ready handler'); 13 | Player.on('ready', spy); 14 | 15 | 16 | runs(function () { 17 | Player.play({ 18 | url: Config.trailer 19 | }); 20 | }); 21 | waitsFor(function () { 22 | return spy.calls.length >= 1 23 | }, 'ready have been triggered', 5000); 24 | 25 | runs(function () { 26 | expect(Player.state).toBe("play"); 27 | }); 28 | }); 29 | 30 | it('has video duration', function () { 31 | var info = Player.videoInfo; 32 | expect(Player.formatTime(info.duration)).toBe(Config.trailerDuration); 33 | }); 34 | 35 | it('has video resolution', function () { 36 | var info = Player.videoInfo; 37 | expect(info.width).toBe(Config.trailerWidth); 38 | expect(info.height).toBe(Config.trailerHeight); 39 | }); 40 | 41 | 42 | it('supports pause, resume methods', function () { 43 | 44 | var time; 45 | 46 | runs(function(){ 47 | Player.pause(); 48 | expect(Player.state).toBe("pause"); 49 | }); 50 | 51 | waits(1000); 52 | runs(function(){ 53 | time = Player.videoInfo.currentTime; 54 | }); 55 | waits(1000); 56 | 57 | runs(function () { 58 | 59 | expect(Player.videoInfo.currentTime).toBe(time); 60 | Player.resume(); 61 | expect(Player.state).toBe("play"); 62 | }); 63 | waits(1000); 64 | runs(function () { 65 | expect(Player.videoInfo.currentTime).not.toBe(time); 66 | }); 67 | }); 68 | 69 | 70 | var spyStop = jasmine.createSpy('stop handler'); 71 | 72 | 73 | it('supports stop method', function () { 74 | runs(function () { 75 | Player.on('stop', spyStop); 76 | Player.stop(); 77 | expect(spyStop).toHaveBeenCalled(); 78 | expect(Player.state).toBe("stop"); 79 | }); 80 | 81 | waits(1000); 82 | 83 | runs(function(){ 84 | expect(spyStop.calls.length == 1); 85 | }); 86 | 87 | }); 88 | }); 89 | 90 | 91 | xdescribe('extended support', function () { 92 | 93 | var begin = jasmine.createSpy('bufferingEnd handler'); 94 | var end = jasmine.createSpy('end handler'); 95 | 96 | it('supports bufferingBegin', function () { 97 | 98 | Player.on('bufferingBegin', begin); 99 | 100 | Player.play({ 101 | url: Config.movie 102 | }); 103 | 104 | expect(Player.state).toBe("play"); 105 | 106 | waitsFor(function () { 107 | return begin.calls.length == 1 108 | }, 'bufferingBegin have been triggered', 15000); 109 | }); 110 | 111 | 112 | it('supports bufferingEnd', function () { 113 | runs(function () { 114 | Player.on('bufferingEnd', end); 115 | }); 116 | 117 | waitsFor(function () { 118 | return end.calls.length == 1 119 | }, 'bufferingEnd have been triggered', 15000); 120 | }); 121 | 122 | 123 | it('supports update', function () { 124 | var update = jasmine.createSpy('update handler'); 125 | var date; 126 | 127 | runs(function () { 128 | Player.on('update', update) 129 | }); 130 | 131 | 132 | waitsFor(function () { 133 | return update.calls.length == 1; 134 | }, 'update have been triggered', 5000); 135 | 136 | 137 | waitsFor(function () { 138 | return Player.videoInfo.currentTime >= 2; 139 | }, '2 seconds playing', 5000); 140 | }); 141 | 142 | 143 | if (Config.movieAudioTracksLength >= 1) { 144 | xit('gets audio tracks array', function () { 145 | runs(function () { 146 | expect(Player.audio.get().length).toBe(Config.movieAudioTracksLength); 147 | expect(Player.audio.cur()).toBe(0); 148 | }); 149 | }); 150 | 151 | 152 | xit('supports audio track switch', function () { 153 | runs(function () { 154 | Player.audio.set(1); 155 | expect(Player.audio.cur()).toBe(1); 156 | }); 157 | }); 158 | } 159 | 160 | 161 | it('supports seek method', function () { 162 | var update = jasmine.createSpy('update spy'); 163 | 164 | runs(function () { 165 | Player.on('update', update); 166 | Player.seek(120); 167 | }); 168 | 169 | waitsFor(function () { 170 | return update.calls.length == 1 171 | }, 'update handler was called', 5000); 172 | 173 | runs(function () { 174 | expect(Player.videoInfo.currentTime).toBeGreaterThan(118); 175 | expect(Player.videoInfo.currentTime).toBeLessThan(122); 176 | }); 177 | }); 178 | 179 | 180 | it('supports complete event', function () { 181 | var onComplete = jasmine.createSpy('complete spy'); 182 | Player.on('complete', onComplete); 183 | 184 | var update = jasmine.createSpy('update handler'); 185 | 186 | var timeToWait = 5; 187 | 188 | runs(function () { 189 | Player.seek(Player.videoInfo.duration - timeToWait); 190 | }); 191 | 192 | 193 | waitsFor(function () { 194 | return onComplete.calls.length >= 1; 195 | }, 'onComplete was called', 15000 + timeToWait * 1000); 196 | 197 | runs(function () { 198 | expect(Player.state).toBe("stop"); 199 | }); 200 | }); 201 | 202 | 203 | }); 204 | 205 | xdescribe('hls', function () { 206 | it('support hls', function () { 207 | 208 | runs(function () { 209 | Player.play({ 210 | url: Config.hls, 211 | type: 'hls' 212 | }); 213 | }); 214 | 215 | 216 | var spy = jasmine.createSpy('ready handler'); 217 | Player.on('ready', spy); 218 | waitsFor(function () { 219 | return spy.calls.length == 1 220 | }, 'ready have been triggered', 20000); 221 | 222 | runs(function () { 223 | Player.stop(); 224 | }); 225 | }); 226 | }); 227 | 228 | }); -------------------------------------------------------------------------------- /test/run_test.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var jasmineEnv = jasmine.getEnv(); 3 | jasmineEnv.updateInterval = 250; 4 | var htmlReporter = new jasmine.HtmlReporter(); 5 | jasmineEnv.addReporter(htmlReporter); 6 | jasmineEnv.specFilter = function ( spec ) { 7 | return htmlReporter.specFilter(spec); 8 | }; 9 | var currentWindowOnload = window.onload; 10 | window.onload = function () { 11 | 12 | if ( currentWindowOnload ) { 13 | currentWindowOnload(); 14 | } 15 | jasmineEnv.execute(); 16 | }; 17 | })(); -------------------------------------------------------------------------------- /test/voicelink.test.js: -------------------------------------------------------------------------------- 1 | describe('Voicelink', function () { 2 | 3 | var findItemWithText = function (jq, text) { 4 | return _.find(jq, function (elem) { 5 | return elem.innerHTML == text; 6 | }) 7 | }; 8 | 9 | 10 | describe('simple', function () { 11 | var $firstButton, spy; 12 | 13 | 14 | it('not fails', function () { 15 | $('
').appendTo('body').voiceLink(); 16 | $firstButton = $('#emul_voice_helpbar').children().eq(0); 17 | spy = jasmine.createSpy('voice listener'); 18 | $('#voice_test').children().eq(0).on('voice', spy); 19 | }); 20 | 21 | 22 | it('supports enabled', function () { 23 | expect($$voice.enabled()).toBe(true); 24 | }); 25 | 26 | it('generates buttons', function () { 27 | expect($firstButton.html()).toBe('Hello'); 28 | }); 29 | 30 | it('triggers `voice` event', function () { 31 | expect(spy.calls.length).toBe(0); 32 | $firstButton.click(); 33 | expect(spy.calls.length).toBe(1); 34 | }); 35 | 36 | it('supports pause', function () { 37 | $$voice.pause(); 38 | $firstButton.click(); 39 | expect(spy.calls.length).toBe(1); 40 | }); 41 | 42 | it('supports resume', function () { 43 | $$voice.resume(); 44 | $firstButton.click(); 45 | expect(spy.calls.length).toBe(2); 46 | }); 47 | 48 | it('supports say method', function () { 49 | $$voice.say('Hello'); 50 | expect(spy.calls.length).toBe(3); 51 | }); 52 | 53 | it('supports refresh', function(){ 54 | $('#voice_test').children().eq(0).hide(); 55 | 56 | expect(findItemWithText($('#emul_voice_helpbar').children(),'Hello')).toBeDefined(); 57 | $$voice.refresh(); 58 | expect(findItemWithText($('#emul_voice_helpbar').children(),'Hello')).toBeUndefined(); 59 | }); 60 | 61 | 62 | }); 63 | 64 | 65 | describe('candidates', function () { 66 | var $firstButton, $secondButton, $thirdButton, spy, $helpItems, $bubble; 67 | 68 | 69 | 70 | 71 | it('not fails', function () { 72 | $('
' + 73 | '' + 74 | '' + 75 | '' + 76 | '' + 77 | '' + 78 | '' + 79 | '' + 80 | '' + 81 | '' + 82 | '' + 83 | '
').appendTo('body').voiceLink(); 84 | 85 | $bubble = $('#voice_buble'); 86 | $helpItems = $bubble.find('.voice_help_item'); 87 | 88 | var $children = $('#emul_voice_helpbar').children(); 89 | 90 | $$voice.setup({ 91 | 92 | }); 93 | 94 | $firstButton = $children.eq(0); 95 | $secondButton = $children.eq(1); 96 | $thirdButton = $children.eq(2); 97 | spy = jasmine.createSpy('voice listener'); 98 | $('#voice_test').children().eq(0).on('voice', spy); 99 | }); 100 | 101 | it('weight attribute sets the order of items', function () { 102 | expect($firstButton.html()).toEqual('g'); 103 | expect($secondButton.html()).toEqual('h'); 104 | expect($thirdButton.html()).toEqual('a'); 105 | }); 106 | 107 | it('has more button that shows help bar', function () { 108 | $('#emul_voice_helpbar').children().eq(5).click(); 109 | expect($bubble.is(':visible')).toEqual(true); 110 | expect($helpItems.length).toBe(3); 111 | }); 112 | 113 | it('supports grouping', function () { 114 | expect($bubble.find('.voice_group_head').eq(1).html()).toBe('1'); 115 | }); 116 | 117 | it('supports hidden attribute', function () { 118 | expect(findItemWithText($helpItems, 'i')).toBeUndefined(); 119 | expect(findItemWithText($('.emul_voice_trigger'), 'i')).toBeDefined(); 120 | }); 121 | 122 | it('ignores invisible items', function () { 123 | expect(findItemWithText($helpItems, 'j')).toBe(undefined); 124 | }); 125 | }); 126 | 127 | 128 | it('supports server recognition', function () { 129 | var spy = jasmine.createSpy('server callback'); 130 | $$voice.fromServer('Say some', spy); 131 | expect(spy).toHaveBeenCalled(); 132 | }); 133 | 134 | 135 | }); --------------------------------------------------------------------------------