diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a821746..52c2af7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,17 +35,43 @@ Missing/outdated strings: - `optionsMirroringCategoryDescription` - `optionsMirroringEnabled` - `optionsMirroringAppId` - - `popupWhitelistNotWhitelisted` - - `popupWhitelistAddToWhitelist` - - `popupMediaTypeAppNotFound` - `optionsBridgeCompatible` - `optionsBridgeLikelyCompatible` - `optionsBridgeIncompatible` + - `optionsSiteWhitelistCategoryName` + - `optionsSiteWhitelistCategoryDescription` + - `optionsSiteWhitelistEnabled` + - `optionsSiteWhitelistEnabledDescription` + - `optionsSiteWhitelistContent` + - `optionsSiteWhitelistBasicView` + - `optionsSiteWhitelistRawView` + - `optionsSiteWhitelistSaveRaw` + - `optionsSiteWhitelistAddItem` + - `optionsSiteWhitelistUserAgent` + - `optionsSiteWhitelistEditItem` + - `optionsSiteWhitelistRemoveItem` + - `optionsSiteWhitelistInvalidMatchPattern` + - `popupWhitelistNotWhitelisted` + - `popupWhitelistAddToWhitelist` + - `popupMediaTypeAppNotFound` - `popupCastMenuTitle` - `popupStopMenuTitle` - `es` + - `optionsSiteWhitelistCategoryName` + - `optionsSiteWhitelistCategoryDescription` + - `optionsSiteWhitelistEnabled` + - `optionsSiteWhitelistEnabledDescription` + - `optionsSiteWhitelistContent` + - `optionsSiteWhitelistBasicView` + - `optionsSiteWhitelistRawView` + - `optionsSiteWhitelistSaveRaw` + - `optionsSiteWhitelistAddItem` + - `optionsSiteWhitelistUserAgent` + - `optionsSiteWhitelistEditItem` + - `optionsSiteWhitelistRemoveItem` + - `optionsSiteWhitelistInvalidMatchPattern` - `popupWhitelistNotWhitelisted` - `popupWhitelistAddToWhitelist` - `popupCastMenuTitle` @@ -54,23 +80,47 @@ Missing/outdated strings: - `nl` - `optionsBridgeBackupEnabled` - - `optionsUserAgentWhitelistRestrictedEnabled` - - `optionsUserAgentWhitelistRestrictedEnabledDescription` + - `optionsBridgeCompatible` + - `optionsBridgeLikelyCompatible` + - `optionsBridgeIncompatible` - `optionsOptionRecommended` - `optionsMirroringCategoryName` - `optionsMirroringCategoryDescription` - `optionsMirroringEnabled` - `optionsMirroringAppId` + - `optionsSiteWhitelistCategoryName` + - `optionsSiteWhitelistCategoryDescription` + - `optionsSiteWhitelistEnabled` + - `optionsSiteWhitelistEnabledDescription` + - `optionsSiteWhitelistContent` + - `optionsSiteWhitelistBasicView` + - `optionsSiteWhitelistRawView` + - `optionsSiteWhitelistSaveRaw` + - `optionsSiteWhitelistAddItem` + - `optionsSiteWhitelistUserAgent` + - `optionsSiteWhitelistEditItem` + - `optionsSiteWhitelistRemoveItem` + - `optionsSiteWhitelistInvalidMatchPattern` - `popupWhitelistNotWhitelisted` - `popupWhitelistAddToWhitelist` - `popupMediaTypeAppNotFound` - - `optionsBridgeCompatible` - - `optionsBridgeLikelyCompatible` - - `optionsBridgeIncompatible` - `popupCastMenuTitle` - `popupStopMenuTitle` - `no` + - `optionsSiteWhitelistCategoryName` + - `optionsSiteWhitelistCategoryDescription` + - `optionsSiteWhitelistEnabled` + - `optionsSiteWhitelistEnabledDescription` + - `optionsSiteWhitelistContent` + - `optionsSiteWhitelistBasicView` + - `optionsSiteWhitelistRawView` + - `optionsSiteWhitelistSaveRaw` + - `optionsSiteWhitelistAddItem` + - `optionsSiteWhitelistUserAgent` + - `optionsSiteWhitelistEditItem` + - `optionsSiteWhitelistRemoveItem` + - `optionsSiteWhitelistInvalidMatchPattern` - `popupWhitelistNotWhitelisted` - `popupWhitelistAddToWhitelist` - `popupCastMenuTitle` diff --git a/ext/src/_locales/de/messages.json b/ext/src/_locales/de/messages.json index 95b332a..9cd1f3f 100644 --- a/ext/src/_locales/de/messages.json +++ b/ext/src/_locales/de/messages.json @@ -259,55 +259,55 @@ "description": "Receiver selector close if focus lost option checkbox label." }, - "optionsUserAgentWhitelistCategoryName": { + "optionsSiteWhitelistCategoryName": { "message": "Useragent-Whitelist", "description": "Options page whitelist category title." }, - "optionsUserAgentWhitelistCategoryDescription": { + "optionsSiteWhitelistCategoryDescription": { "message": "Seiten auf denen der Useragent aus Kompatibilitätsgründen mit einer Chrome-Version ersetzt wird. Suchmuster müssen gültig sein.", "description": "Options page whitelist category description." }, - "optionsUserAgentWhitelistEnabled": { + "optionsSiteWhitelistEnabled": { "message": "Webseiten-Whitelist aktivieren", "description": "Whitelist enabled checkbox label." }, - "optionsUserAgentWhitelistRestrictedEnabled": { + "optionsSiteWhitelistRestrictedEnabled": { "message": "Eingeschränkten Modus aktivieren", "description": "Whitelist restricted mode enabled checkbox label." }, - "optionsUserAgentWhitelistRestrictedEnabledDescription": { + "optionsSiteWhitelistRestrictedEnabledDescription": { "message": "Whitelist-Einschränkungen auch auf Seiten anwenden, die unabhängig vom aktuellen Useragent versuchen Stream-Funktionen zu laden.", "description": "Whitelist restricted mode enabled description." }, - "optionsUserAgentWhitelistContent": { + "optionsSiteWhitelistContent": { "message": "Suchmuster:", "description": "Match patterns editor widget label." }, - "optionsUserAgentWhitelistBasicView": { + "optionsSiteWhitelistBasicView": { "message": "Einfache Ansicht", "description": "Switch to basic view button title." }, - "optionsUserAgentWhitelistRawView": { + "optionsSiteWhitelistRawView": { "message": "Rohdatenansicht", "description": "Switch to raw view button title." }, - "optionsUserAgentWhitelistSaveRaw": { + "optionsSiteWhitelistSaveRaw": { "message": "Rohdaten speichern", "description": "Save raw view edits button title." }, - "optionsUserAgentWhitelistAddItem": { + "optionsSiteWhitelistAddItem": { "message": "Eintrag hinzufügen", "description": "Add new whitelist item button title." }, - "optionsUserAgentWhitelistEditItem": { + "optionsSiteWhitelistEditItem": { "message": "Bearbeiten", "description": "Edit whitelist item button title. Displayed on each item." }, - "optionsUserAgentWhitelistRemoveItem": { + "optionsSiteWhitelistRemoveItem": { "message": "Entfernen", "description": "Remove whitelist item button title. Displayed on each item." }, - "optionsUserAgentWhitelistInvalidMatchPattern": { + "optionsSiteWhitelistInvalidMatchPattern": { "message": "Ungültiges Suchmuster $matchPattern$", "description": "Error displayed by input indicating an invalid match pattern.", "placeholders": { diff --git a/ext/src/_locales/en/messages.json b/ext/src/_locales/en/messages.json index c61c4b4..9524953 100755 --- a/ext/src/_locales/en/messages.json +++ b/ext/src/_locales/en/messages.json @@ -302,55 +302,55 @@ "description": "Receiver selector close if focus lost option checkbox label." }, - "optionsUserAgentWhitelistCategoryName": { - "message": "User agent whitelist", + "optionsSiteWhitelistCategoryName": { + "message": "Site whitelist", "description": "Options page whitelist category title." }, - "optionsUserAgentWhitelistCategoryDescription": { - "message": "Sites for which to replace the user agent with a Chrome version for compatibility. Must be valid match patterns.", + "optionsSiteWhitelistCategoryDescription": { + "message": "Site where cast functionality will be enabled and the user agent string will be replaced with a Chrome version for compatibility.", "description": "Options page whitelist category description." }, - "optionsUserAgentWhitelistEnabled": { + "optionsSiteWhitelistEnabled": { "message": "Enable site whitelist", "description": "Whitelist enabled checkbox label." }, - "optionsUserAgentWhitelistRestrictedEnabled": { - "message": "Enable restricted mode", - "description": "Whitelist restricted mode enabled checkbox label." - }, - "optionsUserAgentWhitelistRestrictedEnabledDescription": { - "message": "Also apply whitelist restrictions to sites attempting to load cast functionality regardless of the current user agent.", + "optionsSiteWhitelistEnabledDescription": { + "message": "Disabling this option will enable cast functionality on any site, but the user agent string will not be replaced.", "description": "Whitelist restricted mode enabled description." }, - "optionsUserAgentWhitelistContent": { + "optionsSiteWhitelistContent": { "message": "Match patterns:", "description": "Match patterns editor widget label." }, - "optionsUserAgentWhitelistBasicView": { + "optionsSiteWhitelistBasicView": { "message": "Basic View", "description": "Switch to basic view button title." }, - "optionsUserAgentWhitelistRawView": { + "optionsSiteWhitelistRawView": { "message": "Raw View", "description": "Switch to raw view button title." }, - "optionsUserAgentWhitelistSaveRaw": { + "optionsSiteWhitelistSaveRaw": { "message": "Save Raw", "description": "Save raw view edits button title." }, - "optionsUserAgentWhitelistAddItem": { + "optionsSiteWhitelistAddItem": { "message": "Add Item", "description": "Add new whitelist item button title." }, - "optionsUserAgentWhitelistEditItem": { + "optionsSiteWhitelistUserAgent": { + "message": "Enable UA", + "description": "Whitelist item user agent checkbox title." + }, + "optionsSiteWhitelistEditItem": { "message": "Edit", "description": "Edit whitelist item button title. Displayed on each item." }, - "optionsUserAgentWhitelistRemoveItem": { + "optionsSiteWhitelistRemoveItem": { "message": "Remove", "description": "Remove whitelist item button title. Displayed on each item." }, - "optionsUserAgentWhitelistInvalidMatchPattern": { + "optionsSiteWhitelistInvalidMatchPattern": { "message": "Invalid match pattern $matchPattern$", "description": "Error displayed by input indicating an invalid match pattern.", "placeholders": { diff --git a/ext/src/_locales/es/messages.json b/ext/src/_locales/es/messages.json index 1397c35..55afeec 100644 --- a/ext/src/_locales/es/messages.json +++ b/ext/src/_locales/es/messages.json @@ -275,55 +275,55 @@ "description": "Receiver selector close if focus lost option checkbox label." }, - "optionsUserAgentWhitelistCategoryName": { + "optionsSiteWhitelistCategoryName": { "message": "Lista blanca de agentes de usuario", "description": "Options page whitelist category title." }, - "optionsUserAgentWhitelistCategoryDescription": { + "optionsSiteWhitelistCategoryDescription": { "message": "Sitios en los cuales reemplazar el agente de usuario con una versión de Chrome para compatibilidad. Deben ser patrones de coincidencia válidos.", "description": "Options page whitelist category description." }, - "optionsUserAgentWhitelistEnabled": { + "optionsSiteWhitelistEnabled": { "message": "Activar lista blanca de sitios", "description": "Whitelist enabled checkbox label." }, - "optionsUserAgentWhitelistRestrictedEnabled": { + "optionsSiteWhitelistRestrictedEnabled": { "message": "Activar modo restringido", "description": "Whitelist restricted mode enabled checkbox label." }, - "optionsUserAgentWhitelistRestrictedEnabledDescription": { + "optionsSiteWhitelistRestrictedEnabledDescription": { "message": "También aplica restricciones de la lista blanca a sitios intentando cargar la funcionalidad de transmisión sin importar el agente de usuario actual.", "description": "Whitelist restricted mode enabled description." }, - "optionsUserAgentWhitelistContent": { + "optionsSiteWhitelistContent": { "message": "Patrones de coincidencia:", "description": "Match patterns editor widget label." }, - "optionsUserAgentWhitelistBasicView": { + "optionsSiteWhitelistBasicView": { "message": "Vista básica", "description": "Switch to basic view button title." }, - "optionsUserAgentWhitelistRawView": { + "optionsSiteWhitelistRawView": { "message": "Vista en bruto", "description": "Switch to raw view button title." }, - "optionsUserAgentWhitelistSaveRaw": { + "optionsSiteWhitelistSaveRaw": { "message": "Guardar archivo en bruto", "description": "Save raw view edits button title." }, - "optionsUserAgentWhitelistAddItem": { + "optionsSiteWhitelistAddItem": { "message": "Añadir elemento", "description": "Add new whitelist item button title." }, - "optionsUserAgentWhitelistEditItem": { + "optionsSiteWhitelistEditItem": { "message": "Editar", "description": "Edit whitelist item button title. Displayed on each item." }, - "optionsUserAgentWhitelistRemoveItem": { + "optionsSiteWhitelistRemoveItem": { "message": "Eliminar", "description": "Remove whitelist item button title. Displayed on each item." }, - "optionsUserAgentWhitelistInvalidMatchPattern": { + "optionsSiteWhitelistInvalidMatchPattern": { "message": "Patrón de coincidencia $matchPattern$ inválido", "description": "Error displayed by input indicating an invalid match pattern.", "placeholders": { diff --git a/ext/src/_locales/nl/messages.json b/ext/src/_locales/nl/messages.json index 4f8e4cd..d7e2f9d 100755 --- a/ext/src/_locales/nl/messages.json +++ b/ext/src/_locales/nl/messages.json @@ -243,47 +243,47 @@ "message": "Sluit na het verliezen van de focus", "description": "Receiver selector close if focus lost option checkbox label." }, - "optionsUserAgentWhitelistCategoryName": { + "optionsSiteWhitelistCategoryName": { "message": "Gebruikersagent - Whitelist", "description": "Options page whitelist category title." }, - "optionsUserAgentWhitelistCategoryDescription": { + "optionsSiteWhitelistCategoryDescription": { "message": "Websites waarvan de gebruikersagent omwille van compatibiliteit moet worden ingesteld op Chrome. De patronen moeten geldig zijn.", "description": "Options page whitelist category description." }, - "optionsUserAgentWhitelistEnabled": { + "optionsSiteWhitelistEnabled": { "message": "Whitelist ingeschakeld", "description": "Whitelist enabled checkbox label." }, - "optionsUserAgentWhitelistContent": { + "optionsSiteWhitelistContent": { "message": "Patronen:", "description": "Match patterns editor widget label." }, - "optionsUserAgentWhitelistBasicView": { + "optionsSiteWhitelistBasicView": { "message": "Basisweergave", "description": "Switch to basic view button title." }, - "optionsUserAgentWhitelistRawView": { + "optionsSiteWhitelistRawView": { "message": "Ruwe weergave", "description": "Switch to raw view button title." }, - "optionsUserAgentWhitelistSaveRaw": { + "optionsSiteWhitelistSaveRaw": { "message": "Ruwe weergave opslaan", "description": "Save raw view edits button title." }, - "optionsUserAgentWhitelistAddItem": { + "optionsSiteWhitelistAddItem": { "message": "Voeg toe", "description": "Add new whitelist item button title." }, - "optionsUserAgentWhitelistEditItem": { + "optionsSiteWhitelistEditItem": { "message": "Bewerken", "description": "Edit whitelist item button title. Displayed on each item." }, - "optionsUserAgentWhitelistRemoveItem": { + "optionsSiteWhitelistRemoveItem": { "message": "Verwijderen", "description": "Remove whitelist item button title. Displayed on each item." }, - "optionsUserAgentWhitelistInvalidMatchPattern": { + "optionsSiteWhitelistInvalidMatchPattern": { "message": "Ongeldig patroon $matchPattern$", "description": "Error displayed by input indicating an invalid match pattern.", "placeholders": { diff --git a/ext/src/_locales/no/messages.json b/ext/src/_locales/no/messages.json index 32b8467..f31c525 100644 --- a/ext/src/_locales/no/messages.json +++ b/ext/src/_locales/no/messages.json @@ -263,55 +263,55 @@ "description": "Receiver selector close if focus lost option checkbox label." }, - "optionsUserAgentWhitelistCategoryName": { + "optionsSiteWhitelistCategoryName": { "message": "Brukeragent whitelist", "description": "Options page whitelist category title." }, - "optionsUserAgentWhitelistCategoryDescription": { + "optionsSiteWhitelistCategoryDescription": { "message": "Sider hvor man kan erstatte brukeragent med en Chrome-versjon for kompatibilitet. Må være et gjenkjennbart mønster.", "description": "Options page whitelist category description." }, - "optionsUserAgentWhitelistEnabled": { + "optionsSiteWhitelistEnabled": { "message": "Skru på whitelist", "description": "Whitelist enabled checkbox label." }, - "optionsUserAgentWhitelistRestrictedEnabled": { + "optionsSiteWhitelistRestrictedEnabled": { "message": "Skru på ", "description": "Whitelist restricted mode enabled checkbox label." }, - "optionsUserAgentWhitelistRestrictedEnabledDescription": { + "optionsSiteWhitelistRestrictedEnabledDescription": { "message": "Legg også til whitelist-begrensninger til side smo prøvde å laste cast-funksjonalitet uavhengig av nåværende brukeragent.", "description": "Whitelist restricted mode enabled description." }, - "optionsUserAgentWhitelistContent": { + "optionsSiteWhitelistContent": { "message": "Match mønster", "description": "Match patterns editor widget label." }, - "optionsUserAgentWhitelistBasicView": { + "optionsSiteWhitelistBasicView": { "message": "Standard visning", "description": "Switch to basic view button title." }, - "optionsUserAgentWhitelistRawView": { + "optionsSiteWhitelistRawView": { "message": "Rå visning", "description": "Switch to raw view button title." }, - "optionsUserAgentWhitelistSaveRaw": { + "optionsSiteWhitelistSaveRaw": { "message": "Lagre rå", "description": "Save raw view edits button title." }, - "optionsUserAgentWhitelistAddItem": { + "optionsSiteWhitelistAddItem": { "message": "Legg til", "description": "Add new whitelist item button title." }, - "optionsUserAgentWhitelistEditItem": { + "optionsSiteWhitelistEditItem": { "message": "Rediger", "description": "Edit whitelist item button title. Displayed on each item." }, - "optionsUserAgentWhitelistRemoveItem": { + "optionsSiteWhitelistRemoveItem": { "message": "Fjern", "description": "Remove whitelist item button title. Displayed on each item." }, - "optionsUserAgentWhitelistInvalidMatchPattern": { + "optionsSiteWhitelistInvalidMatchPattern": { "message": "Ugyldig mønster $matchPattern$", "description": "Error displayed by input indicating an invalid match pattern.", "placeholders": { diff --git a/ext/src/background/menus.ts b/ext/src/background/menus.ts index fff31b2..9c0fc1d 100644 --- a/ext/src/background/menus.ts +++ b/ext/src/background/menus.ts @@ -137,11 +137,11 @@ async function onMenuClicked( ); } - const whitelist = await options.get("userAgentWhitelist"); - if (!whitelist.includes(pattern)) { + const whitelist = await options.get("siteWhitelist"); + if (!whitelist.find(item => item.pattern === pattern)) { // Add to whitelist and update options - whitelist.push(pattern); - await options.set("userAgentWhitelist", whitelist); + whitelist.push({ pattern }); + await options.set("siteWhitelist", whitelist); } return; diff --git a/ext/src/background/whitelist.ts b/ext/src/background/whitelist.ts index 2e03d78..f05030a 100644 --- a/ext/src/background/whitelist.ts +++ b/ext/src/background/whitelist.ts @@ -22,6 +22,11 @@ type OnBeforeRequestDetails = Parameters< frameAncestors?: Array<{ url: string; frameId: number }>; }; +export interface WhitelistItemData { + pattern: string; + isUserAgentDisabled?: boolean; +} + const originUrlCache: string[] = []; let platform: string; @@ -47,18 +52,18 @@ export async function initWhitelist() { } // Register on first run - await registerUserAgentWhitelist(); + await registerSiteWhitelist(); // Re-register when options change options.addEventListener("changed", ev => { const alteredOpts = ev.detail; if ( - alteredOpts.includes("userAgentWhitelist") || - alteredOpts.includes("userAgentWhitelistEnabled") + alteredOpts.includes("siteWhitelist") || + alteredOpts.includes("siteWhitelistEnabled") ) { - unregisterUserAgentWhitelist(); - registerUserAgentWhitelist(); + unregisterSiteWhitelist(); + registerSiteWhitelist(); } }); } @@ -189,9 +194,8 @@ async function onBeforeCastSDKRequest(details: OnBeforeRequestDetails) { }; } -async function registerUserAgentWhitelist() { - const { userAgentWhitelist, userAgentWhitelistEnabled } = - await options.getAll(); +async function registerSiteWhitelist() { + const { siteWhitelist, siteWhitelistEnabled } = await options.getAll(); browser.webRequest.onBeforeRequest.addListener( onBeforeCastSDKRequest, @@ -199,13 +203,18 @@ async function registerUserAgentWhitelist() { ["blocking"] ); - if (!userAgentWhitelistEnabled || !userAgentWhitelist.length) { + if (!siteWhitelistEnabled || !siteWhitelist.length) { return; } browser.webRequest.onBeforeSendHeaders.addListener( onWhitelistedBeforeSendHeaders, - { urls: userAgentWhitelist }, + { + // Filter for items with UA enabled + urls: siteWhitelist.flatMap(item => + !item.isUserAgentDisabled ? [item.pattern] : [] + ) + }, ["blocking", "requestHeaders"] ); @@ -216,7 +225,7 @@ async function registerUserAgentWhitelist() { ); } -function unregisterUserAgentWhitelist() { +function unregisterSiteWhitelist() { originUrlCache.length = 0; browser.webRequest.onBeforeSendHeaders.removeListener( diff --git a/ext/src/defaultOptions.ts b/ext/src/defaultOptions.ts index 90913cc..cd03d25 100644 --- a/ext/src/defaultOptions.ts +++ b/ext/src/defaultOptions.ts @@ -16,7 +16,6 @@ export default { mirroringAppId: MIRRORING_APP_ID, receiverSelectorCloseIfFocusLost: true, receiverSelectorWaitForConnection: true, - userAgentWhitelistEnabled: true, - userAgentWhitelistRestrictedEnabled: true, - userAgentWhitelist: ["https://www.netflix.com/*"] + siteWhitelistEnabled: true, + siteWhitelist: [{ pattern: "https://www.netflix.com/*" }] } as Options; diff --git a/ext/src/lib/options.ts b/ext/src/lib/options.ts index 03dc7fb..fdf0955 100644 --- a/ext/src/lib/options.ts +++ b/ext/src/lib/options.ts @@ -1,6 +1,7 @@ "use strict"; import defaultOptions from "../defaultOptions"; +import type { WhitelistItemData } from "../background/whitelist"; import logger from "./logger"; @@ -25,9 +26,8 @@ export interface Options { mirroringAppId: string; receiverSelectorCloseIfFocusLost: boolean; receiverSelectorWaitForConnection: boolean; - userAgentWhitelistEnabled: boolean; - userAgentWhitelistRestrictedEnabled: boolean; - userAgentWhitelist: string[]; + siteWhitelistEnabled: boolean; + siteWhitelist: WhitelistItemData[]; [key: string]: Options[keyof Options]; } diff --git a/ext/src/ui/options/EditableList.tsx b/ext/src/ui/options/EditableList.tsx deleted file mode 100644 index d50f5c1..0000000 --- a/ext/src/ui/options/EditableList.tsx +++ /dev/null @@ -1,316 +0,0 @@ -/* eslint-disable max-len */ -"use strict"; - -import React, { Component } from "react"; - -const _ = browser.i18n.getMessage; - - -interface EditableListProps { - data: string[]; - itemPattern: RegExp; - onChange (data: string[]): void; - itemPatternError (err?: string): string; -} - -interface EditableListState { - addingNewItem: boolean; - rawView: boolean; - rawViewValue: string; -} - -export default class EditableList extends Component< - EditableListProps, EditableListState> { - - private rawViewTextArea: (HTMLTextAreaElement | null) = null; - - constructor(props: EditableListProps) { - super(props); - - this.state = { - 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); - } - - public render() { - return ( -