mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-13 11:00:01 +00:00
Change message format + refactor options
This commit is contained in:
@@ -1,97 +1,97 @@
|
|||||||
{
|
{
|
||||||
"extension_name": {
|
"extensionName": {
|
||||||
"message": "EXTENSION_NAME"
|
"message": "EXTENSION_NAME"
|
||||||
}
|
}
|
||||||
, "extension_description": {
|
, "extensionDescription": {
|
||||||
"message": ""
|
"message": ""
|
||||||
}
|
}
|
||||||
|
|
||||||
, "popup_cast_button_label": {
|
, "popupCastButtonLabel": {
|
||||||
"message": "Cast"
|
"message": "Cast"
|
||||||
}
|
}
|
||||||
, "popup_casting_button_label": {
|
, "popupCastingButtonLabel": {
|
||||||
"message": "Casting"
|
"message": "Casting"
|
||||||
}
|
}
|
||||||
|
|
||||||
, "context_media_cast": {
|
, "contextCast": {
|
||||||
"message": "Cast..."
|
"message": "Cast..."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
, "options_category_media": {
|
, "optionsMediaCategoryName": {
|
||||||
"message": "Media casting"
|
"message": "Media casting"
|
||||||
}
|
}
|
||||||
, "options_category_media_description": {
|
, "optionsMediaCategoryDescription": {
|
||||||
"message": "HTML5 video/audio media casting."
|
"message": "HTML5 video/audio media casting."
|
||||||
}
|
}
|
||||||
, "options_option_mediaEnabled": {
|
, "optionsMediaEnabled": {
|
||||||
"message": "Enabled"
|
"message": "Enabled"
|
||||||
}
|
}
|
||||||
|
|
||||||
, "options_category_localMedia": {
|
, "optionsLocalMediaCategoryName": {
|
||||||
"message": "Local media casting"
|
"message": "Local media casting"
|
||||||
}
|
}
|
||||||
, "options_category_localMedia_description": {
|
, "optionsLocalMediaCategoryDescription": {
|
||||||
"message": "HTTP server started by the bridge app to stream local media files to the cast receiver."
|
"message": "HTTP server started by the bridge app to stream local media files to the cast receiver."
|
||||||
}
|
}
|
||||||
, "options_option_localMediaEnabled": {
|
, "optionsLocalMediaEnabled": {
|
||||||
"message": "Enabled"
|
"message": "Enabled"
|
||||||
}
|
}
|
||||||
, "options_option_localMediaServerPort": {
|
, "optionsLocalMediaServerPort": {
|
||||||
"message": "HTTP server port"
|
"message": "HTTP server port"
|
||||||
}
|
}
|
||||||
|
|
||||||
, "options_category_uaWhitelist": {
|
, "optionsUserAgentWhitelistCategoryName": {
|
||||||
"message": "User agent whitelist"
|
"message": "User agent whitelist"
|
||||||
}
|
}
|
||||||
, "options_category_uaWhitelist_description": {
|
, "optionsUserAgentWhitelistCategoryDescription": {
|
||||||
"message": "Sites for which to replace the user agent with a Chrome version for compatibility. Must be valid match patterns."
|
"message": "Sites for which to replace the user agent with a Chrome version for compatibility. Must be valid match patterns."
|
||||||
}
|
}
|
||||||
, "options_option_uaWhitelistEnabled": {
|
, "optionsUserAgentWhitelistEnabled": {
|
||||||
"message": "Enabled"
|
"message": "Enabled"
|
||||||
}
|
}
|
||||||
, "options_option_uaWhitelist": {
|
, "optionsUserAgentWhitelistContent": {
|
||||||
"message": "Match patterns (newline-separated)"
|
"message": "Match patterns (newline-separated)"
|
||||||
}
|
}
|
||||||
, "options_option_uaWhitelistBasicView": {
|
, "optionsUserAgentWhitelistBasicView": {
|
||||||
"message": "Basic View"
|
"message": "Basic View"
|
||||||
}
|
}
|
||||||
, "options_option_uaWhitelistRawView": {
|
, "optionsUserAgentWhitelistRawView": {
|
||||||
"message": "Raw View"
|
"message": "Raw View"
|
||||||
}
|
}
|
||||||
, "options_option_uaWhitelistSaveRaw": {
|
, "optionsUserAgentWhitelistSaveRaw": {
|
||||||
"message": "Save Raw"
|
"message": "Save Raw"
|
||||||
}
|
}
|
||||||
, "options_option_uaWhitelistAddItem": {
|
, "optionsUserAgentWhitelistAddItem": {
|
||||||
"message": "Add Item"
|
"message": "Add Item"
|
||||||
}
|
}
|
||||||
, "options_option_uaWhitelistItemEdit": {
|
, "optionsUserAgentWhitelistEditItem": {
|
||||||
"message": "Edit"
|
"message": "Edit"
|
||||||
}
|
}
|
||||||
, "options_option_uaWhitelistItemRemove": {
|
, "optionsUserAgentWhitelistRemoveItem": {
|
||||||
"message": "Remove"
|
"message": "Remove"
|
||||||
}
|
}
|
||||||
, "options_option_uaWhitelistInvalidMatchPattern": {
|
, "optionsUserAgentWhitelistInvalidMatchPattern": {
|
||||||
"message": "Invalid match pattern $1"
|
"message": "Invalid match pattern $1"
|
||||||
}
|
}
|
||||||
|
|
||||||
, "options_category_mirroring": {
|
, "optionsMirroringCategoryName": {
|
||||||
"message": "Screen mirroring"
|
"message": "Screen mirroring"
|
||||||
}
|
}
|
||||||
, "options_category_mirroring_description": {
|
, "optionsMirroringCategoryDescription": {
|
||||||
"message": "Screen/Tab mirroring to a Chromecast receiver app."
|
"message": "Screen/Tab mirroring to a Chromecast receiver app."
|
||||||
}
|
}
|
||||||
, "options_option_mirroringEnabled": {
|
, "optionsMirroringEnabled": {
|
||||||
"message": "Enabled"
|
"message": "Enabled"
|
||||||
}
|
}
|
||||||
, "options_option_mirroringAppId": {
|
, "optionsMirroringAppId": {
|
||||||
"message": "Receiver app ID"
|
"message": "Receiver app ID"
|
||||||
}
|
}
|
||||||
|
|
||||||
, "options_reset": {
|
, "optionsReset": {
|
||||||
"message": "Reset to defaults"
|
"message": "Reset to defaults"
|
||||||
}
|
}
|
||||||
, "options_submit": {
|
, "optionsSubmit": {
|
||||||
"message": "Submit"
|
"message": "Submit"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,20 +10,23 @@ browser.runtime.onInstalled.addListener(async details => {
|
|||||||
switch (details.reason) {
|
switch (details.reason) {
|
||||||
|
|
||||||
// Set default options
|
// Set default options
|
||||||
case "install":
|
case "install": {
|
||||||
await browser.storage.sync.set({
|
await browser.storage.sync.set({
|
||||||
options: defaultOptions
|
options: defaultOptions
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
// Set newly added options
|
// Set newly added options
|
||||||
case "update":
|
case "update": {
|
||||||
const { options } = await browser.storage.sync.get("options");
|
const { options: existingOptions }
|
||||||
|
= await browser.storage.sync.get("options");
|
||||||
|
|
||||||
const newOptions = {};
|
const newOptions = {};
|
||||||
|
|
||||||
// Find options not already in storage
|
// Find options not already in storage
|
||||||
for (const [ key, val ] of Object.entries(defaultOptions)) {
|
for (const [ key, val ] of Object.entries(defaultOptions)) {
|
||||||
if (!options.hasOwnProperty(key)) {
|
if (!existingOptions.hasOwnProperty(key)) {
|
||||||
newOptions[key] = val;
|
newOptions[key] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,12 +34,13 @@ browser.runtime.onInstalled.addListener(async details => {
|
|||||||
// Update storage with default values of new options
|
// Update storage with default values of new options
|
||||||
await browser.storage.sync.set({
|
await browser.storage.sync.set({
|
||||||
options: {
|
options: {
|
||||||
...options
|
...existingOptions
|
||||||
, ...newOptions
|
, ...newOptions
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call after default options have been set
|
// Call after default options have been set
|
||||||
@@ -64,7 +68,7 @@ async function createMenus () {
|
|||||||
*/
|
*/
|
||||||
if (!options || mirrorCastMenuId || mediaCastMenuId) return;
|
if (!options || mirrorCastMenuId || mediaCastMenuId) return;
|
||||||
|
|
||||||
if (options.option_localMediaEnabled) {
|
if (options.localMediaEnabled) {
|
||||||
mediaCastTargetUrlPatterns.add(LOCAL_MEDIA_URL_PATTERN);
|
mediaCastTargetUrlPatterns.add(LOCAL_MEDIA_URL_PATTERN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,16 +77,16 @@ async function createMenus () {
|
|||||||
contexts: [ "audio", "video" ]
|
contexts: [ "audio", "video" ]
|
||||||
, id: "contextCastMedia"
|
, id: "contextCastMedia"
|
||||||
, targetUrlPatterns: Array.from(mediaCastTargetUrlPatterns)
|
, targetUrlPatterns: Array.from(mediaCastTargetUrlPatterns)
|
||||||
, title: _("context_media_cast")
|
, title: _("contextCast")
|
||||||
, visible: options.option_mediaEnabled
|
, visible: options.mediaEnabled
|
||||||
});
|
});
|
||||||
|
|
||||||
// Screen/Tab mirroring "Cast..." context menu item
|
// Screen/Tab mirroring "Cast..." context menu item
|
||||||
mirrorCastMenuId = await browser.menus.create({
|
mirrorCastMenuId = await browser.menus.create({
|
||||||
contexts: [ "browser_action", "page" ]
|
contexts: [ "browser_action", "page" ]
|
||||||
, id: "contextCast"
|
, id: "contextCast"
|
||||||
, title: _("context_media_cast")
|
, title: _("contextCast")
|
||||||
, visible: options.option_mirroringEnabled
|
, visible: options.mirroringEnabled
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,8 +194,8 @@ async function onOptionsUpdated (alteredOptions) {
|
|||||||
onBeforeSendHeaders () {
|
onBeforeSendHeaders () {
|
||||||
browser.webRequest.onBeforeSendHeaders.addListener(
|
browser.webRequest.onBeforeSendHeaders.addListener(
|
||||||
onBeforeSendHeaders
|
onBeforeSendHeaders
|
||||||
, { urls: options.option_uaWhitelistEnabled
|
, { urls: options.userAgentWhitelistEnabled
|
||||||
? options.option_uaWhitelist
|
? options.userAgentWhitelist
|
||||||
: [] }
|
: [] }
|
||||||
, [ "blocking", "requestHeaders" ]);
|
, [ "blocking", "requestHeaders" ]);
|
||||||
}
|
}
|
||||||
@@ -203,26 +207,26 @@ async function onOptionsUpdated (alteredOptions) {
|
|||||||
func();
|
func();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (alteredOptions.includes("option_uaWhitelist")
|
if (alteredOptions.includes("userAgentWhitelist")
|
||||||
|| alteredOptions.includes("option_uaWhitelistEnabled")) {
|
|| alteredOptions.includes("userAgentWhitelistEnabled")) {
|
||||||
browser.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
|
browser.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
|
||||||
registerFunctions.onBeforeSendHeaders();
|
registerFunctions.onBeforeSendHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alteredOptions.includes("option_mirroringEnabled")) {
|
if (alteredOptions.includes("mirroringEnabled")) {
|
||||||
browser.menus.update(mirrorCastMenuId, {
|
browser.menus.update(mirrorCastMenuId, {
|
||||||
visible: options.option_mirroringEnabled
|
visible: options.mirroringEnabled
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alteredOptions.includes("option_mediaEnabled")) {
|
if (alteredOptions.includes("mediaEnabled")) {
|
||||||
browser.menus.update(mediaCastMenuId, {
|
browser.menus.update(mediaCastMenuId, {
|
||||||
visible: options.option_mediaEnabled
|
visible: options.mediaEnabled
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alteredOptions.includes("option_localMediaEnabled")) {
|
if (alteredOptions.includes("localMediaEnabled")) {
|
||||||
if (options.option_localMediaEnabled) {
|
if (options.localMediaEnabled) {
|
||||||
mediaCastTargetUrlPatterns.add(LOCAL_MEDIA_URL_PATTERN);
|
mediaCastTargetUrlPatterns.add(LOCAL_MEDIA_URL_PATTERN);
|
||||||
} else {
|
} else {
|
||||||
mediaCastTargetUrlPatterns.delete(LOCAL_MEDIA_URL_PATTERN);
|
mediaCastTargetUrlPatterns.delete(LOCAL_MEDIA_URL_PATTERN);
|
||||||
@@ -276,7 +280,7 @@ browser.menus.onClicked.addListener(async (info, tab) => {
|
|||||||
|
|
||||||
await browser.tabs.executeScript(tab.id, {
|
await browser.tabs.executeScript(tab.id, {
|
||||||
code: `let selectedMedia = "${info.pageUrl ? "tab" : "screen"}";
|
code: `let selectedMedia = "${info.pageUrl ? "tab" : "screen"}";
|
||||||
let FX_CAST_RECEIVER_APP_ID = "${options.option_mirroringAppId}";`
|
let FX_CAST_RECEIVER_APP_ID = "${options.mirroringEnabled}";`
|
||||||
, frameId
|
, frameId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "__MSG_extension_name__"
|
"name": "__MSG_extensionName__"
|
||||||
, "description": "__MSG_extension_description__"
|
, "description": "__MSG_extensionDescription__"
|
||||||
, "version": "EXTENSION_VERSION"
|
, "version": "EXTENSION_VERSION"
|
||||||
|
|
||||||
, "applications": {
|
, "applications": {
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ async function onRequestSessionSuccess (session_) {
|
|||||||
session = session_;
|
session = session_;
|
||||||
|
|
||||||
let mediaUrl = new URL(srcUrl);
|
let mediaUrl = new URL(srcUrl);
|
||||||
const port = options.option_localMediaServerPort;
|
const port = options.localMediaServerPort;
|
||||||
|
|
||||||
if (isLocalFile) {
|
if (isLocalFile) {
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
@@ -295,7 +295,7 @@ window.__onGCastApiAvailable = async function (loaded, errorInfo) {
|
|||||||
|
|
||||||
options = (await browser.storage.sync.get("options")).options;
|
options = (await browser.storage.sync.get("options")).options;
|
||||||
|
|
||||||
if (isLocalFile && !options.option_localMediaEnabled) {
|
if (isLocalFile && !options.localMediaEnabled) {
|
||||||
logMessage("Local media casting not enabled");
|
logMessage("Local media casting not enabled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
175
ext/src/options/EditableList.jsx
Normal file
175
ext/src/options/EditableList.jsx
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import EditableListItem from "./EditableListItem";
|
||||||
|
|
||||||
|
const _ = browser.i18n.getMessage;
|
||||||
|
|
||||||
|
|
||||||
|
export default class EditableList extends Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
items: new Set(this.props.data)
|
||||||
|
, addingNewItem: false
|
||||||
|
, rawView: false
|
||||||
|
, rawViewValue: ""
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handleItemRemove = this.handleItemRemove.bind(this);
|
||||||
|
this.handleItemEdit = this.handleItemEdit.bind(this);
|
||||||
|
this.handleSwitchView = this.handleSwitchView.bind(this);
|
||||||
|
this.handleSaveRaw = this.handleSaveRaw.bind(this);
|
||||||
|
this.handleRawViewTextAreaChange = this.handleRawViewTextAreaChange.bind(this);
|
||||||
|
this.handleAddItem = this.handleAddItem.bind(this);
|
||||||
|
this.handleNewItemRemove = this.handleNewItemRemove.bind(this);
|
||||||
|
this.handleNewItemEdit = this.handleNewItemEdit.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleItemRemove (item) {
|
||||||
|
this.setState(currentState => {
|
||||||
|
const newItems = new Set(currentState.items);
|
||||||
|
newItems.delete(item);
|
||||||
|
return {
|
||||||
|
items: newItems
|
||||||
|
};
|
||||||
|
}, () => {
|
||||||
|
this.props.onChange(Array.from(this.state.items));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleItemEdit (item, newValue) {
|
||||||
|
this.setState(currentState => ({
|
||||||
|
items: new Set([...currentState.items]
|
||||||
|
.map(item_ => item_ === item ? newValue : item_))
|
||||||
|
}), () => {
|
||||||
|
this.props.onChange(Array.from(this.state.items));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSwitchView () {
|
||||||
|
this.setState(currentState => {
|
||||||
|
if (currentState.rawView) {
|
||||||
|
return {
|
||||||
|
rawView: false
|
||||||
|
, rawViewValue: ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
rawView: true
|
||||||
|
, rawViewValue: [...currentState.items.values()].join("\n")
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSaveRaw () {
|
||||||
|
this.setState(currentState => {
|
||||||
|
console.log(currentState.rawViewValue);
|
||||||
|
const newItems = currentState.rawViewValue.split("\n")
|
||||||
|
.filter(item => item !== "");
|
||||||
|
|
||||||
|
if ("itemPattern" in this.props) {
|
||||||
|
for (const item of newItems) {
|
||||||
|
if (!this.props.itemPattern.test(item)) {
|
||||||
|
this.rawViewTextArea.setCustomValidity(
|
||||||
|
this.props.itemPatternError(item));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.rawViewTextArea.setCustomValidity("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: new Set(newItems)
|
||||||
|
};
|
||||||
|
}, () => {
|
||||||
|
this.props.onChange(Array.from(this.state.items));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRawViewTextAreaChange (ev) {
|
||||||
|
if (this.rawViewTextArea.scrollHeight > this.rawViewTextArea.clientHeight) {
|
||||||
|
this.rawViewTextArea.style.height = `${this.rawViewTextArea.scrollHeight}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
rawViewValue: ev.target.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAddItem () {
|
||||||
|
this.setState({
|
||||||
|
addingNewItem: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNewItemRemove () {
|
||||||
|
this.setState({
|
||||||
|
addingNewItem: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNewItemEdit (item, newItem) {
|
||||||
|
this.setState(currentState => ({
|
||||||
|
items: [ ...currentState.items, newItem ]
|
||||||
|
, addingNewItem: false
|
||||||
|
}), () => {
|
||||||
|
this.props.onChange(Array.from(this.state.items));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const items = Array.from(this.state.items.values());
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="editable-list">
|
||||||
|
<button className="editable-list__view-button"
|
||||||
|
onClick={ this.handleSwitchView }>
|
||||||
|
{ this.state.rawView
|
||||||
|
? _("optionsUserAgentWhitelistBasicView")
|
||||||
|
: _("optionsUserAgentWhitelistRawView") }
|
||||||
|
</button>
|
||||||
|
{ this.state.rawView &&
|
||||||
|
<button className="editable-list__save-raw-button"
|
||||||
|
onClick={ this.handleSaveRaw }>
|
||||||
|
{ _("optionsUserAgentWhitelistSaveRaw") }
|
||||||
|
</button> }
|
||||||
|
<hr />
|
||||||
|
{ do {
|
||||||
|
if (this.state.rawView) {
|
||||||
|
<textarea className="editable-list__raw-view"
|
||||||
|
rows={ items.length}
|
||||||
|
value={ this.state.rawViewValue}
|
||||||
|
onChange={ this.handleRawViewTextAreaChange }
|
||||||
|
ref={ el => { this.rawViewTextArea = el }}>
|
||||||
|
</textarea>
|
||||||
|
} else {
|
||||||
|
<ul className="editable-list__items">
|
||||||
|
{ items.map((item, i) =>
|
||||||
|
<EditableListItem text={ item }
|
||||||
|
itemPattern={ this.props.itemPattern }
|
||||||
|
itemPatternError={ this.props.itemPatternError }
|
||||||
|
onRemove={ this.handleItemRemove }
|
||||||
|
onEdit={ this.handleItemEdit }
|
||||||
|
key={ i } /> )}
|
||||||
|
{ this.state.addingNewItem &&
|
||||||
|
<EditableListItem text=""
|
||||||
|
itemPattern={ this.props.itemPattern }
|
||||||
|
itemPatternError={ this.props.itemPatternError }
|
||||||
|
onRemove={ this.handleNewItemRemove }
|
||||||
|
onEdit={ this.handleNewItemEdit }
|
||||||
|
editing={ true } /> }
|
||||||
|
|
||||||
|
<div className="editable-list__item editable-list__item-actions">
|
||||||
|
<button className="editable-list__add-button"
|
||||||
|
onClick={ this.handleAddItem }>
|
||||||
|
{ _("optionsUserAgentWhitelistAddItem") }
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
91
ext/src/options/EditableListItem.jsx
Normal file
91
ext/src/options/EditableListItem.jsx
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
|
||||||
|
const _ = browser.i18n.getMessage;
|
||||||
|
|
||||||
|
|
||||||
|
export default class EditableListItem extends Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
editing: this.props.editing || false
|
||||||
|
, editValue: ""
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handleRemove = this.handleRemove.bind(this);
|
||||||
|
this.handleEditBegin = this.handleEditBegin.bind(this);
|
||||||
|
this.handleEditEnd = this.handleEditEnd.bind(this);
|
||||||
|
this.handleInputChange = this.handleInputChange.bind(this);
|
||||||
|
this.handleInputKeyPress = this.handleInputKeyPress.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRemove () {
|
||||||
|
this.props.onRemove(this.props.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEditBegin () {
|
||||||
|
this.setState({
|
||||||
|
editing: true
|
||||||
|
, editValue: this.props.text
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEditEnd (ev) {
|
||||||
|
if (this.props.editing
|
||||||
|
&& !this.props.itemPattern.test(this.state.editValue)) {
|
||||||
|
ev.target.setCustomValidity(this.props.itemPatternError());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ev.target.validity.valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.onEdit(this.props.text, this.state.editValue);
|
||||||
|
this.setState({
|
||||||
|
editing: false
|
||||||
|
, editValue: ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInputChange (ev) {
|
||||||
|
this.setState({
|
||||||
|
editValue: ev.target.value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this.props.itemPattern.test(ev.target.value)) {
|
||||||
|
ev.target.setCustomValidity(this.props.itemPatternError());
|
||||||
|
} else {
|
||||||
|
ev.target.setCustomValidity("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInputKeyPress (ev) {
|
||||||
|
if (ev.key === "Enter") {
|
||||||
|
this.handleEditEnd({ target: ev.target });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<li className="editable-list__item">
|
||||||
|
<div className="editable-list__title"
|
||||||
|
onDoubleClick={ this.handleEditBegin }>
|
||||||
|
{ this.state.editing
|
||||||
|
? <input className="editable-list__edit-field"
|
||||||
|
type="text"
|
||||||
|
autoFocus
|
||||||
|
value={ this.state.editValue }
|
||||||
|
onBlur={ this.handleEditEnd }
|
||||||
|
onChange={ this.handleInputChange }
|
||||||
|
onKeyPress={ this.handleInputKeyPress }/>
|
||||||
|
: this.props.text }
|
||||||
|
</div>
|
||||||
|
<button onClick={ this.handleEditBegin }>
|
||||||
|
{ _("optionsUserAgentWhitelistEditItem") }
|
||||||
|
</button>
|
||||||
|
<button onClick={ this.handleRemove }>
|
||||||
|
{ _("optionsUserAgentWhitelistRemoveItem") }
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
export default {
|
export default {
|
||||||
option_mediaEnabled: true
|
mediaEnabled: true
|
||||||
, option_localMediaEnabled: true
|
, localMediaEnabled: true
|
||||||
, option_localMediaServerPort: 9555
|
, localMediaServerPort: 9555
|
||||||
, option_uaWhitelistEnabled: true
|
, mirroringEnabled: false
|
||||||
, option_uaWhitelist: [
|
, mirroringAppId: MIRRORING_APP_ID
|
||||||
|
, userAgentWhitelistEnabled: true
|
||||||
|
, userAgentWhitelist: [
|
||||||
"https://www.netflix.com/*"
|
"https://www.netflix.com/*"
|
||||||
]
|
]
|
||||||
, option_mirroringEnabled: false
|
|
||||||
, option_mirroringAppId: MIRRORING_APP_ID
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
grid-column-gap: 20px;
|
grid-column-gap: 20px;
|
||||||
grid-row-gap: 5px;
|
grid-row-gap: 5px;
|
||||||
}
|
}
|
||||||
.category-name {}
|
.category__name {}
|
||||||
.category-description,
|
.category__description,
|
||||||
.category .category {
|
.category .category {
|
||||||
color: graytext;
|
color: graytext;
|
||||||
grid-column: span 2;
|
grid-column: span 2;
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
.option-label {
|
.option__label {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
|
|
||||||
import defaultOptions from "./defaultOptions";
|
import defaultOptions from "./defaultOptions";
|
||||||
|
import EditableList from "./EditableList";
|
||||||
|
|
||||||
|
|
||||||
const _ = browser.i18n.getMessage;
|
const _ = browser.i18n.getMessage;
|
||||||
@@ -11,266 +12,24 @@ const _ = browser.i18n.getMessage;
|
|||||||
|
|
||||||
const MATCH_PATTERN_REGEX = /^(?:(?:(\*|https?|ftp):\/\/((?:\*\.|[^\/\*])+)|(file):\/\/\/?(?:\*\.|[^\/\*])+)(\/.*)|<all_urls>)$/;
|
const MATCH_PATTERN_REGEX = /^(?:(?:(\*|https?|ftp):\/\/((?:\*\.|[^\/\*])+)|(file):\/\/\/?(?:\*\.|[^\/\*])+)(\/.*)|<all_urls>)$/;
|
||||||
|
|
||||||
class EditableListItem extends React.Component {
|
function getInputValue (input) {
|
||||||
constructor (props) {
|
switch (input.type) {
|
||||||
super(props);
|
case "checkbox":
|
||||||
this.state = {
|
return input.checked;
|
||||||
editing: this.props.editing || false
|
case "number":
|
||||||
, editValue: ""
|
return parseFloat(input.value);
|
||||||
};
|
|
||||||
|
|
||||||
this.handleRemove = this.handleRemove.bind(this);
|
default:
|
||||||
this.handleEditBegin = this.handleEditBegin.bind(this);
|
return input.value;
|
||||||
this.handleEditEnd = this.handleEditEnd.bind(this);
|
|
||||||
this.handleInputChange = this.handleInputChange.bind(this);
|
|
||||||
this.handleInputKeyPress = this.handleInputKeyPress.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRemove () {
|
|
||||||
this.props.onRemove(this.props.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleEditBegin () {
|
|
||||||
this.setState({
|
|
||||||
editing: true
|
|
||||||
, editValue: this.props.text
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleEditEnd (ev) {
|
|
||||||
if (this.props.editing
|
|
||||||
&& !this.props.itemPattern.test(this.state.editValue)) {
|
|
||||||
ev.target.setCustomValidity(this.props.itemPatternError());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ev.target.validity.valid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.props.onEdit(this.props.text, this.state.editValue);
|
|
||||||
this.setState({
|
|
||||||
editing: false
|
|
||||||
, editValue: ""
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleInputChange (ev) {
|
|
||||||
this.setState({
|
|
||||||
editValue: ev.target.value
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.props.itemPattern.test(ev.target.value)) {
|
|
||||||
ev.target.setCustomValidity(this.props.itemPatternError());
|
|
||||||
} else {
|
|
||||||
ev.target.setCustomValidity("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleInputKeyPress (ev) {
|
|
||||||
if (ev.key === "Enter") {
|
|
||||||
this.handleEditEnd({ target: ev.target });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
return (
|
|
||||||
<li className="editable-list__item">
|
|
||||||
<div className="editable-list__title"
|
|
||||||
onDoubleClick={ this.handleEditBegin }>
|
|
||||||
{ this.state.editing
|
|
||||||
? <input className="editable-list__edit-field"
|
|
||||||
type="text"
|
|
||||||
autoFocus
|
|
||||||
value={ this.state.editValue }
|
|
||||||
onBlur={ this.handleEditEnd }
|
|
||||||
onChange={ this.handleInputChange }
|
|
||||||
onKeyPress={ this.handleInputKeyPress }/>
|
|
||||||
: this.props.text }
|
|
||||||
</div>
|
|
||||||
<button onClick={ this.handleEditBegin }>
|
|
||||||
{ _("options_option_uaWhitelistItemEdit") }
|
|
||||||
</button>
|
|
||||||
<button onClick={ this.handleRemove }>
|
|
||||||
{ _("options_option_uaWhitelistItemRemove") }
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class EditableList extends React.Component {
|
class App extends Component {
|
||||||
constructor (props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
items: new Set(this.props.data)
|
|
||||||
, addingNewItem: false
|
|
||||||
, rawView: false
|
|
||||||
, rawViewValue: ""
|
|
||||||
};
|
|
||||||
|
|
||||||
this.handleItemRemove = this.handleItemRemove.bind(this);
|
|
||||||
this.handleItemEdit = this.handleItemEdit.bind(this);
|
|
||||||
this.handleSwitchView = this.handleSwitchView.bind(this);
|
|
||||||
this.handleSaveRaw = this.handleSaveRaw.bind(this);
|
|
||||||
this.handleRawViewTextAreaChange = this.handleRawViewTextAreaChange.bind(this);
|
|
||||||
this.handleAddItem = this.handleAddItem.bind(this);
|
|
||||||
this.handleNewItemRemove = this.handleNewItemRemove.bind(this);
|
|
||||||
this.handleNewItemEdit = this.handleNewItemEdit.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleItemRemove (item) {
|
|
||||||
this.setState(currentState => {
|
|
||||||
const newItems = new Set(currentState.items);
|
|
||||||
newItems.delete(item);
|
|
||||||
return {
|
|
||||||
items: newItems
|
|
||||||
};
|
|
||||||
}, () => {
|
|
||||||
this.props.onListChange(Array.from(this.state.items));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleItemEdit (item, newValue) {
|
|
||||||
this.setState(currentState => ({
|
|
||||||
items: new Set([...currentState.items]
|
|
||||||
.map(item_ => item_ === item ? newValue : item_))
|
|
||||||
}), () => {
|
|
||||||
this.props.onListChange(Array.from(this.state.items));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSwitchView () {
|
|
||||||
this.setState(currentState => {
|
|
||||||
if (currentState.rawView) {
|
|
||||||
return {
|
|
||||||
rawView: false
|
|
||||||
, rawViewValue: ""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
rawView: true
|
|
||||||
, rawViewValue: [...currentState.items.values()].join("\n")
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSaveRaw () {
|
|
||||||
this.setState(currentState => {
|
|
||||||
console.log(currentState.rawViewValue);
|
|
||||||
const newItems = currentState.rawViewValue.split("\n")
|
|
||||||
.filter(item => item !== "");
|
|
||||||
|
|
||||||
if ("itemPattern" in this.props) {
|
|
||||||
for (const item of newItems) {
|
|
||||||
if (!this.props.itemPattern.test(item)) {
|
|
||||||
this.rawViewTextArea.setCustomValidity(
|
|
||||||
this.props.itemPatternError(item));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.rawViewTextArea.setCustomValidity("");
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
items: new Set(newItems)
|
|
||||||
};
|
|
||||||
}, () => {
|
|
||||||
this.props.onListChange(Array.from(this.state.items));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRawViewTextAreaChange (ev) {
|
|
||||||
if (this.rawViewTextArea.scrollHeight > this.rawViewTextArea.clientHeight) {
|
|
||||||
this.rawViewTextArea.style.height = `${this.rawViewTextArea.scrollHeight}px`;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
rawViewValue: ev.target.value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleAddItem () {
|
|
||||||
this.setState({
|
|
||||||
addingNewItem: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleNewItemRemove () {
|
|
||||||
this.setState({
|
|
||||||
addingNewItem: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleNewItemEdit (item, newItem) {
|
|
||||||
this.setState(currentState => ({
|
|
||||||
items: [ ...currentState.items, newItem ]
|
|
||||||
, addingNewItem: false
|
|
||||||
}), () => {
|
|
||||||
this.props.onListChange(Array.from(this.state.items));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const items = Array.from(this.state.items.values());
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="editable-list">
|
|
||||||
<button className="editable-list__view-button"
|
|
||||||
onClick={ this.handleSwitchView }>
|
|
||||||
{ this.state.rawView
|
|
||||||
? _("options_option_uaWhitelistBasicView")
|
|
||||||
: _("options_option_uaWhitelistRawView") }
|
|
||||||
</button>
|
|
||||||
{ this.state.rawView &&
|
|
||||||
<button className="editable-list__save-raw-button"
|
|
||||||
onClick={ this.handleSaveRaw }>
|
|
||||||
{ _("options_option_uaWhitelistSaveRaw") }
|
|
||||||
</button> }
|
|
||||||
<hr />
|
|
||||||
{
|
|
||||||
this.state.rawView
|
|
||||||
? ( <textarea className="editable-list__raw-view"
|
|
||||||
rows={ items.length}
|
|
||||||
value={ this.state.rawViewValue}
|
|
||||||
onChange={ this.handleRawViewTextAreaChange }
|
|
||||||
ref={ el => { this.rawViewTextArea = el }}>
|
|
||||||
</textarea> )
|
|
||||||
: ( <ul className="editable-list__items">
|
|
||||||
{ items.map((item, i) =>
|
|
||||||
<EditableListItem text={ item }
|
|
||||||
itemPattern={ this.props.itemPattern }
|
|
||||||
itemPatternError={ this.props.itemPatternError }
|
|
||||||
onRemove={ this.handleItemRemove }
|
|
||||||
onEdit={ this.handleItemEdit }
|
|
||||||
key={ i } /> )}
|
|
||||||
{ this.state.addingNewItem &&
|
|
||||||
<EditableListItem text=""
|
|
||||||
itemPattern={ this.props.itemPattern }
|
|
||||||
itemPatternError={ this.props.itemPatternError }
|
|
||||||
onRemove={ this.handleNewItemRemove }
|
|
||||||
onEdit={ this.handleNewItemEdit }
|
|
||||||
editing={ true } /> }
|
|
||||||
<div className="editable-list__item editable-list__item-actions">
|
|
||||||
<button className="editable-list__add-button"
|
|
||||||
onClick={ this.handleAddItem }>
|
|
||||||
{ _("options_option_uaWhitelistAddItem") }
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</ul> )
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class OptionsApp extends Component {
|
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...props.options
|
options: props.options
|
||||||
, isFormValid: true
|
, isFormValid: true
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -278,8 +37,10 @@ class OptionsApp extends Component {
|
|||||||
this.handleFormSubmit = this.handleFormSubmit.bind(this);
|
this.handleFormSubmit = this.handleFormSubmit.bind(this);
|
||||||
this.handleFormChange = this.handleFormChange.bind(this);
|
this.handleFormChange = this.handleFormChange.bind(this);
|
||||||
this.handleInputChange = this.handleInputChange.bind(this);
|
this.handleInputChange = this.handleInputChange.bind(this);
|
||||||
this.handleListChange = this.handleListChange.bind(this);
|
this.handleWhitelistChange = this.handleWhitelistChange.bind(this);
|
||||||
this.getItemPatternError = this.getItemPatternError.bind(this);
|
|
||||||
|
this.getWhitelistItemPatternError
|
||||||
|
= this.getWhitelistItemPatternError.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -287,21 +48,13 @@ class OptionsApp extends Component {
|
|||||||
*/
|
*/
|
||||||
setStorage () {
|
setStorage () {
|
||||||
return browser.storage.sync.set({
|
return browser.storage.sync.set({
|
||||||
options: {
|
options: this.state.options
|
||||||
option_mediaEnabled: this.state.option_mediaEnabled
|
|
||||||
, option_localMediaEnabled: this.state.option_localMediaEnabled
|
|
||||||
, option_localMediaServerPort: this.state.option_localMediaServerPort
|
|
||||||
, option_uaWhitelistEnabled: this.state.option_uaWhitelistEnabled
|
|
||||||
, option_uaWhitelist: this.state.option_uaWhitelist
|
|
||||||
, option_mirroringEnabled: this.state.option_mirroringEnabled
|
|
||||||
, option_mirroringAppId: this.state.option_mirroringAppId
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReset () {
|
handleReset () {
|
||||||
this.setState({
|
this.setState({
|
||||||
...defaultOptions
|
options: defaultOptions
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Propagate state properly
|
// TODO: Propagate state properly
|
||||||
@@ -314,7 +67,8 @@ class OptionsApp extends Component {
|
|||||||
this.form.reportValidity();
|
this.form.reportValidity();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { options: oldOptions } = await browser.storage.sync.get("options");
|
const { options: oldOptions }
|
||||||
|
= await browser.storage.sync.get("options");
|
||||||
await this.setStorage();
|
await this.setStorage();
|
||||||
const { options } = await browser.storage.sync.get("options");
|
const { options } = await browser.storage.sync.get("options");
|
||||||
|
|
||||||
@@ -341,31 +95,24 @@ class OptionsApp extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
handleInputChange (ev) {
|
handleInputChange (ev) {
|
||||||
const val = do {
|
const { target } = ev;
|
||||||
if (ev.target.type === "checkbox") {
|
|
||||||
ev.target.checked;
|
|
||||||
} else if (ev.target.type === "number") {
|
|
||||||
parseInt(ev.target.value);
|
|
||||||
} else {
|
|
||||||
ev.target.value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.setState({
|
this.setState(({ options }) => {
|
||||||
[ ev.target.name ]: val
|
options[target.name] = getInputValue(target);
|
||||||
|
return { options };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleListChange (data) {
|
handleWhitelistChange (whitelist) {
|
||||||
this.setState({
|
this.setState(({ options }) => {
|
||||||
option_uaWhitelist: data
|
options.userAgentWhitelist = whitelist;
|
||||||
|
return { options };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getItemPatternError (info) {
|
getWhitelistItemPatternError (info) {
|
||||||
return _("options_option_uaWhitelistInvalidMatchPattern", info);
|
return _("optionsUserAgentWhitelistInvalidMatchPattern", info);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@@ -374,122 +121,122 @@ class OptionsApp extends Component {
|
|||||||
onSubmit={ this.handleFormSubmit }
|
onSubmit={ this.handleFormSubmit }
|
||||||
onChange={ this.handleFormChange }>
|
onChange={ this.handleFormChange }>
|
||||||
<fieldset className="category">
|
<fieldset className="category">
|
||||||
<legend className="category-name">
|
<legend className="category__name">
|
||||||
{ _("options_category_media") }
|
{ _("optionsMediaCategoryName") }
|
||||||
</legend>
|
</legend>
|
||||||
<p className="category-description">
|
<p className="category__description">
|
||||||
{ _("options_category_media_description") }
|
{ _("optionsMediaCategoryDescription") }
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<label className="option">
|
<label className="option">
|
||||||
<div className="option-label">
|
<div className="option__label">
|
||||||
{ _("options_option_mediaEnabled") }
|
{ _("optionsMediaEnabled") }
|
||||||
</div>
|
</div>
|
||||||
<input name="option_mediaEnabled"
|
<input name="mediaEnabled"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.option_mediaEnabled }
|
checked={ this.state.options.mediaEnabled }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<fieldset className="category">
|
<fieldset className="category">
|
||||||
<legend className="category-name">
|
<legend className="category__name">
|
||||||
{ _("options_category_localMedia") }
|
{ _("optionsLocalMediaCategoryName") }
|
||||||
</legend>
|
</legend>
|
||||||
<p className="category-description">
|
<p className="category__description">
|
||||||
{ _("options_category_localMedia_description") }
|
{ _("optionsLocalMediaCategoryDescription") }
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<label className="option">
|
<label className="option">
|
||||||
<div className="option-label">
|
<div className="option__label">
|
||||||
{ _("options_option_localMediaEnabled") }
|
{ _("optionsLocalMediaEnabled") }
|
||||||
</div>
|
</div>
|
||||||
<input name="option_localMediaEnabled"
|
<input name="localMediaEnabled"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.option_localMediaEnabled }
|
checked={ this.state.options.localMediaEnabled }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className="option">
|
<label className="option">
|
||||||
<div className="option-label">
|
<div className="option__label">
|
||||||
{ _("options_option_localMediaServerPort") }
|
{ _("optionsLocalMediaServerPort") }
|
||||||
</div>
|
</div>
|
||||||
<input name="option_localMediaServerPort"
|
<input name="localMediaServerPort"
|
||||||
type="number"
|
type="number"
|
||||||
required
|
required
|
||||||
min="1025"
|
min="1025"
|
||||||
max="65535"
|
max="65535"
|
||||||
value={ this.state.option_localMediaServerPort }
|
value={ this.state.options.localMediaServerPort }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset className="category">
|
<fieldset className="category">
|
||||||
<legend className="category-name">
|
<legend className="category__name">
|
||||||
{ _("options_category_mirroring") }
|
{ _("optionsMirroringCategoryName") }
|
||||||
</legend>
|
</legend>
|
||||||
<p className="category-description">
|
<p className="category__description">
|
||||||
{ _("options_category_mirroring_description") }
|
{ _("optionsMirroringCategoryDescription") }
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<label className="option">
|
<label className="option">
|
||||||
<div className="option-label">
|
<div className="option__label">
|
||||||
{ _("options_option_mirroringEnabled") }
|
{ _("optionsMirroringEnabled") }
|
||||||
</div>
|
</div>
|
||||||
<input name="option_mirroringEnabled"
|
<input name="mirroringEnabled"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.option_mirroringEnabled }
|
checked={ this.state.options.mirroringEnabled }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className="option">
|
<label className="option">
|
||||||
<div className="option-label">
|
<div className="option__label">
|
||||||
{ _("options_option_mirroringAppId") }
|
{ _("optionsMirroringAppId") }
|
||||||
</div>
|
</div>
|
||||||
<input name="option_mirroringAppId"
|
<input name="mirroringAppId"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
value={ this.state.option_mirroringAppId }
|
value={ this.state.options.mirroringAppId }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset className="category">
|
<fieldset className="category">
|
||||||
<legend className="category-name">
|
<legend className="category__name">
|
||||||
{ _("options_category_uaWhitelist") }
|
{ _("optionsUserAgentWhitelistCategoryName") }
|
||||||
</legend>
|
</legend>
|
||||||
<p className="category-description">
|
<p className="category__description">
|
||||||
{ _("options_category_uaWhitelist_description") }
|
{ _("optionsUserAgentWhitelistCategoryDescription") }
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<label className="option">
|
<label className="option">
|
||||||
<div className="option-label">
|
<div className="option__label">
|
||||||
{ _("options_option_uaWhitelistEnabled") }
|
{ _("optionsUserAgentWhitelistEnabled") }
|
||||||
</div>
|
</div>
|
||||||
<input name="option_uaWhitelistEnabled"
|
<input name="userAgentWhitelistEnabled"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={ this.state.option_uaWhitelistEnabled }
|
checked={ this.state.options.userAgentWhitelistEnabled }
|
||||||
onChange={ this.handleInputChange } />
|
onChange={ this.handleInputChange } />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label className="option">
|
<div className="option">
|
||||||
<div className="option-label">
|
<div className="option__label">
|
||||||
{ _("options_option_uaWhitelist") }
|
{ _("optionsUserAgentWhitelistContent") }
|
||||||
</div>
|
</div>
|
||||||
<EditableList data={ this.state.option_uaWhitelist }
|
<EditableList data={ this.state.options.userAgentWhitelist }
|
||||||
onListChange={ this.handleListChange }
|
onChange={ this.handleWhitelistChange }
|
||||||
itemPattern={ MATCH_PATTERN_REGEX }
|
itemPattern={ MATCH_PATTERN_REGEX }
|
||||||
itemPatternError={ this.getItemPatternError }/>
|
itemPatternError={ this.getWhitelistItemPatternError }/>
|
||||||
</label>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div id="buttons">
|
<div id="buttons">
|
||||||
<button onClick={ this.handleReset }>
|
<button onClick={ this.handleReset }>
|
||||||
{ _("options_reset") }
|
{ _("optionsReset") }
|
||||||
</button>
|
</button>
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
disabled={ !this.state.isFormValid }>
|
disabled={ !this.state.isFormValid }>
|
||||||
{ _("options_submit") }
|
{ _("optionsSubmit") }
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -500,6 +247,6 @@ class OptionsApp extends Component {
|
|||||||
|
|
||||||
browser.storage.sync.get("options").then(({options}) => {
|
browser.storage.sync.get("options").then(({options}) => {
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<OptionsApp options={options} />
|
<App options={options} />
|
||||||
, document.querySelector("#root"));
|
, document.querySelector("#root"));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -87,7 +87,9 @@ class App extends Component {
|
|||||||
<div>
|
<div>
|
||||||
<div className="media-select">
|
<div className="media-select">
|
||||||
Cast
|
Cast
|
||||||
<select value={this.state.selectedMedia} onChange={this.onSelectChange.bind(this)} className="media-select-dropdown">
|
<select value={this.state.selectedMedia}
|
||||||
|
onChange={this.onSelectChange.bind(this)}
|
||||||
|
className="media-select-dropdown">
|
||||||
<option value="app" disabled={shareMedia}>this site's app</option>
|
<option value="app" disabled={shareMedia}>this site's app</option>
|
||||||
<option value="tab" disabled={!shareMedia}>Tab</option>
|
<option value="tab" disabled={!shareMedia}>Tab</option>
|
||||||
<option value="screen" disabled={!shareMedia}>Screen</option>
|
<option value="screen" disabled={!shareMedia}>Screen</option>
|
||||||
@@ -152,12 +154,12 @@ class Receiver extends Component {
|
|||||||
disabled={this.props.isLoading}>
|
disabled={this.props.isLoading}>
|
||||||
{ do {
|
{ do {
|
||||||
if (this.state.isLoading) {
|
if (this.state.isLoading) {
|
||||||
_("popup_casting_button_label") +
|
_("popupCastingButtonLabel") +
|
||||||
(this.state.isLoading
|
(this.state.isLoading
|
||||||
? this.state.ellipsis
|
? this.state.ellipsis
|
||||||
: "" )
|
: "" )
|
||||||
} else {
|
} else {
|
||||||
_("popup_cast_button_label")
|
_("popupCastButtonLabel")
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ module.exports = (env) => ({
|
|||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.jsx?$/
|
test: /\.jsx?$/
|
||||||
|
, resolve: {
|
||||||
|
extensions: [ ".js", ".jsx" ]
|
||||||
|
}
|
||||||
, include: `${includePath}`
|
, include: `${includePath}`
|
||||||
, use: {
|
, use: {
|
||||||
loader: "babel-loader"
|
loader: "babel-loader"
|
||||||
|
|||||||
Reference in New Issue
Block a user