├── .editorconfig
├── .github
└── FUNDING.yml
├── .gitignore
├── HISTORY_en.md
├── HISTORY_ru.md
├── LICENSE_en
├── LICENSE_ru
├── README.md
├── README_en.md
├── README_ru.md
├── package.json
├── src
└── loaders
│ └── manifest-loader.js
├── static
├── _locales
│ ├── be
│ │ └── messages.json
│ ├── en_US
│ │ └── messages.json
│ ├── es
│ │ └── messages.json
│ ├── it
│ │ └── messages.json
│ ├── pl
│ │ └── messages.json
│ ├── ru
│ │ └── messages.json
│ └── uk
│ │ └── messages.json
├── browser-action
│ ├── css
│ │ └── browser-action.css
│ ├── img
│ │ ├── amazon-icon-16.png
│ │ ├── checkmark-success-icon-16.png
│ │ ├── clipboard_copy_icon&16.png
│ │ ├── cog_icon&16.png
│ │ ├── google-icon-16.png
│ │ └── vk-icon-16.png
│ ├── index.html
│ └── js
│ │ └── browser-action.js
├── global
│ ├── css
│ │ └── global.css
│ ├── img
│ │ ├── cogs_icon&24.png
│ │ ├── delete_icon&16.png
│ │ ├── emotion_smile_icon&16.png
│ │ ├── info-icon.svg
│ │ ├── list_bullets_icon&16.png
│ │ ├── no-logo.svg
│ │ ├── notification-icon-80.png
│ │ ├── off_icon&16.png
│ │ ├── options-checkbox-tick-icon-11.png
│ │ ├── playback_next_icon&16.png
│ │ ├── playback_play_icon&16.png
│ │ ├── playback_prev_icon&16.png
│ │ ├── playback_stop_icon&16.png
│ │ ├── pozitone-icon-128.png
│ │ ├── pozitone-icon-16.png
│ │ ├── pozitone-icon-19.png
│ │ ├── pozitone-icon-38.png
│ │ ├── pozitone-icon-48.png
│ │ ├── pozitone-notification-icon-80.png
│ │ ├── pozitone.svg
│ │ ├── projects
│ │ │ ├── pe-icon-64.png
│ │ │ ├── pwm-icon-128.svg
│ │ │ ├── sttb-icon-50.svg
│ │ │ └── swaggy-icon-48.svg
│ │ ├── round_plus_icon&16.png
│ │ ├── sound_down_icon&16.png
│ │ ├── sound_high_icon&16.png
│ │ ├── sound_mute_icon&16.png
│ │ └── sound_up_icon&16.png
│ └── js
│ │ ├── angular
│ │ ├── angular-route.min.js
│ │ └── angular.min.js
│ │ ├── background-v2.js
│ │ ├── background.js
│ │ ├── bowser.js
│ │ ├── const.js
│ │ ├── global-v2.js
│ │ ├── global.js
│ │ ├── i18n.js
│ │ ├── i18next
│ │ ├── i18next.min.js
│ │ ├── i18nextBrowserLanguageDetector.min.js
│ │ └── i18nextXHRBackend.js
│ │ ├── log.js
│ │ ├── mixpanel-2.2.min.js
│ │ ├── notifications.js
│ │ ├── page.js
│ │ ├── pozitone-host-api.js
│ │ ├── pozitone-module-sdk.js
│ │ ├── punycode.min.js
│ │ ├── tracking.js
│ │ ├── utils.js
│ │ └── voice-control.js
├── manifest.json
├── modules
│ ├── com_audioaddict
│ │ └── js
│ │ │ └── page-watcher.js
│ ├── com_classicalradio
│ │ ├── img
│ │ │ └── classicalradio-logo-120.png
│ │ └── js
│ │ │ └── page-watcher.js
│ ├── com_google_play_music
│ │ ├── img
│ │ │ ├── google-play-music-logo-80.png
│ │ │ └── google-play-music.svg
│ │ └── js
│ │ │ ├── dom-to-image.js
│ │ │ └── page-watcher.js
│ ├── com_jazzradio
│ │ ├── img
│ │ │ └── jazzradio-logo-120.png
│ │ └── js
│ │ │ └── page-watcher.js
│ ├── com_radiotunes
│ │ ├── img
│ │ │ └── radiotunes-logo-80.png
│ │ └── js
│ │ │ └── page-watcher.js
│ ├── com_rockradio
│ │ ├── img
│ │ │ └── rockradio-logo-120.png
│ │ └── js
│ │ │ └── page-watcher.js
│ ├── com_soundcloud
│ │ ├── img
│ │ │ ├── soundcloud-logo-48.svg
│ │ │ └── soundcloud-logo-80.png
│ │ └── js
│ │ │ └── page-watcher.js
│ ├── com_vgmradio
│ │ ├── img
│ │ │ └── vgmradio-logo-120.svg
│ │ └── js
│ │ │ └── page-watcher.js
│ ├── com_vk_audio
│ │ ├── img
│ │ │ ├── vk-logo-32.png
│ │ │ ├── vk-logo-80.png
│ │ │ └── vk-logo-80.svg
│ │ └── js
│ │ │ ├── page-watcher-2.js
│ │ │ ├── page-watcher-loader.js
│ │ │ └── page-watcher.js
│ ├── fm_di
│ │ ├── img
│ │ │ └── di-logo-120.svg
│ │ └── js
│ │ │ └── page-watcher.js
│ ├── general
│ │ └── js
│ │ │ └── page-watcher.js
│ ├── ru_101
│ │ └── js
│ │ │ ├── page-watcher.js
│ │ │ └── uppod-player-api.js
│ └── ru_ok_audio
│ │ ├── img
│ │ ├── ok-logo-80.svg
│ │ ├── ok-logo-orange-32.png
│ │ └── ok-logo-orange-80.png
│ │ └── js
│ │ └── page-watcher.js
└── options
│ ├── css
│ └── options.css
│ ├── img
│ └── switch-language.svg
│ ├── index.html
│ ├── js
│ ├── app.js
│ ├── controllers
│ │ ├── external-modules-list.js
│ │ ├── misc.js
│ │ ├── settings-modules-list.js
│ │ ├── settings.js
│ │ └── voice-control.js
│ └── options.js
│ └── partials
│ ├── about.html
│ ├── contribution.html
│ ├── external-modules-list.html
│ ├── feedback.html
│ ├── help.html
│ ├── projects.html
│ ├── settings-general.html
│ ├── settings-module.html
│ ├── settings-modules-list.html
│ ├── settings.html
│ ├── voice-control.html
│ └── ❤.html
├── webpack.config.js
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | [*]
5 | end_of_line = lf
6 | insert_final_newline = true
7 | trim_trailing_whitespace = true
8 |
9 | [*.{js,json}]
10 | charset = utf-8
11 | indent_style = space
12 | indent_size = 2
13 | indent_brace_style = K&R
14 | curly_bracket_next_line = false
15 |
16 | [*.{js}]
17 | quote_type = single
18 | spaces_around_operators = true
19 | spaces_around_brackets = inside
20 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | patreon: PoziWorld
2 | custom: paypal.me/PoziTone
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /dist/
2 | /node_modules/
3 |
--------------------------------------------------------------------------------
/LICENSE_en:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013-2016 PoziWorld
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 |
--------------------------------------------------------------------------------
/LICENSE_ru:
--------------------------------------------------------------------------------
1 | Лицензия MIT
2 |
3 | Copyright (c) 2013-2016 PoziWorld
4 |
5 | Данная лицензия разрешает лицам, получившим копию данного программного
6 | обеспечения и сопутствующей документации (в дальнейшем именуемыми «Программное
7 | Обеспечение»), безвозмездно использовать Программное Обеспечение без
8 | ограничений, включая неограниченное право на использование, копирование,
9 | изменение, добавление, публикацию, распространение, сублицензирование и/или
10 | продажу копий Программного Обеспечения, также как и лицам, которым
11 | предоставляется данное Программное Обеспечение, при соблюдении следующих
12 | условий:
13 |
14 | Указанное выше уведомление об авторском праве и данные условия должны быть
15 | включены во все копии или значимые части данного Программного Обеспечения.
16 |
17 | ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО
18 | ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ
19 | ГАРАНТИЯМИ ТОВАРНОЙ ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И
20 | ОТСУТСТВИЯ НАРУШЕНИЙ ПРАВ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ
21 | ОТВЕТСТВЕННОСТИ ПО ИСКАМ О ВОЗМЕЩЕНИИ УЩЕРБА, УБЫТКОВ ИЛИ ДРУГИХ ТРЕБОВАНИЙ ПО
22 | ДЕЙСТВУЮЩИМ КОНТРАКТАМ, ДЕЛИКТАМ ИЛИ ИНОМУ, ВОЗНИКШИМ ИЗ, ИМЕЮЩИМ ПРИЧИНОЙ ИЛИ
23 | СВЯЗАННЫМ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
24 | ИЛИ ИНЫМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [PoziTone
](https://pozitone.com)
2 | =======
3 |
4 |
5 | An extension for the Google Chrome, Opera, Microsoft Edge, and other Chromium-based browsers that adds new features to your favorite online media players.
6 |
7 | [Learn more about PoziTone](README_en.md).
8 |
9 | ---
10 |
11 |
12 | Расширение для браузеров Google Chrome, Opera, Яндекс, Microsoft Edge и других, основанных на Chromium, которое добавляет новые возможности Вашим любимым онлайн-медиаплеерам.
13 |
14 | [Узнать больше о PoziTone](README_ru.md).
15 |
--------------------------------------------------------------------------------
/README_en.md:
--------------------------------------------------------------------------------
1 | > Это английская версия, см. [README_ru.md](README_ru.md) для русской.
2 |
3 | [PoziTone
](https://pozitone.com)
4 | =======
5 |
6 | An extension for the Google Chrome, Opera, Microsoft Edge, and other Chromium-based browsers that adds new [features](#features) to your favorite [online media players](#supported-online-media-players).
7 |
8 | ___
9 |
10 | ##### Table of contents
11 |
12 | * [Features](#features)
13 | * [Supported online media players](#supported-online-media-players)
14 | * [Supported via external modules](#supported-via-external-modules)
15 | * [Installation](#installation)
16 | * [Demo](#demo)
17 | * [Keyboard shortcuts](#keyboard-shortcuts)
18 | * [Set up / change keyboards shortcuts](#set-up--change-keyboards-shortcuts)
19 | * [Demo: how to set up / change keyboards shortcuts](#demo-how-to-set-up--change-keyboards-shortcuts)
20 | * [Global keyboard shortcuts (outside of the browser window)](#global-keyboard-shortcuts-outside-of-the-browser-window)
21 | * [Gratitudes](#gratitudes)
22 | * [Credits](#credits)
23 | * [Privacy](#privacy)
24 | * [Contribution](#contribution)
25 | * [Spread the word](#spread-the-word)
26 | * [Translation](#translation)
27 | * [Incentive](#incentive)
28 | * [Feedback](#feedback)
29 | * [PoziTone on social networks](#pozitone-on-social-networks)
30 |
31 | ___
32 |
33 | Features
34 | --------
35 |
36 | * **Displays info about now playing song, podcast, video, etc.**
37 |
38 | _Familiarizing with a new genre or band?_
39 |
40 | PoziTone will show you track info (its name, artist name) via a pop-up notification on every track change.
41 |
42 | * **Easy player control outside of browser window**
43 |
44 | _Want to stop/resume playback, mute/unmute, switch track or add it to playlist?_
45 |
46 | Three easy ways to do it:
47 | * Click a button in the pop-up notification.
48 | * Press [keyboard shortcuts](#keyboard-shortcuts).
49 | * Or, **_just say a [command](https://github.com/PoziWorld/PoziWorld-Elf/wiki/Commands)_***!
50 |
51 | \* (Voice control app [PoziWorld Elf](https://github.com/PoziWorld/PoziWorld-Elf) needs to be installed)
52 |
53 | * **List of recently played**
54 |
55 | _What track played five minutes ago?_
56 |
57 | PoziTone keeps information about the last 10 tracks played.
58 |
59 | [(back to table of contents)](#table-of-contents)
60 |
61 |
62 | Supported online media players
63 | --------
64 |
65 | * [SoundCloud](https://soundcloud.com) (full version of site)
66 | * [Google Play Music](https://play.google.com/music)
67 | * [AudioAddict](http://www.audioaddict.com) network:
68 | * [ClassicalRadio.com](http://www.classicalradio.com)
69 | * [Digitally Imported](https://www.di.fm) (DI, DI.FM, or DI Radio)
70 | * [JAZZRADIO.com](http://www.jazzradio.com)
71 | * [RadioTunes](http://www.radiotunes.com)
72 | * [ROCKRADIO.COM](http://www.rockradio.com)
73 | * [VK](https://vk.com) (audio, full version of site – old & new)
74 | * [VGM Radio](https://vgmradio.com)
75 | * [Odnoklassniki](http://ok.ru) (audio, full version of site)
76 | * [Online radio 101.ru](http://101.ru) (stations' main player)
77 |
78 | More to come...
79 |
80 |
81 | ### Supported via external modules
82 |
83 | An external PoziTone module is a standalone extension which is able to provide [PoziTone features](#features) by communicating directly with PoziTone behind the scenes.
84 |
85 | * [YouTube embedded player](https://chrome.google.com/webstore/detail/youtube-embedded-player-p/bajalgkbfjloemafmkiheboebghhibbg "PoziTone module for YouTube embedded player") ([for Opera](https://addons.opera.com/en/extensions/details/youtube-embedded-player-pozitone-module/ "“PoziTone module for YouTube embedded player” for Opera"), [for Microsoft Edge](https://microsoftedge.microsoft.com/addons/detail/endgoolfeicagiackhdalbfkinelcgin "“PoziTone module for YouTube embedded player” for Microsoft Edge")).
86 | * [SoundCloud Widget](https://chrome.google.com/webstore/detail/pozitone-module-for-sound/iijloaojdghegopdahladbajodcgnmgh "PoziTone module for SoundCloud Widget") ([for Opera](https://addons.opera.com/en/extensions/details/soundcloud-widget-pozitone-module/ "“PoziTone module for SoundCloud Widget” for Opera"), [for Microsoft Edge](https://microsoftedge.microsoft.com/addons/detail/imijjplgbohoagfnhbdlfhfcgfikgjab "“PoziTone module for SoundCloud Widget” for Microsoft Edge")).
87 | * [Sovyatnik](https://chrome.google.com/webstore/detail/sovyatnik-pozitone-module/ihdoljplikdgegdooeohfmgaaabcbmpn "PoziTone module for Sovyatnik") ([for Opera](https://addons.opera.com/en/extensions/details/soviatnik-pozitone-modul/ "“PoziTone module for Sovyatnik” for Opera"), [for Microsoft Edge](https://microsoftedge.microsoft.com/addons/detail/fkgcpgookmofjfedpfhadkgkhddcdbpp "“PoziTone module for Sovyatnik” for Microsoft Edge")).
88 |
89 |
90 | _Are you a developer?_ Want to add a support for your favorite player and publish it as a PoziTone module under your name? – Check [PoziTone module SDK](https://github.com/PoziWorld/PoziTone-module-SDK).
91 |
92 | [(back to table of contents)](#table-of-contents)
93 |
94 |
95 | Installation
96 | --------
97 |
98 | * [PoziTone in the Chrome Web Store](https://chrome.google.com/webstore/detail/pozitone/bdglbogiolkffcmojmmkipgnpkfipijm/details?hl=en)
99 | * [PoziTone in the Opera add-ons catalog](https://addons.opera.com/en/extensions/details/pozitone/?display=en_US)
100 | * [PoziTone in the Microsoft Edge Addons store](https://microsoftedge.microsoft.com/addons/detail/mnfohmojhhcbbnafeehfhghjaeaokjbl)
101 |
102 | [(back to table of contents)](#table-of-contents)
103 |
104 |
105 | Demo
106 | --------
107 |
108 | Go to [SoundCloud](https://soundcloud.com) and play any track.
109 |
110 | PoziTone should display a notification with the track info.
111 |
112 | [(back to table of contents)](#table-of-contents)
113 |
114 |
115 | Keyboard shortcuts
116 | --------
117 |
118 | ### Preset
119 |
120 | 1. Alt+Shift+D — “Add to playlist”.
121 | 2. Alt+Shift+P — “Stop / Play”.
122 | 3. Alt+Shift+M — “Mute / Unmute”.
123 | 4. Alt+Shift+Q — “Show notification”.
124 |
125 | ### Also available
126 |
127 | Not preset, but can be easily [set up](#set-up--change-keyboards-shortcuts) with the help of next subsection.
128 |
129 | 5. Alt+Shift+F — “I like it!”.
130 | 6. Alt+Shift+N — “Next”.
131 | 7. Alt+Shift+B — “Previous”.
132 | 8. Alt+Shift+A — “Increase sound volume”.
133 | 9. Alt+Shift+Z — “Decrease sound volume”.
134 | 10. Alt+Shift+R — “Activate the extension”.
135 |
136 | ### Set up / change keyboards shortcuts
137 |
138 | 1. Right-click on the PoziTone icon to the right of the address bar.
139 | 2. Click on “Manage extensions”.
140 | 3. Scroll down the page.
141 | 4. Click on the blue “Keyboard shortcuts” link.
142 | 5. Find PoziTone in the list.
143 | 6. Make any necessary changes.
144 | 7. Click on “OK”.
145 |
146 | #### Demo: how to set up / change keyboards shortcuts
147 |
148 | [](https://cloud.githubusercontent.com/assets/8120840/3534660/b7bfb960-07ef-11e4-9102-7d1aa1b25496.gif)
149 |
150 | ### Global keyboard shortcuts (outside of the browser window)
151 |
152 | By default, while the browser is not in the foreground, the shortcut will be inactive.
153 |
154 | You can make the shortcuts work even when the browser is not in the foreground: see [steps 1–7](#set-up--change-keyboards-shortcuts) above, change “In Chrome” to “Global” on step 6.
155 |
156 | [(back to table of contents)](#table-of-contents)
157 |
158 |
159 | Gratitudes
160 | --------
161 |
162 | - To all those who give us life and happiness!
163 | - To the inspirers and testers of PoziTone: Darya, Evgeny, Mel, Nike, Sergey.
164 | - [UserEcho](https://userecho.com/?pcode=1014487) — for the excellent service, kindness and generosity!
165 | - Translators:
166 | - Spanish — [Paco_Zamo (Francisco Zamorano)](https://www.transifex.com/user/profile/Paco_Zamo/);
167 | - Polish — jurczak (Łukasz Jurczak);
168 | - Italian — [Aeco (Giuseppe Mariniello)](https://www.transifex.com/user/profile/Aeco/);
169 | - Ukrainian — [ivan.zusko (Ivan Zusko)](https://www.transifex.com/user/profile/ivan.zusko/), [KovalenkoStas (Коваленко Станіслав)](https://www.transifex.com/user/profile/KovalenkoStas/).
170 | - Belarusian — [natasmirnova1392 (Natalia Smirnova)](https://www.transifex.com/user/profile/natasmirnova1392/).
171 |
172 | [(back to table of contents)](#table-of-contents)
173 |
174 |
175 | Credits
176 | --------
177 |
178 | - The button icons — [Gentleface Mono Icon Set](http://gentleface.com/free_icon_set.html), Gentleface, [CC BY-NC 3.0](http://creativecommons.org/licenses/by-nc/3.0/).
179 | - [The Play icon](http://thenounproject.com/term/play/5206/) — Michael Rowe, [CC BY 3.0](http://creativecommons.org/licenses/by/3.0/us/).
180 | - The Info icon — [Freepik](http://www.freepik.com) from [www.flaticon.com](http://www.flaticon.com), [CC BY 3.0](http://creativecommons.org/licenses/by/3.0/).
181 | - The appearance of the “PoziTone Options” and “Recent Tracks” pages:
182 | * Copyright 2014 The Chromium Authors. All rights reserved.
183 | * Use of this source code is governed by a BSD-style license that can be found in the [LICENSE](http://src.chromium.org/viewvc/chrome/trunk/src/LICENSE) file.
184 | - All trademarks used (trade names, logos, brands, names, service marks, etc.) are the property of their respective owners.
185 |
186 | [(back to table of contents)](#table-of-contents)
187 |
188 |
189 | Privacy
190 | --------
191 |
192 | This extension does not read, change, store, or transmit any of your personal data (e.g., logins, passwords, messages, contacts) from any of the sites or your computer in absolutely any form.
193 |
194 | [(back to table of contents)](#table-of-contents)
195 |
196 |
197 | Contribution
198 | --------
199 | ### Spread the word
200 |
201 | If you find PoziTone useful, why not recommend it to your friends, colleagues, and/or followers?
202 |
203 | There are a few ways you can help our cause:
204 |
205 | * Follow us on [Telegram](https://t.me/PoziTone), [Facebook](https://facebook.com/PoziTone), [Twitter](https://twitter.com/PoziTone), [VK](https://vk.com/PoziTone), [Odnoklassniki](https://ok.ru/group/54738320621596), and [Instagram](https://instagram.com/PoziTone).
206 | * Share the [installation link](https://chrome.google.com/webstore/detail/pozitone/bdglbogiolkffcmojmmkipgnpkfipijm) ([for Opera](https://addons.opera.com/extensions/details/pozitone/)) on your social networks and/or blog.
207 | * [Rate PoziTone](https://chrome.google.com/webstore/detail/pozitone/bdglbogiolkffcmojmmkipgnpkfipijm/reviews) ([for Opera](https://addons.opera.com/extensions/details/pozitone/#feedback-container)) – the feedback really helps others find this extension!
208 |
209 | ### Translation
210 |
211 | Sprechen Sie Deutsch? Vous parlez français?
212 |
213 | We want as many PoziTone users as possible to be able to interact with the extension in their native language.
214 |
215 | If you know any foreign language or just noticed a discrepancy in the translation, please [let us know](https://www.transifex.com/poziworld/pozitone/).
216 |
217 | ### Incentive
218 |
219 | If you appreciate the work we have done, please consider stimulating PoziTone development via [PayPal](https://www.paypal.me/pozitone), [Square Cash](https://cash.me/$PoziTone), or [Coinbase](https://commerce.coinbase.com/checkout/6c0d5cf3-cd19-4b8b-bda3-e843716226df).
220 |
221 | [(back to table of contents)](#table-of-contents)
222 |
223 |
224 | Feedback
225 | --------
226 |
227 | We appreciate your feedback!
228 |
229 | - Please [share your ideas](https://feedback.pozitone.com/).
230 | - Don't be shy to [ask questions](mailto:feedback@pozitone.com).
231 | - [Bug reports](https://chrome.google.com/webstore/detail/pozitone/bdglbogiolkffcmojmmkipgnpkfipijm/support) ([for Opera](https://addons.opera.com/extensions/details/pozitone/?reports#feedback-container)) are valuable!
232 | - We like [praises](https://chrome.google.com/webstore/detail/pozitone/bdglbogiolkffcmojmmkipgnpkfipijm/reviews) ([for Opera](https://addons.opera.com/extensions/details/pozitone/#feedback-container)), who doesn't? :)
233 | - Stick is helpful, but [carrot](https://cash.me/$PoziTone) is sweeter. ;)
234 |
235 | [(back to table of contents)](#table-of-contents)
236 |
237 |
238 | PoziTone on social networks
239 | --------
240 |
241 | - [PoziTone on Telegram](https://t.me/PoziTone)
242 | - [PoziTone on VK](https://vk.com/PoziTone)
243 | - [PoziTone on Twitter](https://twitter.com/PoziTone)
244 | - [PoziTone on Facebook](https://facebook.com/PoziTone)
245 | - [PoziTone on Odnoklassniki](https://ok.ru/group/54738320621596)
246 | - [PoziTone on 101.ru](https://101.ru/?an=User_Info&userId=709962)
247 | - [PoziTone on Instagram](https://instagram.com/PoziTone)
248 |
249 | [(back to table of contents)](#table-of-contents)
250 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pozitone",
3 | "version": "1.0.3",
4 | "description": "PoziTone is a browser extension (add-on) for the Google Chrome, Opera, Microsoft Edge, and other Chromium-based browsers that adds new features to your favorite online media players.",
5 | "scripts": {
6 | "dev": "NODE_ENV=development webpack --mode development --progress",
7 | "build": "NODE_ENV=production webpack --mode production",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/PoziWorld/PoziTone.git"
13 | },
14 | "keywords": [
15 | "pozitone",
16 | "media player",
17 | "browser",
18 | "extension",
19 | "addon",
20 | "chrome",
21 | "edge",
22 | "opera",
23 | "chromium"
24 | ],
25 | "author": "PoziWorld, Inc.",
26 | "license": "MIT",
27 | "bugs": {
28 | "url": "https://github.com/PoziWorld/PoziTone/issues"
29 | },
30 | "homepage": "https://pozitone.com",
31 | "devDependencies": {
32 | "clean-webpack-plugin": "0.1.19",
33 | "copy-webpack-plugin": "4.5.2",
34 | "fs": "^0.0.1-security",
35 | "immutable": "4.0.0-rc.9",
36 | "path": "0.12.7",
37 | "webpack": "^5.0.0-beta.9",
38 | "webpack-clean": "1.2.2",
39 | "webpack-cli": "^3.3.10"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/loaders/manifest-loader.js:
--------------------------------------------------------------------------------
1 | const path = require( 'path' );
2 | const fs = require( 'fs' );
3 | const { mergeDeep } = require( 'immutable' );
4 |
5 | /**
6 | * https://nodejs.org/api/fs.html#fs_fs_readfilesync_path_options
7 | */
8 |
9 | const PACKAGE_JSON_PATH = './package.json';
10 | const PACKAGE_JSON_ENCODING = 'utf8';
11 |
12 | /**
13 | * https://developer.chrome.com/extensions/options#full_page
14 | * https://developer.chrome.com/extensions/options#embedded_options
15 | * https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/options_ui#Syntax
16 | *
17 | * @type {string}
18 | */
19 |
20 | const OPTIONS_PAGE_PATH = 'options/index.html';
21 |
22 | /**
23 | * https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/applications#Extension_ID_format
24 | *
25 | * @type {string}
26 | */
27 |
28 | const EXTENSION_ID_AFFIX = '@poziworld.com';
29 |
30 | module.exports = adaptManifestJson;
31 |
32 | /**
33 | * Build browser-specific manifest.json files, as not all manifest.json keys are supported by all browsers.
34 | * Also, copy over some details from package.json.
35 | *
36 | * https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Browser_compatibility_for_manifest.json
37 | *
38 | * Inspired by https://stackoverflow.com/a/44249538
39 | *
40 | * @param {string} source - Stringified original manifest.json's contents.
41 | * @return {string} - Stringified updated browser-specific manifest.json's contents.
42 | */
43 |
44 | function adaptManifestJson( source ) {
45 | let manifestJsonAsJs = JSON.parse( source );
46 | const packageJsonContents = fs.readFileSync( PACKAGE_JSON_PATH, PACKAGE_JSON_ENCODING );
47 | const packageJsonAsJs = JSON.parse( packageJsonContents );
48 | let newProperties = {
49 | version: packageJsonAsJs.version,
50 | author: packageJsonAsJs.author,
51 | homepage_url: packageJsonAsJs.homepage,
52 | };
53 |
54 | /**
55 | * See supportedBrowsers in webpack.config.js.
56 | *
57 | * @todo Find a cleaner way.
58 | */
59 |
60 | const browserName = path.basename( this._compiler.outputPath );
61 |
62 | switch ( browserName ) {
63 | case 'chromium':
64 | {
65 | newProperties.background = {
66 | persistent: true,
67 | };
68 | newProperties.options_page = OPTIONS_PAGE_PATH;
69 |
70 | break;
71 | }
72 | case 'firefox':
73 | {
74 | newProperties.applications = {
75 | gecko: {
76 | id: packageJsonAsJs.name + EXTENSION_ID_AFFIX,
77 | }
78 | };
79 | newProperties.options_ui = {
80 | page: OPTIONS_PAGE_PATH,
81 | browser_style: true,
82 | };
83 |
84 | delete manifestJsonAsJs.version_name;
85 |
86 | break;
87 | }
88 | }
89 |
90 | const merged = mergeDeep( manifestJsonAsJs, newProperties );
91 | const mergedJson = JSON.stringify( merged );
92 |
93 | this.emitFile( 'manifest.json', mergedJson );
94 |
95 | return mergedJson;
96 | }
97 |
--------------------------------------------------------------------------------
/static/browser-action/css/browser-action.css:
--------------------------------------------------------------------------------
1 | /* =============================================================================
2 |
3 | Product : PoziTone
4 | Author : PoziWorld
5 | Copyright : Copyright (c) 2013-2016 PoziWorld
6 | License : pozitone.com/license
7 | File : browser-action/css/browser-action.css
8 | Description : Popup Styles
9 |
10 | Table of Contents:
11 |
12 | Globals
13 | Content
14 |
15 | ============================================================================ */
16 |
17 | /* =============================================================================
18 |
19 | Globals
20 |
21 | ============================================================================ */
22 |
23 | body
24 | {
25 | min-width: initial;
26 | }
27 |
28 | /* =============================================================================
29 |
30 | Content
31 |
32 | ============================================================================ */
33 |
34 | #content
35 | {
36 | padding: 0 2rem;
37 | min-width: 50rem;
38 | max-width: 50rem;
39 | }
40 | .pageHeading
41 | {
42 | width: 50rem;
43 | max-width: none;
44 | min-width: none;
45 | }
46 | #recentTracks
47 | {
48 | margin: 1.15rem 0 1.4rem 1.8rem;
49 | }
50 | .recentTrack
51 | , #tunesSuggestion
52 | {
53 | line-height: normal !important;
54 | padding: .25rem 0;
55 | transition: all .1s ease;
56 | }
57 | .recentTrack:hover
58 | {
59 | background-color: #fafafa;
60 | }
61 | .recentTrackImg
62 | {
63 | display: inline-block;
64 | vertical-align: top;
65 | width: 3.2rem;
66 | height: 3.2rem;
67 | }
68 | .recentTrackActions
69 | {
70 | display: none;
71 | position: absolute;
72 | z-index: 2;
73 | top: .25rem;
74 | width: 0;
75 | height: 0;
76 | background-color: inherit;
77 | background-image: url(/global/img/cogs_icon&24.png);
78 | background-position: .4rem .4rem;
79 | background-repeat: no-repeat;
80 | overflow: hidden;
81 | }
82 | .recentTrack:hover .recentTrackActions
83 | {
84 | display: block;
85 | width: 3.2rem;
86 | height: 3.2rem;
87 | }
88 | .recentTrack:hover .recentTrackActions:hover
89 | {
90 | width: 100%;
91 | height: 100%;
92 | }
93 | .recentTrackInfo
94 | , #tunesSuggestionInfo
95 | {
96 | display: inline-block;
97 | position: relative;
98 | vertical-align: top;
99 | font-size: 1.2em;
100 | line-height: 1.5em;
101 | padding: .84rem 0 0 .66em;
102 | max-width: 42rem;
103 | }
104 | .recentTrackActions:hover + .recentTrackInfo:after
105 | {
106 | content: '';
107 | position: absolute;
108 | top: 0;
109 | right: 0;
110 | bottom: 0;
111 | left: 0;
112 | background-color: #fafafa;
113 | }
114 | .recentTrackActionsList
115 | {
116 | height: 100%;
117 | margin-left: 4.3rem !important;
118 | }
119 | .recentTrackActionsListItem
120 | {
121 | float: left;
122 | padding: .4rem 0;
123 | line-height: normal !important;
124 | }
125 | .recentTrackAction
126 | {
127 | margin: 0 1.1rem 0 0;
128 | width: 2.4rem;
129 | height: 2.4rem;
130 | cursor: pointer;
131 | }
132 | .recentTrackActionImg
133 | {
134 | margin-top: .18rem;
135 | width: 1.6rem;
136 | height: 1.6rem;
137 | }
138 | .copyToClipboardSuccess
139 | {
140 | position: absolute;
141 | top: 50%;
142 | left: 50%;
143 | margin: -.8rem 0 0 -.8rem;
144 | }
145 |
146 | /* =============================================================================
147 |
148 | Privacy statement
149 |
150 | ============================================================================ */
151 |
152 | #privacyStatementsContainer
153 | {
154 | z-index: 2;
155 | left: 0;
156 | width: auto;
157 | }
158 | .privacyStatement
159 | {
160 | font-size: 1.3em;
161 | white-space: pre-line;
162 | }
163 |
--------------------------------------------------------------------------------
/static/browser-action/img/amazon-icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/browser-action/img/amazon-icon-16.png
--------------------------------------------------------------------------------
/static/browser-action/img/checkmark-success-icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/browser-action/img/checkmark-success-icon-16.png
--------------------------------------------------------------------------------
/static/browser-action/img/clipboard_copy_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/browser-action/img/clipboard_copy_icon&16.png
--------------------------------------------------------------------------------
/static/browser-action/img/cog_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/browser-action/img/cog_icon&16.png
--------------------------------------------------------------------------------
/static/browser-action/img/google-icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/browser-action/img/google-icon-16.png
--------------------------------------------------------------------------------
/static/browser-action/img/vk-icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/browser-action/img/vk-icon-16.png
--------------------------------------------------------------------------------
/static/browser-action/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
29 |
55 |
56 |
143 |
144 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/static/browser-action/js/browser-action.js:
--------------------------------------------------------------------------------
1 | /* =============================================================================
2 |
3 | Product: PoziTone
4 | Author: PoziWorld
5 | Copyright: (c) 2016 PoziWorld
6 | License: pozitone.com/license
7 |
8 | Table of Contents:
9 |
10 | Popup
11 | init()
12 | populateRecentTracks()
13 | addEventListeners()
14 | onCopyToClipboardCtaClick()
15 | requestClipboardWritePermission()
16 | copyToClipboard()
17 | trackData()
18 | composeRecentTrackActionUrl()
19 | encodeQuery()
20 | Events
21 |
22 | ============================================================================ */
23 |
24 | /* =============================================================================
25 |
26 | Popup
27 |
28 | ============================================================================ */
29 |
30 | ( function () {
31 | window.strPage = 'browser-action';
32 |
33 | const strLogPage = 'browserAction';
34 | const strLogPageDivider = '.';
35 |
36 | const strListId = 'recentTracks';
37 | const strListElementSelector = '.recentTrack';
38 | const strListElementInfoSelector = '.recentTrackInfo';
39 |
40 | const strRecentTrackActionUrl = 'https://go.pozitone.com/s/?';
41 |
42 | setUp();
43 |
44 | /**
45 | * Make the logic readily available.
46 | */
47 |
48 | function setUp() {
49 | document.addEventListener( 'DOMContentLoaded', init );
50 | }
51 |
52 | /**
53 | * Initialize the view.
54 | **/
55 |
56 | function init() {
57 | populateRecentTracks();
58 | Page.trackPageView();
59 | }
60 |
61 | /**
62 | * Populate the Recently Played list.
63 | **/
64 |
65 | function populateRecentTracks() {
66 | StorageSync.get( 'arrRecentTracks', function( objReturn ) {
67 | var arrRecentTracks = objReturn.arrRecentTracks
68 | , strHtml = ''
69 | ;
70 |
71 | for ( var i = ( arrRecentTracks.length - 1 ); i >= 0; i-- ) {
72 | var arrRecentTrack = arrRecentTracks[ i ]
73 | , strSrc = arrRecentTrack[ 2 ]
74 | , strExtensionId = arrRecentTrack[ 3 ]
75 | ;
76 |
77 | // External module
78 | if ( typeof strExtensionId === 'string' && strExtensionId !== '' ) {
79 | strSrc = 'chrome-extension://'
80 | + strExtensionId
81 | + ( strSrc[ 0 ] === '/' ? '' : '/' )
82 | + strSrc
83 | ;
84 | }
85 |
86 | strHtml += Page.template(
87 | 'recentTrackRow'
88 | , {
89 | track : arrRecentTrack[ 0 ]
90 | , src : strSrc
91 | , alt : arrRecentTrack[ 1 ]
92 | }
93 | );
94 | }
95 |
96 | var boolIsNullCase = strHtml === ''
97 | , $content = document.getElementById( strListId )
98 | ;
99 |
100 | if ( ! boolIsNullCase ) {
101 | $content.innerHTML = strHtml;
102 | }
103 |
104 | poziworldExtension.i18n.init()
105 | .then( Page.localize.bind( null, 'popup' ) )
106 | .then( addEventListeners )
107 | .then( setLinksUrls.bind( null, boolIsNullCase, $content ) );
108 | } );
109 | }
110 |
111 | /**
112 | * Add event listeners.
113 | **/
114 |
115 | function addEventListeners() {
116 | addEvent(
117 | document.getElementById( 'toolbarOpenOptionsPageBtn' )
118 | , 'click'
119 | , function() {
120 | const strLog = 'toolbar';
121 |
122 | trackData(
123 | strLog
124 | , 'openOptions'
125 | , { strPage : strPage }
126 | );
127 |
128 | Global.openOptionsPage( strLogPage + strLogPageDivider + strLog );
129 | }
130 | );
131 |
132 | addEvent(
133 | document.getElementById( 'toolbarClosePopupPageBtn' )
134 | , 'click'
135 | , function() {
136 | trackData(
137 | 'toolbar'
138 | , 'closePopup'
139 | , { strPage : strPage }
140 | );
141 |
142 | window.close();
143 | }
144 | );
145 |
146 | Page.addDevelopersMessageEventListeners();
147 |
148 | addEvent(
149 | document.querySelectorAll( '#tunesSuggestionInfo a' )
150 | , 'click'
151 | , function( objEvent ) {
152 | const $this = objEvent.target;
153 |
154 | trackData(
155 | 'tunesSuggestion'
156 | , 'followLink'
157 | , { strPerformer : $this.dataset.performer }
158 | );
159 |
160 | Global.createTabOrUpdate( $this.href );
161 |
162 | objEvent.preventDefault();
163 | }
164 | );
165 |
166 | addEvent(
167 | document.getElementsByClassName( 'recentTrack' )
168 | , 'mouseleave'
169 | , function( objEvent ) {
170 | const $this = objEvent.currentTarget;
171 |
172 | $this.querySelector( '.fadeOutFadeIn' ).classList.remove( 'show' );
173 | $this.querySelector( '.fadeInFadeOut' ).classList.remove( 'show' );
174 | }
175 | );
176 |
177 | addEvent(
178 | document.getElementsByClassName( 'providerAction' )
179 | , 'click'
180 | , function( objEvent ) {
181 | const $this = objEvent.currentTarget;
182 | const strProvider = $this.dataset.provider;
183 | const strTrack = $this.parentNode.parentNode.dataset.track;
184 | const strUrl = composeRecentTrackActionUrl( strProvider, strTrack );
185 |
186 | trackData(
187 | 'recentTracks'
188 | , 'providerAction'
189 | , { strProvider : strProvider }
190 | );
191 |
192 | Global.createTabOrUpdate( strUrl );
193 | }
194 | );
195 |
196 | addEvent(
197 | document.getElementsByClassName( 'copyToClipboard' )
198 | , 'click'
199 | , onCopyToClipboardCtaClick
200 | );
201 | }
202 |
203 | /**
204 | * "Copy to clipboard" call-to-action is clicked on.
205 | *
206 | * @param {Event} objEvent - MouseEvent object.
207 | **/
208 |
209 | function onCopyToClipboardCtaClick( objEvent ) {
210 | chrome.permissions.contains( { permissions : [ 'clipboardWrite' ] }, function( boolIsGranted ) {
211 | if ( boolIsGranted ) {
212 | copyToClipboard( objEvent );
213 | }
214 | else {
215 | requestClipboardWritePermission( objEvent );
216 | }
217 | } );
218 | }
219 |
220 | /**
221 | * "clipboardWrite" permission hasn't been granted yet, request it.
222 | *
223 | * @param {Event} objEvent - MouseEvent object.
224 | **/
225 |
226 | function requestClipboardWritePermission( objEvent ) {
227 | const strLog = 'requestClipboardWritePermission';
228 | const $privacyStatementsContainer = document.getElementById( 'privacyStatementsContainer' );
229 |
230 | Page.toggleElement( $privacyStatementsContainer, true );
231 |
232 | chrome.permissions.request( { permissions: [ 'clipboardWrite' ] }, function( boolIsGranted ) {
233 | Global.checkForRuntimeError(
234 | function() {
235 | trackData(
236 | 'recentTracks'
237 | , strLog
238 | , { boolIsGranted : boolIsGranted }
239 | );
240 |
241 | if ( boolIsGranted ) {
242 | copyToClipboard( objEvent );
243 | }
244 | }
245 | , undefined
246 | , { strAction : strLog }
247 | , true
248 | );
249 |
250 | Page.toggleElement( $privacyStatementsContainer, false );
251 | } );
252 | }
253 |
254 | /**
255 | * "clipboardWrite" permission granted, copy to clipboard.
256 | *
257 | * @param {Event} objEvent - MouseEvent object.
258 | **/
259 |
260 | function copyToClipboard( objEvent ) {
261 | let $this = objEvent.currentTarget || objEvent.target;
262 | const $text = $this.closest( strListElementSelector );
263 |
264 | if ( ! $text ) {
265 | /**
266 | * @todo Track.
267 | */
268 | return;
269 | }
270 |
271 | const $trackInfo = $text.querySelector( strListElementInfoSelector );
272 |
273 | if ( ! $trackInfo ) {
274 | /**
275 | * @todo Track.
276 | */
277 | return;
278 | }
279 |
280 | trackData(
281 | 'recentTracks'
282 | , 'copyToClipboard'
283 | );
284 |
285 | // http://stackoverflow.com/a/11128179/561712
286 | var objSelection = window.getSelection();
287 | var objRange = document.createRange();
288 |
289 | objRange.selectNodeContents( $trackInfo );
290 | objSelection.removeAllRanges();
291 | objSelection.addRange( objRange );
292 |
293 | document.execCommand( 'copy' );
294 | objSelection.removeAllRanges();
295 |
296 | // Clicked on (target is) the button icon
297 | if ( ! $this.classList.contains( 'cta' ) ) {
298 | $this = $this.parentNode;
299 | }
300 |
301 | Page.showSuccess( $this.children[ 0 ] );
302 | Page.showSuccess( $this.children[ 1 ] );
303 | }
304 |
305 | /**
306 | * If user participates in UEIP, track some helpful insights.
307 | *
308 | * @param {string} strLog - The log entry marker.
309 | * @param {string} strAction - The action being tracked.
310 | * @param {Object} [objData] - Additional data to track.
311 | **/
312 |
313 | function trackData( strLog, strAction, objData ) {
314 | let objTrackingData = {
315 | strAction : strAction
316 | , strLanguage : poziworldExtension.i18n.getLanguage()
317 | , strVersion : strConstExtensionVersion
318 | , strVersionName : strConstExtensionVersionName
319 | };
320 |
321 | if ( typeof objData === 'object' && ! Global.isEmpty( objData ) ) {
322 | for ( let strKey in objData ) {
323 | if ( objData.hasOwnProperty( strKey ) && typeof strKey === 'string' ) {
324 | objTrackingData[ strKey ] = objData[ strKey ];
325 | }
326 | }
327 | }
328 |
329 | if ( typeof strLog === 'string' && strLog !== '' ) {
330 | strLog = strLogPage + strLogPageDivider + strLog;
331 | }
332 | else {
333 | strLog = strLogPage;
334 | }
335 |
336 | chrome.runtime.sendMessage(
337 | {
338 | strReceiver : 'background'
339 | , strLog : strLog
340 | , objVars : objTrackingData
341 | }
342 | );
343 | }
344 |
345 | /**
346 | * Compose a URL from the given parameters
347 | *
348 | * @param {string} strProvider - Service provider.
349 | * @param {string} strQuery - Query.
350 | * @return {string}
351 | **/
352 |
353 | function composeRecentTrackActionUrl( strProvider, strQuery ) {
354 | if ( typeof strProvider === 'undefined'
355 | || typeof strQuery === 'undefined'
356 | || strProvider === ''
357 | || strQuery === ''
358 | ) {
359 | return '';
360 | }
361 |
362 | return strRecentTrackActionUrl
363 | + 'p=' + strProvider
364 | + '&q=' + encodeQuery( strQuery )
365 | + '&v=' + strConstExtensionVersion
366 | + '&l=' + poziworldExtension.i18n.getLanguage()
367 | ;
368 | }
369 |
370 | /**
371 | * Encode query
372 | *
373 | * @param {string} strQuery - Query.
374 | * @return {string}
375 | **/
376 |
377 | function encodeQuery( strQuery ) {
378 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
379 | return encodeURIComponent( strQuery )
380 | // Note that although RFC3986 reserves "!", RFC5987 does not,
381 | // so we do not need to escape it
382 | .replace( /['()]/g, escape ) // i.e., %27 %28 %29
383 | .replace( /\*/g, '%2A' )
384 | // The following are not required for percent-encoding
385 | // per RFC5987, so we can allow for a little better readability
386 | // over the wire: |`^
387 | .replace( /%(?:7C|60|5E)/g, unescape )
388 | ;
389 | }
390 |
391 | /**
392 | * The null case (when there are no items in the Recently Played list) shows suggestions for different styles of music. The text comes from the translation, but the translation doesn't contain URLs.
393 | *
394 | * @param {boolean} nullCase - Whether there are any items in the Recently Played list.
395 | * @param {HTMLElement} container
396 | */
397 |
398 | function setLinksUrls( nullCase, container ) {
399 | if ( nullCase ) {
400 | container.querySelector( '[data-performer="funbox"]' ).href = 'https://vk.com/funboxband';
401 | container.querySelector( '[data-performer="nickybutter"]' ).href = 'https://soundcloud.com/nickybutter';
402 | container.querySelector( '[data-performer="theroux"]' ).href = 'https://soundcloud.com/theroux';
403 | container.querySelector( '[data-performer="emilyclibourn"]' ).href = 'https://soundcloud.com/emilyclibourn';
404 | }
405 | }
406 | } )();
407 |
--------------------------------------------------------------------------------
/static/global/img/cogs_icon&24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/cogs_icon&24.png
--------------------------------------------------------------------------------
/static/global/img/delete_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/delete_icon&16.png
--------------------------------------------------------------------------------
/static/global/img/emotion_smile_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/emotion_smile_icon&16.png
--------------------------------------------------------------------------------
/static/global/img/info-icon.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/static/global/img/list_bullets_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/list_bullets_icon&16.png
--------------------------------------------------------------------------------
/static/global/img/no-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/global/img/notification-icon-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/notification-icon-80.png
--------------------------------------------------------------------------------
/static/global/img/off_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/off_icon&16.png
--------------------------------------------------------------------------------
/static/global/img/options-checkbox-tick-icon-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/options-checkbox-tick-icon-11.png
--------------------------------------------------------------------------------
/static/global/img/playback_next_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/playback_next_icon&16.png
--------------------------------------------------------------------------------
/static/global/img/playback_play_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/playback_play_icon&16.png
--------------------------------------------------------------------------------
/static/global/img/playback_prev_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/playback_prev_icon&16.png
--------------------------------------------------------------------------------
/static/global/img/playback_stop_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/playback_stop_icon&16.png
--------------------------------------------------------------------------------
/static/global/img/pozitone-icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/pozitone-icon-128.png
--------------------------------------------------------------------------------
/static/global/img/pozitone-icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/pozitone-icon-16.png
--------------------------------------------------------------------------------
/static/global/img/pozitone-icon-19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/pozitone-icon-19.png
--------------------------------------------------------------------------------
/static/global/img/pozitone-icon-38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/pozitone-icon-38.png
--------------------------------------------------------------------------------
/static/global/img/pozitone-icon-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/pozitone-icon-48.png
--------------------------------------------------------------------------------
/static/global/img/pozitone-notification-icon-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/pozitone-notification-icon-80.png
--------------------------------------------------------------------------------
/static/global/img/pozitone.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/global/img/projects/pe-icon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/projects/pe-icon-64.png
--------------------------------------------------------------------------------
/static/global/img/projects/pwm-icon-128.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/global/img/projects/sttb-icon-50.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/global/img/projects/swaggy-icon-48.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/global/img/round_plus_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/round_plus_icon&16.png
--------------------------------------------------------------------------------
/static/global/img/sound_down_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/sound_down_icon&16.png
--------------------------------------------------------------------------------
/static/global/img/sound_high_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/sound_high_icon&16.png
--------------------------------------------------------------------------------
/static/global/img/sound_mute_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/sound_mute_icon&16.png
--------------------------------------------------------------------------------
/static/global/img/sound_up_icon&16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/global/img/sound_up_icon&16.png
--------------------------------------------------------------------------------
/static/global/js/angular/angular-route.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.3.14
3 | (c) 2010-2014 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(q,d,C){'use strict';function v(r,k,h){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,f,b,c,y){function z(){l&&(h.cancel(l),l=null);m&&(m.$destroy(),m=null);n&&(l=h.leave(n),l.then(function(){l=null}),n=null)}function x(){var b=r.current&&r.current.locals;if(d.isDefined(b&&b.$template)){var b=a.$new(),c=r.current;n=y(b,function(b){h.enter(b,null,n||f).then(function(){!d.isDefined(t)||t&&!a.$eval(t)||k()});z()});m=c.scope=b;m.$emit("$viewContentLoaded");
7 | m.$eval(w)}else z()}var m,n,l,t=b.autoscroll,w=b.onload||"";a.$on("$routeChangeSuccess",x);x()}}}function A(d,k,h){return{restrict:"ECA",priority:-400,link:function(a,f){var b=h.current,c=b.locals;f.html(c.$template);var y=d(f.contents());b.controller&&(c.$scope=a,c=k(b.controller,c),b.controllerAs&&(a[b.controllerAs]=c),f.data("$ngControllerController",c),f.children().data("$ngControllerController",c));y(a)}}}q=d.module("ngRoute",["ng"]).provider("$route",function(){function r(a,f){return d.extend(Object.create(a),
8 | f)}function k(a,d){var b=d.caseInsensitiveMatch,c={originalPath:a,regexp:a},h=c.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,d,b,c){a="?"===c?c:null;c="*"===c?c:null;h.push({name:b,optional:!!a});d=d||"";return""+(a?"":d)+"(?:"+(a?d:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");c.regexp=new RegExp("^"+a+"$",b?"i":"");return c}var h={};this.when=function(a,f){var b=d.copy(f);d.isUndefined(b.reloadOnSearch)&&(b.reloadOnSearch=!0);
9 | d.isUndefined(b.caseInsensitiveMatch)&&(b.caseInsensitiveMatch=this.caseInsensitiveMatch);h[a]=d.extend(b,a&&k(a,b));if(a){var c="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";h[c]=d.extend({redirectTo:a},k(c,b))}return this};this.caseInsensitiveMatch=!1;this.otherwise=function(a){"string"===typeof a&&(a={redirectTo:a});this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$templateRequest","$sce",function(a,f,b,c,k,q,x){function m(b){var e=s.current;
10 | (v=(p=l())&&e&&p.$$route===e.$$route&&d.equals(p.pathParams,e.pathParams)&&!p.reloadOnSearch&&!w)||!e&&!p||a.$broadcast("$routeChangeStart",p,e).defaultPrevented&&b&&b.preventDefault()}function n(){var u=s.current,e=p;if(v)u.params=e.params,d.copy(u.params,b),a.$broadcast("$routeUpdate",u);else if(e||u)w=!1,(s.current=e)&&e.redirectTo&&(d.isString(e.redirectTo)?f.path(t(e.redirectTo,e.params)).search(e.params).replace():f.url(e.redirectTo(e.pathParams,f.path(),f.search())).replace()),c.when(e).then(function(){if(e){var a=
11 | d.extend({},e.resolve),b,g;d.forEach(a,function(b,e){a[e]=d.isString(b)?k.get(b):k.invoke(b,null,null,e)});d.isDefined(b=e.template)?d.isFunction(b)&&(b=b(e.params)):d.isDefined(g=e.templateUrl)&&(d.isFunction(g)&&(g=g(e.params)),g=x.getTrustedResourceUrl(g),d.isDefined(g)&&(e.loadedTemplateUrl=g,b=q(g)));d.isDefined(b)&&(a.$template=b);return c.all(a)}}).then(function(c){e==s.current&&(e&&(e.locals=c,d.copy(e.params,b)),a.$broadcast("$routeChangeSuccess",e,u))},function(b){e==s.current&&a.$broadcast("$routeChangeError",
12 | e,u,b)})}function l(){var a,b;d.forEach(h,function(c,h){var g;if(g=!b){var k=f.path();g=c.keys;var m={};if(c.regexp)if(k=c.regexp.exec(k)){for(var l=1,n=k.length;l 1 && match[1]) || '';
21 | }
22 |
23 | var versionIdentifier = getFirstMatch(/version\/(\d+(\.\d+)?)/i)
24 | , result
25 |
26 | if (/opera|opr/i.test(ua)) {
27 | result = {
28 | name: 'Opera'
29 | , version: versionIdentifier || getFirstMatch(/(?:opera|opr)[\s\/](\d+(\.\d+)?)/i)
30 | , versionFull: getFirstMatch(/(?:opera|opr)\/(\d+(\.\d+)+(\.\d+)+( \([a-zA-Z0-9 ]{1,50}\))?)/i)
31 | }
32 | }
33 | else if (/yabrowser/i.test(ua)) {
34 | result = {
35 | name: 'Yandex.Browser'
36 | , version: getFirstMatch(/(?:yabrowser)\/(\d+(\.\d+)?)/i)
37 | , versionFull: getFirstMatch(/(?:yabrowser)\/(\d+(\.\d+)+(\.\d+)?)/i)
38 | }
39 | }
40 | else if (/mrchrome soc/i.test(ua)) {
41 | result = {
42 | name: 'Amigo'
43 | , version: getFirstMatch(/(?:chrome)\/(\d+(\.\d+)?)/i)
44 | , versionFull: getFirstMatch(/(?:chrome)\/(\d+(\.\d+)+(\.\d+)?)/i)
45 | }
46 |
47 | result.chromeVersion = result.version;
48 | result.chromeVersionFull = result.versionFull;
49 | }
50 | else if (/nichrome\/self/i.test(ua)) {
51 | result = {
52 | name: 'Rambler-Browser'
53 | , version: getFirstMatch(/(?:nichrome\/self)\/(\d+(\.\d+)?)/i)
54 | , versionFull: getFirstMatch(/(?:chrome)\/(\d+(\.\d+)+(\.\d+)?)/i)
55 | }
56 | }
57 | else if (/dragon/i.test(ua)) {
58 | result = {
59 | name: 'Comodo Dragon'
60 | , version: getFirstMatch(/(?:dragon)\/(\d+(\.\d+)?)/i)
61 | , versionFull: getFirstMatch(/(?:dragon)\/(\d+(\.\d+)+(\.\d+)?)/i)
62 | }
63 | }
64 | else if (/corom/i.test(ua)) {
65 | result = {
66 | name: 'Cốc Cốc'
67 | , version: getFirstMatch(/(?:corom)\/(\d+(\.\d+)?)/i)
68 | , versionFull: getFirstMatch(/(?:corom)\/(\d+(\.\d+)+(\.\d+)?)/i)
69 | }
70 | }
71 | else if (/sleipnir/i.test(ua)) {
72 | result = {
73 | name: 'Sleipnir'
74 | , version: getFirstMatch(/(?:sleipnir)\/(\d+(\.\d+)?)/i)
75 | , versionFull: getFirstMatch(/(?:sleipnir)\/(\d+(\.\d+)+(\.\d+)?)/i)
76 | }
77 | }
78 | else if (/spark/i.test(ua)) {
79 | result = {
80 | name: 'Spark'
81 | , version: getFirstMatch(/(?:spark)\/(\d+(\.\S+)?)/i)
82 | , versionFull: getFirstMatch(/(?:spark)\/(\d+(\.\S+)?)/i)
83 | }
84 | }
85 | else if (/iron/i.test(ua)) {
86 | result = {
87 | name: 'SRWare Iron'
88 | , version: getFirstMatch(/(?:iron)\/(\d+(\.\d+)?)/i)
89 | , versionFull: getFirstMatch(/(?:iron)\/(\d+(\.\d+)+(\.\d+)?)/i)
90 | }
91 | }
92 | else if (/u01-04/i.test(ua)) {
93 | result = {
94 | name: 'Uran'
95 | , version: getFirstMatch(/(?:chrome)\/(\d+(\.\d+)?)/i)
96 | , versionFull: getFirstMatch(/(?:chrome)\/(\d+(\.\d+)+(\.\d+)?)/i)
97 | }
98 |
99 | result.chromeVersion = result.version;
100 | result.chromeVersionFull = result.versionFull;
101 | }
102 | else if (/ edg\//i.test(ua)) {
103 | result = {
104 | name: 'Edge (Chromium)'
105 | , version: getFirstMatch(/(?:edg)\/(\d+(\.\d+)?)/i)
106 | , versionFull: getFirstMatch(/(?:edg)\/(\d+(\.\d+)+(\.\d+)?)/i)
107 | }
108 |
109 | result.chromeVersion = result.version;
110 | result.chromeVersionFull = result.versionFull;
111 | }
112 | else if (/chrome|crios/i.test(ua)) {
113 | result = {
114 | name: 'Chrome' // or a few others that don't identify themselves
115 | , version: getFirstMatch(/(?:chrome|crios)\/(\d+(\.\d+)?)/i)
116 | , versionFull: getFirstMatch(/(?:chrome|crios)\/(\d+(\.\d+)+(\.\d+)?)/i)
117 | }
118 |
119 | result.chromeVersion = result.version;
120 | result.chromeVersionFull = result.versionFull;
121 | }
122 | else result = {
123 | name: 'Unknown'
124 | }
125 |
126 | if ( typeof result.name !== 'undefined' && [ 'Amigo', 'Chrome', 'Unknown', 'Uran', 'Edge (Chromium)' ].indexOf(result.name) === -1 ) {
127 | result.chromeVersion = getFirstMatch(/(?:chrome|crios)\/(\d+(\.\d+)?)/i);
128 | result.chromeVersionFull = getFirstMatch(/(?:chrome|crios)\/(\d+(\.\d+)+(\.\d+)?)/i);
129 | }
130 |
131 | result.userAgent = ua
132 |
133 | return result
134 | }
135 |
136 | var bowser = detect(typeof navigator !== 'undefined' ? navigator.userAgent : '')
137 |
138 | return bowser
139 | });
140 |
--------------------------------------------------------------------------------
/static/global/js/const.js:
--------------------------------------------------------------------------------
1 | /* =============================================================================
2 |
3 | Product : PoziTone
4 | Author : PoziWorld
5 | Copyright : Copyright (c) 2013-2016 PoziWorld
6 | License : pozitone.com/license
7 | File : global/js/const.js
8 | Description : Constants JavaScript
9 |
10 | Table of Contents:
11 |
12 | Constants
13 | Storage
14 | PoziTone
15 |
16 | ============================================================================ */
17 |
18 | 'use strict';
19 |
20 | /* =============================================================================
21 |
22 | Constants
23 |
24 | ============================================================================ */
25 |
26 | const
27 | // Extension
28 | strConstExtensionId = chrome.runtime.id
29 | , objConstExtensionManifest = chrome.runtime.getManifest()
30 | , strConstExtensionName = objConstExtensionManifest.name
31 | , strConstExtensionVersion = objConstExtensionManifest.version
32 | , strConstExtensionVersionName = objConstExtensionManifest.version_name || strConstExtensionVersion
33 |
34 | // Browser & UI
35 | , boolConstIsBowserAvailable = typeof bowser === 'object'
36 | , boolConstIsOpera =
37 | boolConstIsBowserAvailable && bowser.name === 'Opera'
38 | , boolConstIsYandex =
39 | boolConstIsBowserAvailable && bowser.name === 'Yandex.Browser'
40 | , boolConstIsOperaAddon = boolConstIsOpera || boolConstIsYandex
41 | , strConstChromeVersion =
42 | boolConstIsBowserAvailable ? bowser.chromeVersion : ''
43 | , boolConstUseOptionsUi =
44 | strConstChromeVersion >= '40.0' && ! boolConstIsOpera
45 |
46 | // URLs
47 | , strConstVersionParam = '%v'
48 | , strConstLangParam = '%lang'
49 | , strConstMessageUrl =
50 | 'https://poziworld.github.io/PoziTone/message/v%v/?lang=%lang&ref=ext&ueip='
51 | , strConstTranslationUrl = 'https://www.transifex.com/poziworld/pozitone/'
52 |
53 | // External modules, separators, and Notifications
54 | , strConstGenericStringSeparator = '_'
55 | , strConstExternalModuleSeparator = strConstGenericStringSeparator
56 | , strConstNotificationIdSeparator = strConstGenericStringSeparator
57 | , strConstNotificationLinesSeparator = "\n\n"
58 | , strConstNotificationId =
59 | strConstExtensionName + strConstNotificationIdSeparator
60 |
61 | // Developers Message: Browser Action settings (tooltip, badge)
62 | , strConstBadgeOnDevelopersMessageText = '1'
63 | , strConstBadgeOnDevelopersMessageColor = [ 44, 160, 44, 255 ]
64 |
65 | // Developers Message: Alarm
66 | , strConstDevelopersMessageAlarmName = 'developersMessage'
67 | , intConstDevelopersMessageAlarmDelayMinutes = 1440
68 |
69 | // Settings
70 | , strConstSettingsPrefix = 'objSettings_'
71 | , strConstGeneralSettingsSuffix = 'general'
72 | , strConstGeneralSettings =
73 | strConstSettingsPrefix + strConstGeneralSettingsSuffix
74 |
75 | , strConstLogOnInstalled = 'chrome.runtime.onInstalled'
76 |
77 | , objConstUserSetUp = boolConstIsBowserAvailable
78 | ? {
79 | currentVersion : strConstExtensionVersion
80 | , currentVersionName : strConstExtensionVersionName
81 | , browserName : bowser.name
82 | , browserVersion : bowser.version
83 | , browserVersionFull : bowser.versionFull
84 | , chromeVersion : strConstChromeVersion
85 | , chromeVersionFull : bowser.chromeVersionFull
86 | , userAgent : bowser.userAgent
87 |
88 | /**
89 | * @todo Use a listener instead of poziworldExtension.i18n.saveExtensionLanguage
90 | */
91 |
92 | , language : ''
93 | }
94 | : {}
95 |
96 | , objConst = {
97 | strIncentiveCarrotUrl : 'https://cash.me/$PoziTone'
98 | }
99 | ;
100 |
101 | /* =============================================================================
102 |
103 | Storage
104 |
105 | ============================================================================ */
106 |
107 | var StorageApi = chrome.storage
108 | , StorageLocal = StorageApi.local
109 | , StorageSync = StorageApi.sync || StorageLocal
110 | ;
111 |
112 | /* =============================================================================
113 |
114 | PoziTone
115 |
116 | ============================================================================ */
117 |
118 | var pozitone = {};
119 |
--------------------------------------------------------------------------------
/static/global/js/global-v2.js:
--------------------------------------------------------------------------------
1 | /* =============================================================================
2 |
3 | Product: PoziTone
4 | Author: PoziWorld
5 | Copyright: (c) 2016 PoziWorld
6 | License: pozitone.com/license
7 |
8 | Table of Contents:
9 |
10 | Global2
11 | isModuleBuiltIn()
12 | isModuleBuiltInApiCompliant()
13 | isModuleExternal()
14 | On Load
15 | Initialize
16 |
17 | ============================================================================ */
18 |
19 | ( function () {
20 | 'use strict';
21 |
22 | /**
23 | * Browser-specific extension-related URLs.
24 | *
25 | * @typedef {object} ExtensionStoreSpecificUrls
26 | * @property {string} chrome - URL for an extension installed via the Chrome Web Store.
27 | * @property {string} edge - URL for an extension for the new Microsoft Edge.
28 | * @property {string} opera - URL for an extension installed via the Opera add-ons catalogue.
29 | */
30 |
31 | /**
32 | * Browser-specific extension-related URLs.
33 | *
34 | * @typedef {object} Urls
35 | * @property {ExtensionStoreSpecificUrls} installation
36 | * @property {ExtensionStoreSpecificUrls} rating
37 | * @property {ExtensionStoreSpecificUrls} feedback
38 | */
39 |
40 | const URLS = {
41 | installation: {
42 | chrome: 'https://chrome.google.com/webstore/detail/pozitone/bdglbogiolkffcmojmmkipgnpkfipijm',
43 | edge: 'https://microsoftedge.microsoft.com/addons/detail/mnfohmojhhcbbnafeehfhghjaeaokjbl',
44 | opera: 'https://addons.opera.com/extensions/details/pozitone/',
45 | },
46 | rating: {
47 | chrome: 'https://chrome.google.com/webstore/detail/pozitone/bdglbogiolkffcmojmmkipgnpkfipijm/reviews',
48 | edge: 'https://microsoftedge.microsoft.com/addons/detail/mnfohmojhhcbbnafeehfhghjaeaokjbl',
49 | opera: 'https://addons.opera.com/extensions/details/pozitone/#rating-form',
50 | },
51 | feedback: {
52 | chrome: 'https://chrome.google.com/webstore/detail/pozitone/bdglbogiolkffcmojmmkipgnpkfipijm/support',
53 | edge: 'https://feedback.pozitone.com/',
54 | opera: 'https://addons.opera.com/extensions/details/pozitone/#feedback-container',
55 | },
56 | swaggyProject: {
57 | chrome: 'https://chrome.google.com/webstore/detail/beblcchllamebejoakjbhhajpmlkjoaf',
58 | edge: 'https://github.com/PoziWorld/Swaggy',
59 | opera: 'https://chrome.google.com/webstore/detail/beblcchllamebejoakjbhhajpmlkjoaf',
60 | },
61 | poziworldElfProject: {
62 | chrome: 'https://github.com/PoziWorld/PoziWorld-Elf',
63 | edge: 'https://github.com/PoziWorld/PoziWorld-Elf',
64 | opera: 'https://github.com/PoziWorld/PoziWorld-Elf',
65 | },
66 | scrollToTopButtonProject: {
67 | chrome: 'https://chrome.google.com/webstore/detail/scroll-to-top-button/chinfkfmaefdlchhempbfgbdagheknoj',
68 | edge: 'https://microsoftedge.microsoft.com/addons/detail/dobeplcigkjlbajngcgnndecohjkjmia',
69 | opera: 'https://addons.opera.com/extensions/details/scroll-to-top-button/',
70 | },
71 | printWasteMinimizerProject: {
72 | chrome: 'https://chrome.google.com/webstore/detail/print-waste-minimizer/nhglpabogkpplpcemgiaopjoehcpajdk',
73 | edge: 'https://microsoftedge.microsoft.com/addons/detail/badkpckfhemokiobdfnjepgnllimkbia',
74 | opera: 'https://addons.opera.com/extensions/details/print-waste-minimizer/',
75 | },
76 | };
77 |
78 | setUp();
79 |
80 | /**
81 | * Make the logic readily available.
82 | */
83 |
84 | function setUp() {
85 | exposeApi();
86 | }
87 |
88 | /**
89 | * Create an instance of the Global2 API and expose it to other parts of the extension.
90 | */
91 |
92 | function exposeApi() {
93 | if ( typeof pozitone === 'undefined' ) {
94 | window.pozitone = {};
95 | }
96 |
97 | pozitone.global = new Global2();
98 | }
99 |
100 | /**
101 | * @constructor
102 | */
103 |
104 | function Global2() {
105 | }
106 |
107 | /**
108 | * Checks whether the module is built-in.
109 | *
110 | * @type method
111 | * @param strModuleId
112 | * Module ID.
113 | * @return boolean
114 | **/
115 |
116 | Global2.prototype.isModuleBuiltIn = function ( strModuleId ) {
117 | return strModuleId in Global.objModules;
118 | };
119 |
120 | /**
121 | * Checks whether the module is built-in and API compliant.
122 | *
123 | * @type method
124 | * @param strModuleId
125 | * Module ID.
126 | * @param boolIsBuiltIn
127 | * Optional. Whether the module is built-in.
128 | * @return boolean
129 | **/
130 |
131 | Global2.prototype.isModuleBuiltInApiCompliant = function ( strModuleId, boolIsBuiltIn ) {
132 | if ( boolIsBuiltIn || this.isModuleBuiltIn( strModuleId ) ) {
133 | var objModule = Global.objModules[ strModuleId ];
134 |
135 | return typeof objModule.boolIsApiCompliant === 'boolean' && objModule.boolIsApiCompliant;
136 | }
137 |
138 | return false;
139 | };
140 |
141 | /**
142 | * Checks whether the module is external.
143 | *
144 | * @type method
145 | * @param strModuleId
146 | * Module ID.
147 | * @return boolean
148 | **/
149 |
150 | Global2.prototype.isModuleExternal = function ( strModuleId ) {
151 | return ! this.isModuleBuiltIn( strModuleId );
152 | };
153 |
154 | /**
155 | * Don't show these buttons, if they've been clicked for this track already.
156 | *
157 | * @return {string[]} - Messages-indicators.
158 | */
159 |
160 | Global2.prototype.getAddTrackToPlaylistFeedbackMessages = function () {
161 | return [
162 | poziworldExtension.i18n.getMessage( 'notificationAddTrackToPlaylistFeedbackSuccessfullyAdded' ),
163 | poziworldExtension.i18n.getMessage( 'notificationAddTrackToPlaylistFeedbackAlreadyInPlaylist' ),
164 | ];
165 | };
166 |
167 | /**
168 | * Don't show these buttons, if they've been clicked for this track already.
169 | *
170 | * @return {string} - Message-indicator.
171 | */
172 |
173 | Global2.prototype.getFavoriteStatusSuccess = function () {
174 | return poziworldExtension.i18n.getMessage( 'notificationFavoriteStatusSuccess' );
175 | };
176 |
177 | /**
178 | * Some changes might require reloading the extension and reopening the Options page.
179 | *
180 | * @param {string} optionsPageTab - The options page tab/section/“subpage” to reopen after the extension reload.
181 | * @param {string} [logMessage] - The log message passed to the background view.
182 | */
183 |
184 | Global2.prototype.reloadExtensionAndOptions = function ( optionsPageTab, logMessage ) {
185 | Global.setStorageItems(
186 | StorageLocal,
187 | {
188 | boolOpenOptionsPageOnRestart: true,
189 | strOptionsPageToOpen: optionsPageTab,
190 | },
191 | strLog + ', reopen Options',
192 | pozitone.background ?
193 | pozitone.background.reloadExtension :
194 | requestExtensionReload.bind( null, logMessage )
195 | );
196 | };
197 |
198 | /**
199 | * Return browser-specific extension installation URL.
200 | *
201 | * @returns {string}
202 | */
203 |
204 | Global2.prototype.getInstallationUrl = function () {
205 | return getUrl( 'installation' );
206 | };
207 |
208 | /**
209 | * Return browser-specific extension rating/review URL.
210 | *
211 | * @returns {string}
212 | */
213 |
214 | Global2.prototype.getRatingUrl = function () {
215 | return getUrl( 'rating' );
216 | };
217 |
218 | /**
219 | * Return browser-specific extension feedback/issue reporting URL.
220 | *
221 | * @returns {string}
222 | */
223 |
224 | Global2.prototype.getFeedbackUrl = function () {
225 | return getUrl( 'feedback' );
226 | };
227 |
228 | /**
229 | * Return browser-specific sister project URL.
230 | *
231 | * @param {string} urlId
232 | * @returns {string}
233 | */
234 |
235 | Global2.prototype.getSisterProjectUrl = function ( urlId ) {
236 | return getUrl( urlId );
237 | };
238 |
239 | /**
240 | * Ask the background view to handle the extension reload.
241 | *
242 | * @param {string} logMessage - The log message passed to the background view.
243 | */
244 |
245 | function requestExtensionReload( logMessage ) {
246 | chrome.runtime.sendMessage(
247 | {
248 | strReceiver: 'background',
249 | strLog: logMessage,
250 | extensionReloadRequested: true,
251 | }
252 | );
253 | }
254 |
255 | /**
256 | * Return an extension-related URL depending on the browser.
257 | *
258 | * @param {('installation'|'rating'|'feedback')} urlType
259 | */
260 |
261 | function getUrl( urlType ) {
262 | return URLS[ urlType ][ poziworldExtension.utils.getExtensionStoreType() ];
263 | }
264 | } )();
265 |
266 | /* =============================================================================
267 |
268 | On Load
269 |
270 | ============================================================================ */
271 |
272 | /**
273 | * Initializes.
274 | *
275 | * @type method
276 | * @param No Parameters taken
277 | * @return void
278 | **/
279 |
280 | Global.init();
281 |
--------------------------------------------------------------------------------
/static/global/js/i18next/i18nextBrowserLanguageDetector.min.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.i18nextBrowserLanguageDetector=t()}(this,function(){"use strict";function e(e){return i.call(a.call(arguments,1),function(t){if(t)for(var o in t)void 0===e[o]&&(e[o]=t[o])}),e}function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(){return{order:["querystring","cookie","localStorage","navigator","htmlTag"],lookupQuerystring:"lng",lookupCookie:"i18next",lookupLocalStorage:"i18nextLng",caches:["localStorage"],excludeCacheFor:["cimode"]}}var n=[],i=n.forEach,a=n.slice,r={create:function(e,t,o,n){var i=void 0;if(o){var a=new Date;a.setTime(a.getTime()+60*o*1e3),i="; expires="+a.toGMTString()}else i="";n=n?"domain="+n+";":"",document.cookie=e+"="+t+i+";"+n+"path=/"},read:function(e){for(var t=e+"=",o=document.cookie.split(";"),n=0;n0){var r=n[i].substring(0,a);r===e.lookupQuerystring&&(t=n[i].substring(a+1))}}return t}},l=void 0;try{l="undefined"!==window&&null!==window.localStorage;window.localStorage.setItem("i18next.translate.boo","foo"),window.localStorage.removeItem("i18next.translate.boo")}catch(e){l=!1}var s={name:"localStorage",lookup:function(e){var t=void 0;if(e.lookupLocalStorage&&l){var o=window.localStorage.getItem(e.lookupLocalStorage);o&&(t=o)}return t},cacheUserLanguage:function(e,t){t.lookupLocalStorage&&l&&window.localStorage.setItem(t.lookupLocalStorage,e)}},g={name:"navigator",lookup:function(e){var t=[];if("undefined"!=typeof navigator){if(navigator.languages)for(var o=0;o0?t:void 0}},f={name:"htmlTag",lookup:function(e){var t=void 0,o=e.htmlTag||("undefined"!=typeof document?document.documentElement:null);return o&&"function"==typeof o.getAttribute&&(t=o.getAttribute("lang")),t}},d=function(){function e(e,t){for(var o=0;o1&&void 0!==arguments[1]?arguments[1]:{};t(this,n),this.type="languageDetector",this.detectors={},this.init(e,o)}return d(n,[{key:"init",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};this.services=t,this.options=e(n,this.options||{},o()),this.i18nOptions=i,this.addDetector(u),this.addDetector(c),this.addDetector(s),this.addDetector(g),this.addDetector(f)}},{key:"addDetector",value:function(e){this.detectors[e.name]=e}},{key:"detect",value:function(e){var t=this;e||(e=this.options.order);var o=[];e.forEach(function(e){if(t.detectors[e]){var n=t.detectors[e].lookup(t.options);n&&"string"==typeof n&&(n=[n]),n&&(o=o.concat(n))}});var n=void 0;if(o.forEach(function(e){if(!n){var o=t.services.languageUtils.formatLanguageCode(e);t.services.languageUtils.isWhitelisted(o)&&(n=o)}}),!n){var i=this.i18nOptions.fallbackLng;"string"==typeof i&&(i=[i]),i||(i=[]),n="[object Array]"===Object.prototype.toString.apply(i)?i[0]:i[0]||i.default&&i.default[0]}return n}},{key:"cacheUserLanguage",value:function(e,t){var o=this;t||(t=this.options.caches),t&&(this.options.excludeCacheFor&&this.options.excludeCacheFor.indexOf(e)>-1||t.forEach(function(t){o.detectors[t]&&o.detectors[t].cacheUserLanguage(e,o.options)}))}}]),n}();return v.type="languageDetector",v});
--------------------------------------------------------------------------------
/static/global/js/i18next/i18nextXHRBackend.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 | typeof define === 'function' && define.amd ? define(factory) :
4 | (global.i18nextXHRBackend = factory());
5 | }(this, (function () { 'use strict';
6 |
7 | var arr = [];
8 | var each = arr.forEach;
9 | var slice = arr.slice;
10 |
11 | function defaults(obj) {
12 | each.call(slice.call(arguments, 1), function (source) {
13 | if (source) {
14 | for (var prop in source) {
15 | if (obj[prop] === undefined) obj[prop] = source[prop];
16 | }
17 | }
18 | });
19 | return obj;
20 | }
21 |
22 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
23 |
24 | function addQueryString(url, params) {
25 | if (params && (typeof params === 'undefined' ? 'undefined' : _typeof(params)) === 'object') {
26 | var queryString = '',
27 | e = encodeURIComponent;
28 |
29 | // Must encode data
30 | for (var paramName in params) {
31 | queryString += '&' + e(paramName) + '=' + e(params[paramName]);
32 | }
33 |
34 | if (!queryString) {
35 | return url;
36 | }
37 |
38 | url = url + (url.indexOf('?') !== -1 ? '&' : '?') + queryString.slice(1);
39 | }
40 |
41 | return url;
42 | }
43 |
44 | // https://gist.github.com/Xeoncross/7663273
45 | function ajax(url, options, callback, data, cache) {
46 |
47 | if (data && (typeof data === 'undefined' ? 'undefined' : _typeof(data)) === 'object') {
48 | if (!cache) {
49 | data['_t'] = new Date();
50 | }
51 | // URL encoded form data must be in querystring format
52 | data = addQueryString('', data).slice(1);
53 | }
54 |
55 | if (options.queryStringParams) {
56 | url = addQueryString(url, options.queryStringParams);
57 | }
58 |
59 | try {
60 | var x;
61 | if (XMLHttpRequest) {
62 | x = new XMLHttpRequest();
63 | } else {
64 | x = new ActiveXObject('MSXML2.XMLHTTP.3.0');
65 | }
66 | x.open(data ? 'POST' : 'GET', url, options.async);
67 | if (!options.crossDomain) {
68 | x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
69 | }
70 | x.withCredentials = !!options.withCredentials;
71 | if (data) {
72 | x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
73 | }
74 | if (x.overrideMimeType) {
75 | x.overrideMimeType("application/json");
76 | }
77 | var h = options.customHeaders;
78 | if (h) {
79 | for (var i in h) {
80 | x.setRequestHeader(i, h[i]);
81 | }
82 | }
83 | x.onreadystatechange = function () {
84 | x.readyState > 3 && callback && callback(x.responseText, x);
85 | };
86 | x.send(data);
87 | } catch (e) {
88 | console && console.log(e);
89 | }
90 | }
91 |
92 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
93 |
94 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
95 |
96 | function getDefaults() {
97 | return {
98 | loadPath: '/locales/{{lng}}/{{ns}}.json',
99 | addPath: '/locales/add/{{lng}}/{{ns}}',
100 | allowMultiLoading: false,
101 | parse: JSON.parse,
102 | crossDomain: false,
103 | ajax: ajax,
104 | async: true
105 | };
106 | }
107 |
108 | var Backend = function () {
109 | function Backend(services) {
110 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
111 |
112 | _classCallCheck(this, Backend);
113 |
114 | this.init(services, options);
115 |
116 | this.type = 'backend';
117 | }
118 |
119 | _createClass(Backend, [{
120 | key: 'init',
121 | value: function init(services) {
122 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
123 |
124 | this.services = services;
125 | this.options = defaults(options, this.options || {}, getDefaults());
126 | }
127 | }, {
128 | key: 'readMulti',
129 | value: function readMulti(languages, namespaces, callback) {
130 | var loadPath = this.options.loadPath;
131 | if (typeof this.options.loadPath === 'function') {
132 | loadPath = this.options.loadPath(languages, namespaces);
133 | }
134 |
135 | var url = this.services.interpolator.interpolate(loadPath, { lng: languages.join('+'), ns: namespaces.join('+') });
136 |
137 | this.loadUrl(url, callback);
138 | }
139 | }, {
140 | key: 'read',
141 | value: function read(language, namespace, callback) {
142 | var loadPath = this.options.loadPath;
143 | if (typeof this.options.loadPath === 'function') {
144 | loadPath = this.options.loadPath([language], [namespace]);
145 | }
146 |
147 | var url = this.services.interpolator.interpolate(loadPath, { lng: language, ns: namespace });
148 |
149 | this.loadUrl(url, callback);
150 | }
151 | }, {
152 | key: 'loadUrl',
153 | value: function loadUrl(url, callback) {
154 | var _this = this;
155 |
156 | this.options.ajax(url, this.options, function (data, xhr) {
157 | if (xhr.status >= 500 && xhr.status < 600) return callback('failed loading ' + url, true /* retry */);
158 | if (xhr.status >= 400 && xhr.status < 500) return callback('failed loading ' + url, false /* no retry */);
159 |
160 | var ret = void 0,
161 | err = void 0;
162 | try {
163 | ret = _this.options.parse(data, url);
164 | } catch (e) {
165 | err = 'failed parsing ' + url + ' to json';
166 | }
167 | if (err) return callback(err, false);
168 | callback(null, ret);
169 | });
170 | }
171 | }, {
172 | key: 'create',
173 | value: function create(languages, namespace, key, fallbackValue) {
174 | var _this2 = this;
175 |
176 | if (typeof languages === 'string') languages = [languages];
177 |
178 | var payload = {};
179 | payload[key] = fallbackValue || '';
180 |
181 | languages.forEach(function (lng) {
182 | var url = _this2.services.interpolator.interpolate(_this2.options.addPath, { lng: lng, ns: namespace });
183 |
184 | _this2.options.ajax(url, _this2.options, function (data, xhr) {
185 | //const statusCode = xhr.status.toString();
186 | // TODO: if statusCode === 4xx do log
187 | }, payload);
188 | });
189 | }
190 | }]);
191 |
192 | return Backend;
193 | }();
194 |
195 | Backend.type = 'backend';
196 |
197 | return Backend;
198 |
199 | })));
200 |
--------------------------------------------------------------------------------
/static/global/js/log.js:
--------------------------------------------------------------------------------
1 | /* =============================================================================
2 |
3 | Product : PoziTone
4 | Author : PoziWorld
5 | Copyright : Copyright (c) 2013-2016 PoziWorld
6 | License : pozitone.com/license
7 | File : global/js/log.js
8 | Description : Log JavaScript
9 |
10 | Table of Contents:
11 |
12 | Log
13 | init()
14 | add()
15 | setPropertiesOnUserRecord()
16 | Listeners
17 | StorageApi.onChanged
18 | Events
19 |
20 | ============================================================================ */
21 |
22 | /* =============================================================================
23 |
24 | Log
25 |
26 | ============================================================================ */
27 |
28 | const
29 | strLogDo = ', do'
30 | , strLogDoNot = ', do not'
31 | , strLogDone = ', done'
32 | , strLogError = ', error'
33 | , strLogSuccess = ', success'
34 | , strLogNoSuccess = ', no success'
35 |
36 | , strJoinUeipVar = 'strJoinUeip'
37 | , strJoinUeipAgreed = 'yes'
38 | ;
39 |
40 | var
41 | strLog = ''
42 |
43 | , Log = {
44 | strJoinUeip : null
45 | , strLastTrackedEvent : ''
46 | , intTrackCount : 0
47 | , intTrackCountMax : 20
48 | , intTrackCountDelay : 150
49 | ,
50 |
51 | /**
52 | * Initialize
53 | *
54 | * @type method
55 | * @param No Parameters Taken
56 | * @return void
57 | **/
58 | init : function() {
59 | StorageSync.get( strConstGeneralSettings, function( objReturn ) {
60 | var objGeneralSettings = objReturn[ strConstGeneralSettings ];
61 |
62 | if (
63 | typeof objGeneralSettings === 'object'
64 | && typeof objGeneralSettings[ strJoinUeipVar ] === 'string'
65 | )
66 | Log.strJoinUeip = objGeneralSettings[ strJoinUeipVar ];
67 | });
68 | }
69 | ,
70 |
71 | /**
72 | * Add new item to the log (console.log + track)
73 | *
74 | * @type method
75 | * @param strEvent
76 | * Event name/desc
77 | * @param miscVar
78 | * Optional. Var to output contents of
79 | * @param boolTrack
80 | * Optional. Whether to track this
81 | * @param boolDoNotSendData
82 | * Optional. Whether to send details of the event
83 | * @return void
84 | **/
85 | add : function( strEvent, miscVar, boolTrack, boolDoNotSendData ) {
86 | if ( typeof miscVar === 'undefined' )
87 | miscVar = {};
88 |
89 | // Debug
90 | console.log( strEvent, miscVar );
91 |
92 | // Tracking
93 | var funcTrack = function(
94 | strEvent
95 | , miscVar
96 | , boolTrack
97 | , boolDoNotSendData
98 | ) {
99 | var funcTrackRetry;
100 |
101 | if (
102 | Log.strJoinUeip === strJoinUeipAgreed
103 | && typeof boolTrack !== 'undefined'
104 | && boolTrack
105 | ) {
106 | if ( typeof boolDoNotSendData !== 'undefined' && boolDoNotSendData )
107 | miscVar = {};
108 | else if ( Array.isArray( miscVar ) )
109 | miscVar = Global.convertArrToObj( miscVar );
110 |
111 | if (
112 | strEvent !== Log.strLastTrackedEvent
113 | || ! Global.isEmpty( miscVar )
114 | ) {
115 | mixpanel.track( strEvent, miscVar );
116 | Log.strLastTrackedEvent = strEvent;
117 |
118 | if ( strEvent === strConstLogOnInstalled )
119 | Log.setPropertiesOnUserRecord( miscVar );
120 | }
121 | }
122 | // If storage hasn't returned value yet and at least one try left
123 | // TODO: Use observer/deferred instead
124 | else if (
125 | Log.strJoinUeip === null
126 | && Log.intTrackCount < Log.intTrackCountMax
127 | ) {
128 | funcTrackRetry = setTimeout(
129 | function() {
130 | funcTrack(
131 | strEvent
132 | , miscVar
133 | , boolTrack
134 | , boolDoNotSendData
135 | );
136 | }
137 | , Log.intTrackCountDelay
138 | );
139 | Log.intTrackCount++;
140 | }
141 |
142 | // Reset if value received
143 | if ( typeof Log.strJoinUeip === 'string' )
144 | Log.intTrackCount = 0;
145 | };
146 |
147 | funcTrack( strEvent, miscVar, boolTrack, boolDoNotSendData );
148 | }
149 | ,
150 |
151 | /**
152 | * Set properties on a user record
153 | *
154 | * @type method
155 | * @param objProperties
156 | * The properties to set
157 | * @param funcCallback
158 | * Optional. Callback
159 | * @return void
160 | **/
161 | setPropertiesOnUserRecord : function( objProperties, funcCallback ) {
162 | mixpanel.identify( mixpanel.get_distinct_id() );
163 | mixpanel.people.set( objProperties, funcCallback );
164 | }
165 | };
166 |
167 | /* =============================================================================
168 |
169 | Listeners
170 |
171 | ============================================================================ */
172 |
173 | /**
174 | * Fired when one or more items change.
175 | *
176 | * @type method
177 | * @param objMessage
178 | * Message received
179 | * @param objSender
180 | * Sender of the message
181 | * @return void
182 | **/
183 | StorageApi.onChanged.addListener(
184 | function( objChanges, strAreaName ) {
185 | if ( strAreaName === 'sync' ) {
186 | var objGeneralSettings = objChanges[ strConstGeneralSettings ];
187 |
188 | if (
189 | typeof objGeneralSettings === 'object'
190 | && typeof objGeneralSettings.newValue === 'object'
191 | && typeof objGeneralSettings.newValue[ strJoinUeipVar ] === 'string'
192 | )
193 | Log.strJoinUeip = objGeneralSettings.newValue[ strJoinUeipVar ];
194 | else if (
195 | typeof objGeneralSettings === 'object'
196 | && typeof objGeneralSettings.newValue === 'undefined'
197 | && typeof objGeneralSettings.oldValue === 'object'
198 | )
199 | Log.strJoinUeip = null;
200 | }
201 | }
202 | );
203 |
204 | /* =============================================================================
205 |
206 | Events
207 |
208 | ============================================================================ */
209 |
210 | document.addEventListener( 'DOMContentLoaded', Log.init );
211 |
--------------------------------------------------------------------------------
/static/global/js/notifications.js:
--------------------------------------------------------------------------------
1 | /* =============================================================================
2 |
3 | PoziTone
4 | © 2013-2018 PoziWorld, Inc.
5 | https://pozitone.com
6 |
7 | ============================================================================ */
8 |
9 | ( function () {
10 | 'use strict';
11 |
12 | /**
13 | * Callback in case of success.
14 | *
15 | * @callback funcSuccessCallback
16 | */
17 |
18 | /**
19 | * Enhance functionality of https://developer.chrome.com/extensions/notifications
20 | *
21 | * @constructor
22 | */
23 |
24 | function Notifications() {
25 | /**
26 | * Known API error messages.
27 | *
28 | * @typedef {Object} Errors
29 | * @property {string} type - What the error relates to.
30 | */
31 |
32 | const objApiErrorMessages = {
33 | buttons: 'Adding buttons to notifications is not supported.'
34 | };
35 |
36 | /**
37 | * Check whether the last runtime error is what is expected.
38 | *
39 | * @param {string} strErrorMessage - https://developer.chrome.com/extensions/runtime#property-lastError
40 | * @param {Errors~type} strType
41 | * @return {boolean}
42 | * @private
43 | */
44 |
45 | Notifications.prototype._isKnownErrorMessage = function ( strErrorMessage, strType ) {
46 | Log.add( 'pozitone.notifications._getApiErrorMessage' );
47 |
48 | return strErrorMessage === objApiErrorMessages[ strType ];
49 | };
50 | }
51 |
52 | /**
53 | * https://developer.chrome.com/extensions/notifications#method-create
54 | *
55 | * @param {string} [strNotificationId] - Identifier of the notification. If not set or empty, an ID will automatically be generated. If it matches an existing notification, this method first clears that notification before proceeding with the create operation. The identifier may not be longer than 500 characters.
56 | * @param {Object} objNotificationOptions - Contents of the notification.
57 | * @param {funcSuccessCallback} [funcSuccessCallback] - If successfully created.
58 | */
59 |
60 | Notifications.prototype.create = function ( strNotificationId, objNotificationOptions, funcSuccessCallback ) {
61 | Log.add( 'pozitone.notifications.create', strNotificationId, objNotificationOptions );
62 |
63 | const _this = this;
64 | const options = localize( objNotificationOptions );
65 |
66 | chrome.notifications.create(
67 | strNotificationId,
68 | options,
69 | function ( strNotificationId ) {
70 | Global.checkForRuntimeError(
71 | funcSuccessCallback,
72 | function ( strErrorMessage ) {
73 | // Opera doesn't allow buttons
74 | if ( _this._isKnownErrorMessage( strErrorMessage, 'buttons' ) ) {
75 | delete options.buttons;
76 |
77 | chrome.notifications.create(
78 | strNotificationId,
79 | options,
80 | funcSuccessCallback
81 | );
82 | }
83 | }
84 | );
85 | }
86 | );
87 | };
88 |
89 | /**
90 | * The notification title and the buttons' captions need to be localized right before the notification is shown, as i18n API is async.
91 | *
92 | * @param {Object} options
93 | * @return {Object}
94 | */
95 |
96 | function localize( options ) {
97 | options = localizeTitle( options );
98 |
99 | const buttons = options.buttons;
100 |
101 | if ( Array.isArray( buttons ) && buttons.length ) {
102 | buttons.forEach( localizeTitle );
103 |
104 | options.buttons = buttons;
105 | }
106 |
107 | return options;
108 | }
109 |
110 | /**
111 | * Notification itself and buttons are captioned with the “title” property, which needs to be localized.
112 | *
113 | * @param {Object} object
114 | * @return {Object}
115 | */
116 |
117 | function localizeTitle( object ) {
118 | const title = object.title;
119 |
120 | if ( poziworldExtension.utils.isNonEmptyString( title ) ) {
121 | object.title = poziworldExtension.i18n.getMessage( title );
122 | }
123 |
124 | return object;
125 | }
126 |
127 | if ( typeof pozitone === 'undefined' ) {
128 | window.pozitone = {};
129 | }
130 |
131 | pozitone.notifications = new Notifications();
132 | } )();
133 |
--------------------------------------------------------------------------------
/static/global/js/punycode.min.js:
--------------------------------------------------------------------------------
1 | /*! https://mths.be/punycode v1.3.2 by @mathias */
2 | !function(a){function b(a){throw RangeError(E[a])}function c(a,b){for(var c=a.length,d=[];c--;)d[c]=b(a[c]);return d}function d(a,b){var d=a.split("@"),e="";d.length>1&&(e=d[0]+"@",a=d[1]),a=a.replace(D,".");var f=a.split("."),g=c(f,b).join(".");return e+g}function e(a){for(var b,c,d=[],e=0,f=a.length;f>e;)b=a.charCodeAt(e++),b>=55296&&56319>=b&&f>e?(c=a.charCodeAt(e++),56320==(64512&c)?d.push(((1023&b)<<10)+(1023&c)+65536):(d.push(b),e--)):d.push(b);return d}function f(a){return c(a,function(a){var b="";return a>65535&&(a-=65536,b+=H(a>>>10&1023|55296),a=56320|1023&a),b+=H(a)}).join("")}function g(a){return 10>a-48?a-22:26>a-65?a-65:26>a-97?a-97:t}function h(a,b){return a+22+75*(26>a)-((0!=b)<<5)}function i(a,b,c){var d=0;for(a=c?G(a/x):a>>1,a+=G(a/b);a>F*v>>1;d+=t)a=G(a/F);return G(d+(F+1)*a/(a+w))}function j(a){var c,d,e,h,j,k,l,m,n,o,p=[],q=a.length,r=0,w=z,x=y;for(d=a.lastIndexOf(A),0>d&&(d=0),e=0;d>e;++e)a.charCodeAt(e)>=128&&b("not-basic"),p.push(a.charCodeAt(e));for(h=d>0?d+1:0;q>h;){for(j=r,k=1,l=t;h>=q&&b("invalid-input"),m=g(a.charCodeAt(h++)),(m>=t||m>G((s-r)/k))&&b("overflow"),r+=m*k,n=x>=l?u:l>=x+v?v:l-x,!(n>m);l+=t)o=t-n,k>G(s/o)&&b("overflow"),k*=o;c=p.length+1,x=i(r-j,c,0==j),G(r/c)>s-w&&b("overflow"),w+=G(r/c),r%=c,p.splice(r++,0,w)}return f(p)}function k(a){var c,d,f,g,j,k,l,m,n,o,p,q,r,w,x,B=[];for(a=e(a),q=a.length,c=z,d=0,j=y,k=0;q>k;++k)p=a[k],128>p&&B.push(H(p));for(f=g=B.length,g&&B.push(A);q>f;){for(l=s,k=0;q>k;++k)p=a[k],p>=c&&l>p&&(l=p);for(r=f+1,l-c>G((s-d)/r)&&b("overflow"),d+=(l-c)*r,c=l,k=0;q>k;++k)if(p=a[k],c>p&&++d>s&&b("overflow"),p==c){for(m=d,n=t;o=j>=n?u:n>=j+v?v:n-j,!(o>m);n+=t)x=m-o,w=t-o,B.push(H(h(o+x%w,0))),m=G(x/w);B.push(H(h(m,0))),j=i(d,r,f==g),d=0,++f}++d,++c}return B.join("")}function l(a){return d(a,function(a){return B.test(a)?j(a.slice(4).toLowerCase()):a})}function m(a){return d(a,function(a){return C.test(a)?"xn--"+k(a):a})}var n="object"==typeof exports&&exports&&!exports.nodeType&&exports,o="object"==typeof module&&module&&!module.nodeType&&module,p="object"==typeof global&&global;(p.global===p||p.window===p||p.self===p)&&(a=p);var q,r,s=2147483647,t=36,u=1,v=26,w=38,x=700,y=72,z=128,A="-",B=/^xn--/,C=/[^\x20-\x7E]/,D=/[\x2E\u3002\uFF0E\uFF61]/g,E={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},F=t-u,G=Math.floor,H=String.fromCharCode;if(q={version:"1.3.2",ucs2:{decode:e,encode:f},decode:j,encode:k,toASCII:m,toUnicode:l},"function"==typeof define&&"object"==typeof define.amd&&define.amd)define("punycode",function(){return q});else if(n&&o)if(module.exports==n)o.exports=q;else for(r in q)q.hasOwnProperty(r)&&(n[r]=q[r]);else a.punycode=q}(this);
3 |
--------------------------------------------------------------------------------
/static/global/js/tracking.js:
--------------------------------------------------------------------------------
1 | (function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" ");
2 | for(g=0;g
--------------------------------------------------------------------------------
/static/modules/com_jazzradio/img/jazzradio-logo-120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/modules/com_jazzradio/img/jazzradio-logo-120.png
--------------------------------------------------------------------------------
/static/modules/com_jazzradio/js/page-watcher.js:
--------------------------------------------------------------------------------
1 | /* =============================================================================
2 |
3 | Product: PoziTone module for JAZZRADIO.com
4 | Author: PoziWorld
5 | Copyright: (c) 2016 PoziWorld
6 | License: pozitone.com/license
7 |
8 | Table of Contents:
9 |
10 | Const
11 |
12 | ============================================================================ */
13 |
14 | /* =============================================================================
15 |
16 | Const
17 |
18 | ============================================================================ */
19 |
20 | const strModule = 'com_jazzradio';
21 |
--------------------------------------------------------------------------------
/static/modules/com_radiotunes/img/radiotunes-logo-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/modules/com_radiotunes/img/radiotunes-logo-80.png
--------------------------------------------------------------------------------
/static/modules/com_radiotunes/js/page-watcher.js:
--------------------------------------------------------------------------------
1 | /* =============================================================================
2 |
3 | Product: PoziTone module for RadioTunes
4 | Author: PoziWorld
5 | Copyright: (c) 2016 PoziWorld
6 | License: pozitone.com/license
7 |
8 | Table of Contents:
9 |
10 | Const
11 |
12 | ============================================================================ */
13 |
14 | /* =============================================================================
15 |
16 | Const
17 |
18 | ============================================================================ */
19 |
20 | const strModule = 'com_radiotunes';
21 |
--------------------------------------------------------------------------------
/static/modules/com_rockradio/img/rockradio-logo-120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/modules/com_rockradio/img/rockradio-logo-120.png
--------------------------------------------------------------------------------
/static/modules/com_rockradio/js/page-watcher.js:
--------------------------------------------------------------------------------
1 | /* =============================================================================
2 |
3 | Product: PoziTone module for ROCKRADIO.COM
4 | Author: PoziWorld
5 | Copyright: (c) 2016 PoziWorld
6 | License: pozitone.com/license
7 |
8 | Table of Contents:
9 |
10 | Const
11 |
12 | ============================================================================ */
13 |
14 | /* =============================================================================
15 |
16 | Const
17 |
18 | ============================================================================ */
19 |
20 | const strModule = 'com_rockradio';
21 |
--------------------------------------------------------------------------------
/static/modules/com_soundcloud/img/soundcloud-logo-48.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/modules/com_soundcloud/img/soundcloud-logo-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/modules/com_soundcloud/img/soundcloud-logo-80.png
--------------------------------------------------------------------------------
/static/modules/com_vgmradio/img/vgmradio-logo-120.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/modules/com_vk_audio/img/vk-logo-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/modules/com_vk_audio/img/vk-logo-32.png
--------------------------------------------------------------------------------
/static/modules/com_vk_audio/img/vk-logo-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/modules/com_vk_audio/img/vk-logo-80.png
--------------------------------------------------------------------------------
/static/modules/com_vk_audio/img/vk-logo-80.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/modules/com_vk_audio/js/page-watcher-loader.js:
--------------------------------------------------------------------------------
1 | /* =============================================================================
2 |
3 | Product: PoziTone
4 | Author: PoziWorld
5 | Copyright: (c) 2016 PoziWorld
6 | License: pozitone.com/license
7 |
8 | ============================================================================ */
9 |
10 | // Choose the right PageWatcher to initialize, the one for the old site or for the redesigned one.
11 |
12 | ( function ( ) {
13 | if ( document.contains( document.getElementById( 'page_header_cont' ) ) ) {
14 | pozitone.pageWatcherV2.init();
15 | }
16 | else {
17 | pozitone.pageWatcherV1.init();
18 | }
19 | } )();
20 |
--------------------------------------------------------------------------------
/static/modules/fm_di/img/di-logo-120.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/modules/general/js/page-watcher.js:
--------------------------------------------------------------------------------
1 | /* =============================================================================
2 |
3 | Product : PoziTone
4 | Author : PoziWorld
5 | Copyright : Copyright (c) 2013-2016 PoziWorld
6 | License : pozitone.com/license
7 | File : js/page-watcher.js
8 | Description : General Page Watcher JavaScript
9 |
10 | Table of Contents:
11 |
12 | General Page Watcher
13 | getVolumeDeltaSettings()
14 | processButtonClick_volumeUp()
15 | processButtonClick_volumeDown()
16 |
17 | ============================================================================ */
18 |
19 | /* =============================================================================
20 |
21 | General Page Watcher
22 |
23 | ============================================================================ */
24 |
25 | var GeneralPageWatcher = {
26 |
27 | /**
28 | * Get general (default) volume delta settings and for the current player.
29 | *
30 | * @type method
31 | * @param funcSetVolume
32 | * Callback to set changed volume level.
33 | * @return void
34 | **/
35 | getVolumeDeltaSettings : function( funcSetVolume ) {
36 | var arrSettings = [ strModuleSettings, strConstGeneralSettings ];
37 |
38 | StorageSync.get( arrSettings, function( objReturn ) {
39 | var
40 | objModuleSettings = objReturn[ strModuleSettings ]
41 | , objGeneralSettings = objReturn[ strConstGeneralSettings ]
42 | ;
43 |
44 | // Use general delta if set to do so, use player's own delta otherwise
45 | if (
46 | typeof objModuleSettings === 'object'
47 | && typeof objModuleSettings.boolUseGeneralVolumeDelta === 'boolean'
48 | ) {
49 | if (
50 | objModuleSettings.boolUseGeneralVolumeDelta
51 | && typeof objGeneralSettings === 'object'
52 | ) {
53 | var intGeneralVolumeDelta = objGeneralSettings.intVolumeDelta;
54 |
55 | if (
56 | typeof intGeneralVolumeDelta === 'number'
57 | && intGeneralVolumeDelta > 0
58 | )
59 | funcSetVolume( intGeneralVolumeDelta );
60 | }
61 | else {
62 | var intModuleVolumeDelta = objModuleSettings.intVolumeDelta;
63 |
64 | if (
65 | typeof intModuleVolumeDelta === 'number'
66 | && intModuleVolumeDelta > 0
67 | )
68 | funcSetVolume( intModuleVolumeDelta );
69 | }
70 | }
71 | });
72 | }
73 | ,
74 |
75 | /**
76 | * Simulate "volume up" player method
77 | *
78 | * @type method
79 | * @param No Parameters Taken
80 | * @return void
81 | **/
82 | processButtonClick_volumeUp : function() {
83 | PageWatcher.changeVolume( 'up' );
84 | }
85 | ,
86 |
87 | /**
88 | * Simulate "volume up" player method
89 | *
90 | * @type method
91 | * @param No Parameters Taken
92 | * @return void
93 | **/
94 | processButtonClick_volumeDown : function() {
95 | PageWatcher.changeVolume( 'down' );
96 | }
97 | };
98 |
--------------------------------------------------------------------------------
/static/modules/ru_ok_audio/img/ok-logo-80.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/modules/ru_ok_audio/img/ok-logo-orange-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/modules/ru_ok_audio/img/ok-logo-orange-32.png
--------------------------------------------------------------------------------
/static/modules/ru_ok_audio/img/ok-logo-orange-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PoziWorld/PoziTone/866c230f642bcb0efd4629b0bf8888399f36810a/static/modules/ru_ok_audio/img/ok-logo-orange-80.png
--------------------------------------------------------------------------------
/static/options/img/switch-language.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/static/options/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 |
31 |
32 |
122 |
137 |
138 |
163 |
164 |
165 |
--------------------------------------------------------------------------------
/static/options/js/controllers/external-modules-list.js:
--------------------------------------------------------------------------------
1 | // Controller for External Modules list page
2 | optionsControllers.controller( 'ExternalModulesListCtrl', function( $scope, $rootScope ) {
3 | $scope.arrExternalModules = [
4 | {
5 | strModuleId: 'com_youtube',
6 | url: {
7 | chrome: 'https://chrome.google.com/webstore/detail/youtube-embedded-player-p/bajalgkbfjloemafmkiheboebghhibbg',
8 | opera: 'https://addons.opera.com/extensions/details/youtube-embedded-player-pozitone-module/',
9 | edge: 'https://microsoftedge.microsoft.com/addons/detail/endgoolfeicagiackhdalbfkinelcgin',
10 | },
11 | },
12 | {
13 | strModuleId: 'com_soundcloud',
14 | url: {
15 | chrome: 'https://chrome.google.com/webstore/detail/pozitone-module-for-sound/iijloaojdghegopdahladbajodcgnmgh',
16 | opera: 'https://addons.opera.com/extensions/details/soundcloud-widget-pozitone-module/',
17 | edge: 'https://microsoftedge.microsoft.com/addons/detail/imijjplgbohoagfnhbdlfhfcgfikgjab',
18 | },
19 | },
20 | {
21 | strModuleId: 'ru_sovyatnik',
22 | url: {
23 | chrome: 'https://chrome.google.com/webstore/detail/sovyatnik-pozitone-module/ihdoljplikdgegdooeohfmgaaabcbmpn',
24 | opera: 'https://addons.opera.com/extensions/details/soviatnik-pozitone-modul/',
25 | edge: 'https://microsoftedge.microsoft.com/addons/detail/fkgcpgookmofjfedpfhadkgkhddcdbpp',
26 | },
27 | },
28 | ];
29 |
30 | Page.localize( strPage, '#content' );
31 |
32 | strSubpage = 'modules-external';
33 | strSubsection = undefined;
34 |
35 | Page.trackPageView( strSubpage );
36 |
37 | $rootScope.toggleExternalLinksListeners(
38 | true
39 | , 'content'
40 | , strPage
41 | , strSubpage
42 | );
43 |
44 | /**
45 | * When a link leading to any website is clicked, track click.
46 | *
47 | * @type method
48 | * @param objEvent
49 | * MouseEvent object.
50 | * @return void
51 | **/
52 |
53 | $scope.trackExternalLinkClick = function( objEvent ) {
54 | $rootScope.trackExternalLinkClick( objEvent );
55 | };
56 |
57 | /**
58 | * When a link leading to any website is clicked, track click.
59 | *
60 | * @type method
61 | * @param objEvent
62 | * MouseEvent object.
63 | * @return void
64 | **/
65 |
66 | $scope.install = function( objEvent ) {
67 | const moduleId = objEvent.target.getAttribute( 'data-module-id' );
68 | const MATCHING_RESULT_INDEX = 0;
69 | const MODULE_DETAILS_URL_KEY = 'url';
70 | const url = $scope.arrExternalModules.filter( function ( externalModuleDetails ) {
71 | return moduleId === externalModuleDetails.strModuleId;
72 | } )[ MATCHING_RESULT_INDEX ][ MODULE_DETAILS_URL_KEY ][ poziworldExtension.utils.getExtensionStoreType() ];
73 |
74 | Global.createTabOrUpdate( url );
75 | };
76 | } );
77 |
--------------------------------------------------------------------------------
/static/options/js/controllers/misc.js:
--------------------------------------------------------------------------------
1 | // Controller for Sister Projects page
2 | optionsControllers.controller( 'ProjectsCtrl', function( $scope, $rootScope ) {
3 | $scope.boolIsNotOperaAddon = ! boolConstIsOperaAddon;
4 |
5 | $scope.arrProjects = [
6 | {
7 | strName: 'Swaggy',
8 | abbreviatedName: 'swaggy',
9 | strImageFileName: 'swaggy-icon-48.svg',
10 | /**
11 | * @todo Camelize strName instead?
12 | */
13 | urlId: 'swaggyProject',
14 | },
15 | {
16 | strName: 'PoziWorld Elf',
17 | abbreviatedName: 'pe',
18 | strImageFileName: 'pe-icon-64.png',
19 | urlId: 'poziworldElfProject',
20 | },
21 | {
22 | strName: 'Scroll To Top Button',
23 | abbreviatedName: 'sttb',
24 | strImageFileName: 'sttb-icon-50.svg',
25 | urlId: 'scrollToTopButtonProject',
26 | },
27 | {
28 | strName: 'Print Waste Minimizer',
29 | abbreviatedName: 'pwm',
30 | strImageFileName: 'pwm-icon-128.svg',
31 | urlId: 'printWasteMinimizerProject',
32 | },
33 | ];
34 |
35 | Page.localize( strPage, '#content' );
36 |
37 | strSubpage = 'projects';
38 | strSubsection = undefined;
39 |
40 | Page.trackPageView( strSubpage );
41 |
42 | $rootScope.toggleExternalLinksListeners(
43 | true
44 | , 'content'
45 | , strPage
46 | , strSubpage
47 | );
48 |
49 | /**
50 | * Return browser-specific sister project URL.
51 | *
52 | * @param {string} urlId
53 | */
54 |
55 | $scope.getSisterProjectUrl = function ( urlId ) {
56 | return pozitone.global.getSisterProjectUrl( urlId );
57 | };
58 | } );
59 |
60 | // Controller for Contribution page
61 | optionsControllers.controller( 'ContributionCtrl', function( $scope, $rootScope ) {
62 | Page.localize( strPage, '#content' );
63 |
64 | strSubpage = 'contribution';
65 | strSubsection = undefined;
66 |
67 | Page.trackPageView( strSubpage );
68 |
69 | $rootScope.toggleExternalLinksListeners(
70 | true
71 | , 'content'
72 | , strPage
73 | , strSubpage
74 | );
75 |
76 | document.getElementById( 'installationLink' ).href = pozitone.global.getInstallationUrl();
77 | document.getElementById( 'rateLink' ).href = pozitone.global.getRatingUrl();
78 | } );
79 |
80 | // Controller for Feedback page
81 | optionsControllers.controller( 'FeedbackCtrl', function( $scope, $rootScope ) {
82 | Page.localize( strPage, '#content' );
83 |
84 | strSubpage = 'feedback';
85 | strSubsection = undefined;
86 |
87 | Page.trackPageView( strSubpage );
88 |
89 | $rootScope.toggleExternalLinksListeners(
90 | true
91 | , 'content'
92 | , strPage
93 | , strSubpage
94 | );
95 |
96 | document.getElementById( 'reviewLink' ).href = pozitone.global.getRatingUrl();
97 | document.getElementById( 'bugLink' ).href = pozitone.global.getFeedbackUrl();
98 | document.getElementById( 'incentiveLink' ).href = objConst.strIncentiveCarrotUrl;
99 | } );
100 |
101 | // Controller for About page
102 | optionsControllers.controller( 'AboutCtrl', function( $scope, $rootScope ) {
103 | document.getElementById( 'logo' ).alt = strConstExtensionName;
104 | document.getElementById( 'name' ).textContent = strConstExtensionName;
105 | document.getElementById( 'version' ).textContent = strConstExtensionVersionName;
106 |
107 | Page.localize( strPage, '#content' );
108 | setLinks();
109 |
110 | strSubpage = 'about';
111 | strSubsection = undefined;
112 |
113 | Page.trackPageView( strSubpage );
114 |
115 | $rootScope.toggleExternalLinksListeners(
116 | true
117 | , 'content'
118 | , strPage
119 | , strSubpage
120 | );
121 |
122 | /**
123 | * Some links come from the translation files and don't have URLs, so it's easier to make updates to the URLs.
124 | * Others use Markdown, so the markup is not hardcoded in the translation files.
125 | */
126 |
127 | function setLinks() {
128 | const translationPortalLink = document.querySelector( '[data-id="translation"]' );
129 | const translatedByText = document.getElementById( 'translatedBy' );
130 |
131 | if ( translationPortalLink ) {
132 | translationPortalLink.href = strConstTranslationUrl;
133 | }
134 |
135 | if ( translatedByText ) {
136 | translatedByText.innerHTML = translatedByText.innerHTML.replace(
137 | // Markdown-style link: [John Doe](https://www.transifex.com/user/profile/john.doe777/)
138 | /(\[)([^\]]+\.?)(\])(\()(http[s]:\/\/(-\.)?([^\s\/?\.\#\-]+\.?)+(\/[^\s]*)?)(\))/g
139 | , '$2'
140 | );
141 | }
142 | }
143 | } );
144 |
145 | // Controller for Help page
146 | optionsControllers.controller( 'HelpCtrl', function( $scope ) {
147 | Options.removeNotAvailable();
148 | Page.localize( strPage, '#content' );
149 |
150 | strSubpage = 'help';
151 | strSubsection = undefined;
152 |
153 | Page.trackPageView( 'help' );
154 |
155 | // Show debug info
156 | var strHtml = '';
157 |
158 | for ( var miscProperty in objConstUserSetUp ) {
159 | if ( objConstUserSetUp.hasOwnProperty( miscProperty ) ) {
160 | strHtml += Page.template(
161 | 'helpInfoToSubmitTmpl'
162 | , {
163 | key : miscProperty
164 | , value : objConstUserSetUp[ miscProperty ]
165 | }
166 | );
167 | }
168 | }
169 |
170 | if ( strHtml !== '' )
171 | document.getElementById( strHelpInfoToSubmitId ).innerHTML = strHtml;
172 | } );
173 |
174 | // Controller for ❤ page
175 | optionsControllers.controller( '❤Ctrl', function( $scope ) {
176 | document.getElementById( 'header' ).hidden = true;
177 | document.getElementById( 'toolbar' ).hidden = true;
178 | document.getElementById( 'footer' ).hidden = true;
179 |
180 | const $$ascii = document.getElementById( '❤❤' );
181 | const strReadyAttribute = 'data-ready';
182 | const boolIsReady = JSON.parse( $$ascii.getAttribute( strReadyAttribute ) );
183 |
184 | if ( ! boolIsReady ) {
185 | $$ascii.textContent = window.atob( $$ascii.textContent );
186 | $$ascii.setAttribute( strReadyAttribute, true );
187 | }
188 |
189 | strSubpage = '❤';
190 | strSubsection = undefined;
191 |
192 | Page.trackPageView( '❤' );
193 | } );
194 |
--------------------------------------------------------------------------------
/static/options/js/controllers/settings.js:
--------------------------------------------------------------------------------
1 | // Controller for General and Modules' Settings
2 | optionsControllers.controller( 'SettingsCtrl', function(
3 | $scope
4 | , $rootScope
5 | , $route
6 | , $routeParams
7 | , $location
8 | ) {
9 | // If there are no enabled modules, show modules list - START
10 | var objModules = $rootScope.objModules
11 | , strKey
12 | , intEnabledModules = 0
13 | , objModule
14 | , boolIsEnabled
15 | ;
16 |
17 | for ( strKey in objModules ) {
18 | if ( objModules.hasOwnProperty( strKey ) ) {
19 | objModule = objModules[ strKey ];
20 | boolIsEnabled = objModule.boolIsEnabled;
21 |
22 | if ( typeof boolIsEnabled === 'boolean' && boolIsEnabled ) {
23 | intEnabledModules++;
24 | }
25 | }
26 | }
27 |
28 | if ( intEnabledModules === 0
29 | && typeof $rootScope.boolWasPageJustOpened !== 'boolean'
30 | ) {
31 | $rootScope.boolWasPageJustOpened = true;
32 |
33 | var boolPreventLocationOverriding = $rootScope.boolPreventLocationOverriding;
34 |
35 | if ( typeof boolPreventLocationOverriding !== 'boolean'
36 | || ! boolPreventLocationOverriding
37 | ) {
38 | $location.path( '/settings/modules/built-in' );
39 | return;
40 | }
41 | }
42 | // If there are no enabled modules, show modules list - END
43 |
44 | var boolShowAdvancedSettings = $rootScope.objModules[ strConstGeneralSettingsSuffix ].boolShowAdvancedSettings
45 | , strRouteModuleId = $routeParams.moduleId
46 | , strModuleId = strRouteModuleId || strConstGeneralSettingsSuffix
47 | ;
48 |
49 | objModule = $rootScope.objModules[ strModuleId ];
50 |
51 | // Buttons
52 | var arrAvailableNotificationButtons = objModule.arrAvailableNotificationButtons
53 | , arrActiveNotificationButtons = objModule.arrActiveNotificationButtons
54 | // Icon Formats
55 | , arrAvailableNotificationIconFormats = objModule.arrAvailableNotificationIconFormats
56 | // Title Formats
57 | , arrAvailableNotificationTitleFormats = objModule.arrAvailableNotificationTitleFormats
58 | ;
59 |
60 | // Remember chosen settings subpage
61 | strChosenSettingsSubpage = strModuleId;
62 |
63 | // When settings get saved, they use strChosenSettingsSubpage.
64 | // But this one belongs only to general, thus we need to have a way
65 | // to override strChosenSettingsSubpage.
66 | if ( typeof boolShowAdvancedSettings === 'boolean' ) {
67 | $scope.boolShowAdvancedSettings = boolShowAdvancedSettings;
68 | $scope.strConstGeneralSettingsSuffix = strConstGeneralSettingsSuffix;
69 | }
70 |
71 | // Checks if array is not empty.
72 | // If so, creates it in $scope.
73 | function checkArray( arrVar, strArrVarName ) {
74 | if ( Array.isArray( arrVar ) && ! Global.isEmpty( arrVar ) ) {
75 | $scope[ strArrVarName ] = arrVar;
76 |
77 | return true;
78 | }
79 |
80 | return false;
81 | }
82 |
83 | $scope.objModule = objModule;
84 |
85 | // Available buttons
86 | $scope.boolProvidesButtons =
87 | checkArray(
88 | arrAvailableNotificationButtons
89 | , 'arrAvailableNotificationButtons'
90 | );
91 |
92 | // Active buttons
93 | $scope.boolHasActiveButtons =
94 | checkArray(
95 | arrActiveNotificationButtons
96 | , 'arrActiveNotificationButtons'
97 | );
98 |
99 | // Icon Formats
100 | $scope.boolProvidesIconFormats =
101 | checkArray(
102 | arrAvailableNotificationIconFormats
103 | , 'arrAvailableNotificationIconFormats'
104 | );
105 |
106 | // Title Formats
107 | $scope.boolProvidesTitleFormats =
108 | checkArray(
109 | arrAvailableNotificationTitleFormats
110 | , 'arrAvailableNotificationTitleFormats'
111 | );
112 |
113 | $scope.format = 'M/d/yy h:mm:ss a';
114 |
115 | // $includeContentLoaded doesn't trigger on Voice control page
116 | if ( ! strRouteModuleId ) {
117 | $settingsSaved = document.getElementById( 'settingsSaved' );
118 | }
119 |
120 | // On settings page loaded
121 | $scope.$on( '$includeContentLoaded', function( $scope ) {
122 | Options.setPageValues();
123 |
124 | Page.localize( strPage, '#content' );
125 |
126 | strSubpage = 'settings';
127 | strSubsection = strModuleId;
128 |
129 | Page.trackPageView( strSubpage, strSubsection );
130 |
131 | $rootScope.toggleExternalLinksListeners(
132 | true
133 | , 'content'
134 | , strPage
135 | , strSubpage
136 | , strSubsection
137 | );
138 | } );
139 |
140 | /**
141 | * TODO
142 | *
143 | * @type method
144 | * @param funcResolve
145 | * @param funcReject
146 | * @return void
147 | **/
148 |
149 | $scope.requestManagementPermission = function( funcResolve, funcReject ) {
150 | // TODO
151 | funcReject();
152 | };
153 |
154 | /**
155 | * Force "page refresh" if some changes can be reflected only after refresh.
156 | **/
157 |
158 | $scope.forcePageRefresh = function() {
159 | $route.reload();
160 | };
161 |
162 | /**
163 | * Save new setting value.
164 | *
165 | * @param {(Event|Object)} event - Event object.
166 | * @param {string} [groupModel] - Checkbox group model name.
167 | * @param {string} [moduleOverride] - If the setting belongs to a different module, then the rest on the current Options page.
168 | * @param {function} [callbackBefore] - Additional logic to do before saving.
169 | * @param {function} [callbackAfter] - Additional logic to do after saving.
170 | **/
171 |
172 | $scope.inputChange = function ( event, groupModel, moduleOverride, callbackBefore, callbackAfter ) {
173 | if ( poziworldExtension.utils.isType( callbackBefore, 'function' ) ) {
174 | new Promise( callbackBefore )
175 | .then( onSettingChange.bind( null, event, groupModel, moduleOverride, callbackAfter ) );
176 | }
177 | else {
178 | onSettingChange( event, groupModel, moduleOverride, callbackAfter );
179 | }
180 | };
181 |
182 | /**
183 | * Handle dropdown (select) value change.
184 | *
185 | * @param {string} id - Dropdown element ID attribute.
186 | * @param callback
187 | */
188 |
189 | $scope.handleDropdownChange = function ( id, callback ) {
190 | this.$parent.inputChange(
191 | {
192 | target: document.getElementById( id ),
193 | },
194 | undefined,
195 | undefined,
196 | undefined,
197 | callback
198 | );
199 | };
200 |
201 | /**
202 | * Apply the UI language change.
203 | */
204 |
205 | $scope.changeUiLanguage = function () {
206 | pozitone.global.reloadExtensionAndOptions( 'settingsGeneral', 'changeUiLanguage' );
207 | };
208 |
209 | /**
210 | * Process the setting change.
211 | *
212 | * @param {(Event|Object)} $event - Event object.
213 | * @param {string} [strGroupModel] - Checkbox group model name.
214 | * @param {string} [strModuleOverride] - If the setting belongs to a different module, then the rest on the current Options page.
215 | * @param {function} [funcDoAfter] - Additional logic to do after saving.
216 | */
217 |
218 | function onSettingChange( $event, strGroupModel, strModuleOverride, funcDoAfter ) {
219 | Options.onSettingChange( $event, strModuleOverride );
220 |
221 | var _target = $event.target
222 | , strName = _target.name
223 | , strValue = _target.value
224 | , boolChecked = _target.checked
225 | ;
226 |
227 | // ng-model doesn't work right for checkbox group
228 | if ( typeof strGroupModel === 'string' ) {
229 | var arrGroup = $scope.objModule[ strGroupModel ];
230 |
231 | // Save if just selected and not in array yet
232 | if ( boolChecked && ~~ arrGroup.indexOf( strValue ) ) {
233 | arrGroup.push( strValue );
234 | }
235 | // Remove if just unselected and in array
236 | else if ( ! boolChecked && ~ arrGroup.indexOf( strValue ) ) {
237 | arrGroup.splice( arrGroup.indexOf( strValue ), 1 );
238 | }
239 | }
240 |
241 | // model doesn't get changed for some reason
242 | if ( typeof strModuleOverride === 'string' ) {
243 | if ( _target.type === 'checkbox' ) {
244 | $scope[ strName ] = boolChecked;
245 | $rootScope.objModules[ strModuleOverride ][ strName ] = boolChecked;
246 | }
247 | }
248 |
249 | // Track setting change if needed
250 | if ( _target.getAttribute( 'data-track-setting-change' ) ) {
251 | var miscTrackedValue;
252 |
253 | // TODO: Add other types
254 | if ( _target.type === 'checkbox' ) {
255 | miscTrackedValue = boolChecked;
256 | }
257 | else if ( _target.tagName === 'SELECT' ) {
258 | miscTrackedValue = _target.value;
259 | }
260 |
261 | chrome.runtime.sendMessage(
262 | {
263 | strReceiver : 'background'
264 | , strLog : 'settingChange'
265 | , objVars : {
266 | strName : strName
267 | , miscValue : miscTrackedValue
268 | , strModule : strModuleId
269 | }
270 | }
271 | );
272 | }
273 |
274 | if ( typeof funcDoAfter === 'function' ) {
275 | funcDoAfter();
276 | }
277 | }
278 | } );
279 |
--------------------------------------------------------------------------------
/static/options/js/controllers/voice-control.js:
--------------------------------------------------------------------------------
1 | // Controller for Voice Control page
2 | optionsControllers.controller( 'VoiceControlCtrl', function( $scope, $rootScope ) {
3 | Page.localize( strPage, '#content' );
4 |
5 | strSubpage = 'voice-control';
6 | strSubsection = undefined;
7 |
8 | Page.trackPageView( strSubpage );
9 |
10 | $rootScope.toggleExternalLinksListeners(
11 | true
12 | , 'content'
13 | , strPage
14 | , strSubpage
15 | );
16 |
17 | const $enableVoiceControl = document.getElementById( 'boolEnableVoiceControl' );
18 | const $voiceControlActivateCta = document.getElementById( 'voiceControlActivateCta' );
19 | let boolIsOnVoiceControlDeactivationListenerSet = false;
20 | const objModules = $rootScope.objModules;
21 |
22 | if ( typeof objModules === 'object' ) {
23 | var objGeneralSettings = objModules.general;
24 |
25 | if ( typeof objGeneralSettings !== 'object' ) {
26 | /**
27 | * @todo Logic below won't work, handle error
28 | */
29 | }
30 | }
31 |
32 | // Check whether it'd already been enabled
33 | chrome.permissions.contains( { permissions : [ 'nativeMessaging' ] }, function( boolIsGranted ) {
34 | if ( boolIsGranted ) {
35 | const boolEnableVoiceControl = objGeneralSettings.boolEnableVoiceControl;
36 |
37 | // Voice control 'Enabled' state had been saved
38 | if ( typeof boolEnableVoiceControl === 'boolean' && boolEnableVoiceControl ) {
39 | $enableVoiceControl.checked = true;
40 | $enableVoiceControl.disabled = false;
41 | $voiceControlActivateCta.disabled = false;
42 |
43 | return;
44 | }
45 | }
46 |
47 | $enableVoiceControl.checked = false;
48 | $enableVoiceControl.disabled = false;
49 | $voiceControlActivateCta.disabled = true;
50 | } );
51 |
52 | /**
53 | * Toggle voice control setting via permissions API and in storage
54 | *
55 | * @param {MouseEvent} objEvent - MouseEvent object.
56 | **/
57 |
58 | $scope.toggleVoiceControl = function( objEvent ) {
59 | // Avoid second click while in process
60 | $enableVoiceControl.disabled = true;
61 |
62 | // It just made it checked, so check for the opposite
63 | if ( $enableVoiceControl.checked ) {
64 | $scope.enableVoiceControl( objEvent );
65 | }
66 | else {
67 | $scope.disableVoiceControl( objEvent );
68 | }
69 | };
70 |
71 | /**
72 | * Request 'nativeMessaging' permission and save the state in storage.
73 | *
74 | * @param {MouseEvent} objEvent - MouseEvent object.
75 | **/
76 |
77 | $scope.enableVoiceControl = function( objEvent ) {
78 | const objLogDetails = {};
79 |
80 | chrome.permissions.request( { permissions: [ 'nativeMessaging' ] }, function( boolIsGranted ) {
81 | const strPermissionRequestLog = strLog = 'enableVoiceControl';
82 |
83 | function onFinished( boolIsEnabled ) {
84 | $enableVoiceControl.checked = boolIsEnabled;
85 | $enableVoiceControl.disabled = false;
86 | $voiceControlActivateCta.disabled = ! boolIsEnabled;
87 |
88 | Log.add( strPermissionRequestLog, objLogDetails, true );
89 | }
90 |
91 | Global.checkForRuntimeError(
92 | function() {
93 | objLogDetails.boolIsRuntimeLastErrorNotSet = true;
94 |
95 | if ( boolIsGranted ) {
96 | objLogDetails.boolIsPermissionGranted = true;
97 | objGeneralSettings.boolEnableVoiceControl = true;
98 |
99 | let objModulesSettings = {};
100 |
101 | objModulesSettings[ strConstGeneralSettings ] = objGeneralSettings;
102 |
103 | Global.setStorageItems(
104 | StorageSync
105 | , objModulesSettings
106 | , strPermissionRequestLog
107 | , function() {
108 | $scope.saveModulesSettingsScope( objModulesSettings );
109 |
110 | objLogDetails.boolAreStorageItemsSet = true;
111 |
112 | onFinished( true );
113 | }
114 | , function() {
115 | objLogDetails.boolAreStorageItemsSet = false;
116 |
117 | onFinished( false );
118 | /**
119 | * @todo Show error message to user
120 | */
121 | }
122 | , objLogDetails
123 | );
124 | }
125 | else {
126 | objLogDetails.boolIsPermissionGranted = false;
127 | objLogDetails.boolAreStorageItemsSet = false;
128 |
129 | onFinished( false );
130 | /**
131 | * @todo Show error message to user
132 | */
133 | }
134 | }
135 | , function() {
136 | objLogDetails.boolIsRuntimeLastErrorNotSet = false;
137 | objLogDetails.boolIsPermissionGranted = false;
138 | objLogDetails.boolAreStorageItemsSet = false;
139 |
140 | onFinished( false );
141 | /**
142 | * @todo Show error message to user
143 | */
144 | }
145 | , objLogDetails
146 | , true
147 | );
148 | } );
149 | };
150 |
151 | /**
152 | * Request 'nativeMessaging' permission and save the state in storage.
153 | *
154 | * @param {MouseEvent} objEvent - MouseEvent object.
155 | **/
156 |
157 | $scope.disableVoiceControl = function( objEvent ) {
158 | const objLogDetails = {};
159 |
160 | chrome.permissions.remove( { permissions: [ 'nativeMessaging' ] }, function( boolIsRemoved ) {
161 | const strPermissionsRemoveLog = strLog = 'disableVoiceControl';
162 |
163 | function onFinished( boolIsDisabled ) {
164 | $enableVoiceControl.checked = ! boolIsDisabled;
165 | $enableVoiceControl.disabled = false;
166 | $voiceControlActivateCta.disabled = boolIsDisabled;
167 |
168 | Log.add( strPermissionsRemoveLog, objLogDetails, true );
169 | }
170 |
171 | Global.checkForRuntimeError(
172 | function() {
173 | objLogDetails.boolIsRuntimeLastErrorNotSet = true;
174 |
175 | if ( boolIsRemoved ) {
176 | objLogDetails.boolIsPermissionRemoved = true;
177 | objGeneralSettings.boolEnableVoiceControl = false;
178 |
179 | let objModulesSettings = {};
180 |
181 | objModulesSettings[ strConstGeneralSettings ] = objGeneralSettings;
182 |
183 | Global.setStorageItems(
184 | StorageSync
185 | , objModulesSettings
186 | , strPermissionsRemoveLog
187 | , function() {
188 | $scope.saveModulesSettingsScope( objModulesSettings );
189 |
190 | objLogDetails.boolAreStorageItemsSet = true;
191 |
192 | onFinished( true );
193 | }
194 | , function() {
195 | objLogDetails.boolAreStorageItemsSet = false;
196 |
197 | onFinished( false );
198 | /**
199 | * @todo Show error message to user
200 | */
201 | }
202 | , objLogDetails
203 | );
204 | }
205 | else {
206 | objLogDetails.boolIsPermissionRemoved = false;
207 | objLogDetails.boolAreStorageItemsSet = false;
208 |
209 | onFinished( false );
210 | /**
211 | * @todo Show error message to user
212 | */
213 | }
214 | }
215 | , function() {
216 | objLogDetails.boolIsRuntimeLastErrorNotSet = false;
217 | objLogDetails.boolIsPermissionRemoved = false;
218 | objLogDetails.boolAreStorageItemsSet = false;
219 |
220 | onFinished( false );
221 | /**
222 | * @todo Show error message to user
223 | */
224 | }
225 | , objLogDetails
226 | , true
227 | );
228 | } );
229 | };
230 |
231 | /**
232 | * When switching Options tabs (pages, subpages),
233 | * changes should be preserved.
234 | *
235 | * @todo Get rid of this duplicate
236 | *
237 | * @param {Object} objModulesSettings - Modules settings object.
238 | **/
239 | $scope.saveModulesSettingsScope = function( objModulesSettings ) {
240 | if ( typeof objModulesSettings !== 'object' ) {
241 | return;
242 | }
243 |
244 | var strModuleSettings, strModuleId;
245 |
246 | for ( strModuleSettings in objModulesSettings ) {
247 | if ( objModulesSettings.hasOwnProperty( strModuleSettings ) ) {
248 | strModuleId = strModuleSettings.replace( strConstSettingsPrefix, '' );
249 |
250 | $scope.$parent.objModules[ strModuleId ] = objModulesSettings[ strModuleSettings ];
251 | }
252 | }
253 |
254 | $scope.intChanges = 0;
255 |
256 | $scope.$apply();
257 | };
258 |
259 | document.getElementById( 'voiceControlActivateCta' ).addEventListener( 'click', function( objEvent ) {
260 | pozitoneModule.sdk.activateVoiceControl(
261 | onVoiceControlAlreadyActivated
262 | , onVoiceControlNotActivated
263 | );
264 | } );
265 |
266 | function onVoiceControlAlreadyActivated() {
267 | $voiceControlActivateCta.disabled = true;
268 | $voiceControlActivateCta.title = poziworldExtension.i18n.getMessage( 'voiceControlAlreadyActivated' );
269 |
270 | if ( ! boolIsOnVoiceControlDeactivationListenerSet ) {
271 | pozitoneModule.sdk.addOnVoiceControlDeactivationListener( onVoiceControlNotActivated );
272 | boolIsOnVoiceControlDeactivationListenerSet = true;
273 | }
274 | }
275 |
276 | function onVoiceControlNotActivated() {
277 | $voiceControlActivateCta.disabled = false;
278 | $voiceControlActivateCta.title = '';
279 |
280 | boolIsOnVoiceControlDeactivationListenerSet = false;
281 | }
282 |
283 | pozitoneModule.sdk.getVoiceControlStatus( function( objStatus ) {
284 | var boolIsConnected = objStatus.boolIsConnected;
285 |
286 | if ( typeof boolIsConnected === 'boolean' && boolIsConnected ) {
287 | onVoiceControlAlreadyActivated();
288 | }
289 | } );
290 |
291 | /**
292 | * @todo onVoiceControlNotActivated() on disconnect
293 | */
294 | } );
295 |
--------------------------------------------------------------------------------
/static/options/js/options.js:
--------------------------------------------------------------------------------
1 | /* =============================================================================
2 |
3 | Product : PoziTone
4 | Author : PoziWorld
5 | Copyright : Copyright (c) 2013-2016 PoziWorld
6 | License : pozitone.com/license
7 | File : options/js/options.js
8 | Description : Options JavaScript
9 |
10 | Table of Contents:
11 |
12 | Globals
13 | Options
14 | init()
15 | removeNotAvailable()
16 | setPageValues()
17 | onSettingChange()
18 | addEventListeners()
19 | removeModuleNotifications()
20 | initEeLauncher()
21 | Listeners
22 | runtime.onMessage
23 | Events
24 |
25 | ============================================================================ */
26 |
27 | /* =============================================================================
28 |
29 | Globals
30 |
31 | ============================================================================ */
32 |
33 | const
34 | strPage = 'options'
35 | , strNotAvailableOperaSettingsClass = 'moduleAvailableNotificationButtons'
36 | , strModuleLocalPrefix = 'module_'
37 | , strSettingsId = 'settings'
38 | , strSettingsSavedId = 'settingsSaved'
39 | , strModuleSubpageId = 'settings_module'
40 | , strModuleSubpageIdPrefix = 'settings_'
41 | , strSettingsSubpageClass = 'settingsSubpage'
42 | , strMenuItemSelectedClass = 'selected'
43 | , strEnableModule = 'boolIsEnabled'
44 |
45 | , strEeLauncherKeyword = 'help'
46 | , strEeLauncherId = 'helpMenuItem'
47 | , strHelpInfoToSubmitId = 'helpInfoToSubmit'
48 | , strHelpSubmitInfoCtaId = 'helpSubmitInfoCta'
49 | ;
50 |
51 | var
52 | objParams = {}
53 | , strSubpage
54 | , strSubsection
55 |
56 | , $allInputs // All
57 | , intInputs // Num of $allInputs
58 | , $settingsSaved
59 | , $settingsSubpages
60 |
61 | , strChosenSettingsSubpage
62 | , intSettingsSubpages
63 | ;
64 |
65 | /* =============================================================================
66 |
67 | Options
68 |
69 | ============================================================================ */
70 |
71 | var Options = {
72 |
73 | /**
74 | * Initialize
75 | *
76 | * @type method
77 | * @param No Parameters Taken
78 | * @return void
79 | **/
80 | init : function() {
81 | poziworldExtension.i18n.init()
82 | .then( Page.localize.bind( null, strPage ) );
83 | Options.setPageValues();
84 | Options.addEventListeners();
85 | Options.initEeLauncher();
86 | }
87 | ,
88 |
89 | /**
90 | * If some settings n/a for this browser, remove them
91 | *
92 | * @type method
93 | * @param No Parameters Taken
94 | * @return void
95 | **/
96 | removeNotAvailable : function() {
97 | if ( bowser.name === 'Opera' ) {
98 | var $elements =
99 | document
100 | .getElementsByClassName( strNotAvailableOperaSettingsClass );
101 |
102 | for ( var i = ( $elements.length - 1 ); i >= 0; i-- ) {
103 | var $element = $elements[i];
104 |
105 | $element.parentNode.removeChild( $element );
106 | }
107 | }
108 | }
109 | ,
110 |
111 | /**
112 | * Set values when DOM is ready
113 | *
114 | * @type method
115 | * @param No Parameters Taken
116 | * @return void
117 | **/
118 | setPageValues : function() {
119 | $allInputs = document.querySelectorAll( '.subpage input' );
120 | intInputs = $allInputs.length;
121 |
122 | $settingsSaved = document.getElementById( strSettingsSavedId );
123 | $settingsSubpages = document.getElementsByClassName( strSettingsSubpageClass );
124 | intSettingsSubpages = $settingsSubpages.length;
125 | }
126 | ,
127 |
128 | /**
129 | * Assign change listeners for settings
130 | *
131 | * @type method
132 | * @param objEvent
133 | * Event object
134 | * @param strModuleOverride
135 | * When settings get saved, they use strChosenSettingsSubpage.
136 | * This one lets override strChosenSettingsSubpage.
137 | * @return void
138 | **/
139 | onSettingChange : function( objEvent, strModuleOverride ) {
140 | var $this = objEvent.target
141 | , objTemp = {}
142 | , objModuleSettings = {}
143 | , miscSetting
144 | , strModule = strChosenSettingsSubpage
145 | , strModuleSettings
146 | ;
147 |
148 | if ( typeof strModuleOverride === 'string' ) {
149 | strModule = strModuleOverride;
150 | }
151 |
152 | var boolIsExternal = strModule !== 'general' && pozitone.global.isModuleExternal( strModule )
153 | , StorageTemp = boolIsExternal ? StorageLocal : StorageSync
154 | ;
155 |
156 | if ( $this.type === 'checkbox' && $this.value === 'on' ) {
157 | var boolIsChecked = $this.checked;
158 |
159 | miscSetting = boolIsChecked;
160 |
161 | if ( $this.name === strEnableModule && ! boolIsChecked ) {
162 | Options.removeModuleNotifications( strModule );
163 | }
164 | }
165 | else if ( $this.type === 'checkbox' && $this.value !== 'on' ) {
166 | var $moduleSubpage = document.getElementById( strModuleSubpageId )
167 | , $group = $moduleSubpage.querySelectorAll( 'input[name="' + $this.name + '"]' )
168 | , arrTemp = []
169 | ;
170 |
171 | for ( var i = 0, l = $group.length; i < l; i++ ) {
172 | var $groupEl = $group[ i ];
173 |
174 | if ( $groupEl.checked ) {
175 | arrTemp.push( $groupEl.value );
176 | }
177 | }
178 |
179 | miscSetting = arrTemp;
180 | }
181 | else if ( $this.type === 'radio' || $this.tagName === 'SELECT' ) {
182 | miscSetting = $this.value;
183 | }
184 | else if ( $this.type === 'number' ) {
185 | miscSetting = parseInt( $this.value );
186 | }
187 |
188 | if ( typeof miscSetting === 'undefined' ) {
189 | return;
190 | }
191 |
192 | strModuleSettings = strConstSettingsPrefix + strModule;
193 |
194 | // TODO: Is there a need for objTemp?
195 | objTemp[ strModuleSettings ] = {};
196 | objTemp[ strModuleSettings ][ $this.name ] = miscSetting;
197 | objModuleSettings[ $this.name ] = miscSetting;
198 |
199 | if ( ! Global.isEmpty( objTemp ) ) {
200 | StorageTemp.get( strModuleSettings, function( objReturn ) {
201 | for ( var strKey in objModuleSettings ) {
202 | if ( objModuleSettings.hasOwnProperty( strKey ) )
203 | objReturn[ strModuleSettings ][ strKey ] =
204 | objModuleSettings[ strKey ];
205 | }
206 |
207 | // TODO: Add callback to Global.setStorageItems() and then utilize it
208 | StorageTemp.set( objReturn, function() {
209 | Page.showSuccess( $settingsSaved );
210 |
211 | // Debug
212 | StorageTemp.get( null, function(data) {
213 | console.log(data);
214 | } );
215 | } );
216 | } );
217 | }
218 | }
219 | ,
220 |
221 | /**
222 | * Add event listeners
223 | *
224 | * @type method
225 | * @param No Parameters Taken
226 | * @return void
227 | **/
228 | addEventListeners : function() {
229 | addEvent(
230 | $allInputs
231 | , 'change'
232 | , function( objEvent ) { Options.onSettingChange( objEvent ); }
233 | );
234 |
235 | addEvent(
236 | document.getElementById( strHelpSubmitInfoCtaId )
237 | , 'click'
238 | , function( objEvent ) {
239 | var $element = objEvent.target;
240 |
241 | $element.disabled = true;
242 | Log.setPropertiesOnUserRecord(
243 | objConstUserSetUp
244 | , function() {
245 | $element.innerText =
246 | poziworldExtension.i18n.getMessage( 'optionsHelpSubmitInfoCtaSuccess' );
247 | }
248 | );
249 |
250 | }
251 | );
252 | }
253 | ,
254 |
255 | /**
256 | * Remove all notifications for a module when just disabled it
257 | *
258 | * @type method
259 | * @param strModule
260 | * Remove notifications of this module
261 | * @return void
262 | **/
263 | removeModuleNotifications : function( strModule ) {
264 | StorageLocal.get( 'arrTabsIds', function( objData ) {
265 | var arrTabsIds = objData.arrTabsIds;
266 |
267 | if ( typeof arrTabsIds === 'undefined' )
268 | return;
269 |
270 | for ( var i = 0, l = arrTabsIds.length; i < l; i++ ) {
271 | var arrTabId = arrTabsIds[ i ];
272 |
273 | if ( arrTabId[ 1 ] === strModule )
274 | Global.removeNotification( arrTabId[ 0 ], strModule );
275 | }
276 | });
277 | }
278 | ,
279 |
280 | /**
281 | * Initialize E.E. launcher (waits for a command)
282 | *
283 | * @type method
284 | * @param No Parameters Taken
285 | * @return void
286 | **/
287 | initEeLauncher : function() {
288 | // http://stackoverflow.com/a/18272907
289 | let strInput = '';
290 |
291 | window.addEventListener( 'keypress', function( objEvent ) {
292 | var c = String.fromCharCode( objEvent.keyCode );
293 |
294 | strInput += c.toLowerCase();
295 |
296 | if ( ~ strInput.indexOf( strEeLauncherKeyword ) ) {
297 | strInput = '';
298 |
299 | document.getElementById( strEeLauncherId ).click();
300 | }
301 | else if ( ~ strInput.indexOf( window.atob( 'bGlh' ) ) ) {
302 | strInput = '';
303 |
304 | location.href='#/❤';
305 | }
306 | } );
307 | }
308 | };
309 |
310 | /* =============================================================================
311 |
312 | Listeners
313 |
314 | ============================================================================ */
315 |
316 | /**
317 | * Listens for messages from other pages
318 | *
319 | * @type method
320 | * @param objMessage
321 | * Message received
322 | * @param objSender
323 | * Sender of the message
324 | * @return void
325 | **/
326 | chrome.runtime.onMessage.addListener(
327 | function( objMessage, objSender, funcSendResponse ) {
328 | if ( objMessage.strReceiver === 'options' ) {
329 | var objVars = objMessage[ 'objVars' ];
330 |
331 | for ( strProp in objVars ) {
332 | if ( objVars.hasOwnProperty( strProp ) )
333 | document.querySelector( '[name="' + strProp + '"]' ).checked =
334 | objVars[ strProp ];
335 | }
336 | }
337 | }
338 | );
339 |
340 | /* =============================================================================
341 |
342 | Events
343 |
344 | ============================================================================ */
345 |
346 | document.addEventListener( 'DOMContentLoaded', Options.init );
347 |
--------------------------------------------------------------------------------
/static/options/partials/about.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
20 |
21 |
22 |
23 | ()
28 |
29 |
30 |
31 |
32 |
38 |
39 |
--------------------------------------------------------------------------------
/static/options/partials/contribution.html:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/static/options/partials/external-modules-list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
14 |
67 |
71 |
75 |
76 |
--------------------------------------------------------------------------------
/static/options/partials/feedback.html:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/static/options/partials/help.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
16 |
20 |
21 |
--------------------------------------------------------------------------------
/static/options/partials/projects.html:
--------------------------------------------------------------------------------
1 |
42 |
--------------------------------------------------------------------------------
/static/options/partials/settings-general.html:
--------------------------------------------------------------------------------
1 |
2 |
156 |
--------------------------------------------------------------------------------
/static/options/partials/settings-modules-list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
14 |
114 |
118 |
122 |
123 |
138 |
--------------------------------------------------------------------------------
/static/options/partials/settings.html:
--------------------------------------------------------------------------------
1 |
51 |
--------------------------------------------------------------------------------
/static/options/partials/voice-control.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
19 |
46 |
128 |
129 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require( 'path' );
2 | const { List, Map } = require( 'immutable' );
3 | const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
4 | const CleanWebpackPlugin = require( 'clean-webpack-plugin' );
5 | const WebpackCleanPlugin = require( 'webpack-clean' );
6 |
7 | const modeDevelopment = process.env.NODE_ENV === 'development';
8 |
9 | const defaultConfig = Map( {
10 | entry: {
11 | 'manifest': './static/manifest.json',
12 | },
13 | output: Map( {
14 | filename: '[name].js',
15 | chunkFilename: '[name].js',
16 | path: path.resolve( __dirname, 'dist' ),
17 | } ),
18 | module: {
19 | rules: [
20 | {
21 | test: /manifest.json$/,
22 | exclude: /node_modules/,
23 | loader: 'manifest-loader',
24 | },
25 | ],
26 | },
27 | plugins: List( [
28 | new CleanWebpackPlugin(
29 | [
30 | 'dist',
31 | ]
32 | ),
33 |
34 | new CopyWebpackPlugin(
35 | [
36 | {
37 | from: './static',
38 | to: './',
39 | },
40 | ]
41 | ),
42 | ] ),
43 | resolveLoader: {
44 | modules: [
45 | path.resolve( __dirname, 'src', 'loaders' ),
46 | 'node_modules',
47 | ],
48 | },
49 | devtool: modeDevelopment ?
50 | 'inline-cheap-module-source-map' :
51 | false,
52 | watch: modeDevelopment,
53 | } );
54 |
55 | const supportedBrowsers = [
56 | 'chromium',
57 | // @todo Add support for Firefox.
58 | // 'firefox',
59 | ];
60 |
61 | module.exports = supportedBrowsers.map( browserName => {
62 | return defaultConfig
63 | // Create a separate dist folder for each browser
64 | .updateIn(
65 | [
66 | 'output',
67 | 'path',
68 | ],
69 | () => path.resolve( __dirname, 'dist', browserName ),
70 | )
71 | // Remove unused automatically-created JavaScript files post-build
72 | .updateIn(
73 | [
74 | 'plugins',
75 | ],
76 | value => value.push(
77 | new WebpackCleanPlugin(
78 | [
79 | 'manifest.js',
80 | ],
81 | {
82 | basePath: path.resolve( __dirname, 'dist', browserName ),
83 | },
84 | ),
85 | ),
86 | )
87 | .toJS()
88 | ;
89 | } );
90 |
--------------------------------------------------------------------------------