copy over modules & utils folders

This commit is contained in:
Collin M. Barrett 2019-07-26 20:06:42 -05:00
parent e4daf3d36c
commit 410004fa8a
110 changed files with 2130 additions and 0 deletions

View file

@ -0,0 +1,107 @@
import * as React from "react";
import { IColumnVisibility, ILanguage, ILicense, IList, IMaintainer, ISoftware, ISyntax, ITag } from "./interfaces";
import { ListsTable, Oneliner } from "./components";
const columnVisibilityDefaults: IColumnVisibility[] = [
{ column: "Software", visible: true },
{ column: "Languages", visible: true },
{ column: "Tags", visible: true },
//{ column: "Updated", visible: false },
//{ column: "Rules", visible: false },
{ column: "License", visible: false },
{ column: "Maintainers", visible: false },
{ column: "Subscribe", visible: false }
];
interface IProps {
languages: ILanguage[];
licenses: ILicense[];
lists: IList[];
maintainers: IMaintainer[];
//ruleCount: number;
software: ISoftware[];
syntaxes: ISyntax[];
tags: ITag[];
};
interface IState {
columnVisibility: IColumnVisibility[];
pageSize: number;
};
export class Home extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
columnVisibility: columnVisibilityDefaults,
pageSize: 20
};
this.updatePageSize = this.updatePageSize.bind(this);
}
componentDidMount() {
this.setMobileColumnVisibility();
this.updatePageSize();
};
setMobileColumnVisibility() {
if (window.innerWidth < 768) {
this.state.columnVisibility.forEach((c: IColumnVisibility) => {
c.visible = false;
});
}
};
updatePageSize() {
this.setState({
pageSize: Math.max(Math.floor((window.innerHeight - 400) / 52), 5)
});
};
render() {
return <div>
<Oneliner listCount={this.props.lists.length}/* ruleCount={this.props.ruleCount}*//>
<ListsTable {...this.props} {...this.state}/>
{this.renderColumnVisibilityCheckboxes()}
</div>;
};
renderColumnVisibilityCheckboxes() {
return this.props.lists.length > 0
? <div className="d-none d-md-block text-right">
Visible:&nbsp;&nbsp;{this.state.columnVisibility.map(
(c: IColumnVisibility, i: number) => this.renderColumnVisibilityCheckbox(c, i))}
</div>
: null;
};
renderColumnVisibilityCheckbox(props: IColumnVisibility, key: number) {
return <div className="form-check form-check-inline" key={key}>
<input className="form-check-input"
type="checkbox"
id={`checkbox${props.column.replace(/\s+/g, "")}`}
defaultChecked={props.visible}
onChange={() => this.checkColumn(props)}/>
<label className="form-check-label"
htmlFor={`checkbox${props.column.replace(/\s+/g, "")}`}>
{props.column}
</label>
</div>;
};
checkColumn(props: IColumnVisibility) {
const columnVisibility = this.state.columnVisibility;
const index = this.findWithAttr(columnVisibility, "column", props.column);
columnVisibility[index].visible = !columnVisibility[index].visible;
this.forceUpdate();
};
findWithAttr(array: any, attr: string, value: string) {
for (let i = 0; i < array.length; i += 1) {
if (array[i][attr] === value) {
return i;
}
}
return -1;
};
};

View file

@ -0,0 +1,94 @@
import * as React from "react";
import "isomorphic-fetch";
import { ILanguage, ILicense, IList, IMaintainer, ISoftware, ISyntax, ITag } from "./interfaces";
import { Home } from "./Home";
interface IState {
languages: ILanguage[];
licenses: ILicense[];
lists: IList[];
maintainers: IMaintainer[];
ruleCount: number;
software: ISoftware[];
syntaxes: ISyntax[];
tags: ITag[];
};
export class HomeContainer extends React.Component<{}, IState> {
constructor(props: any) {
super(props);
this.state = {
languages: [],
licenses: [],
lists: [],
maintainers: [],
ruleCount: 0,
software: [],
syntaxes: [],
tags: []
};
}
componentDidMount() {
this.fetchLanguages();
this.fetchLicenses();
this.fetchLists();
this.fetchMaintainers();
this.fetchSoftware();
this.fetchSyntaxes();
this.fetchTags();
// this.fetchRuleCount();
};
fetchLanguages() {
fetch("/api/v1/languages")
.then(r => r.json() as Promise<ILanguage[]>)
.then(p => { this.setState({ languages: p }); });
};
fetchLicenses() {
fetch("/api/v1/licenses")
.then(r => r.json() as Promise<ILicense[]>)
.then(p => { this.setState({ licenses: p }); });
};
fetchLists() {
fetch("/api/v1/lists")
.then(r => r.json() as Promise<IList[]>)
.then(p => { this.setState({ lists: p }); });
};
fetchMaintainers() {
fetch("/api/v1/maintainers")
.then(r => r.json() as Promise<IMaintainer[]>)
.then(p => { this.setState({ maintainers: p }); });
};
fetchSoftware() {
fetch("/api/v1/software")
.then(r => r.json() as Promise<ISoftware[]>)
.then(p => { this.setState({ software: p }); });
};
fetchSyntaxes() {
fetch("/api/v1/syntaxes")
.then(r => r.json() as Promise<ISyntax[]>)
.then(p => { this.setState({ syntaxes: p }); });
};
fetchTags() {
fetch("/api/v1/tags")
.then(r => r.json() as Promise<ITag[]>)
.then(p => { this.setState({ tags: p }); });
};
fetchRuleCount() {
fetch("/api/v1/rules")
.then(r => r.json() as Promise<number>)
.then(p => { this.setState({ ruleCount: p }); });
};
render() {
return <Home {...this.state}/>;
};
};

View file

@ -0,0 +1,17 @@
import * as React from "react";
interface IProps {
listCount: number;
//ruleCount: number;
};
export const Oneliner = (props: IProps) =>
props.listCount > 0/* && props.ruleCount > 0*/
? <p className="ml-2 mr-2">
The independent, comprehensive directory of {/*}<strong>{props.ruleCount.toLocaleString()
}</strong> unique rules across */}<strong>{props.listCount.toLocaleString()
}</strong> filter and host lists for advertisements, trackers, malware, and annoyances.
</p>
: <p className="ml-2 mr-2">
The independent, comprehensive directory of filter and host lists for advertisements, trackers, malware, and annoyances.
</p>;

View file

@ -0,0 +1,67 @@
import * as React from "react";
import { ITag } from "../interfaces";
import { getContrast } from "../../../utils";
interface IProps {
tags: ITag[];
};
export const TagGroup = (props: IProps) =>
props.tags && props.tags.length > 0
? <div className="fl-wrap-cell">
{props.tags.map((t: ITag, i: number) => <Tag tag={t} key={i}/>)}
</div>
: null;
interface ITagProps {
tag: ITag;
};
const Tag = (props: ITagProps) => {
if (props.tag) {
const hexColor = kelly_colors_hex[props.tag.id % kelly_colors_hex.length];
return <span className="badge"
style={{
backgroundColor: `#${hexColor}`,
color: getContrast(hexColor)
}}
title={props.tag.description}>
{props.tag.name}
</span>;
} else {
return null;
}
};
//https://stackoverflow.com/a/4382138/2343739
const kelly_colors_hex = [
"FFB300", // Vivid Yellow
"803E75", // Strong Purple
"FF6800", // Vivid Orange
"A6BDD7", // Very Light Blue
"C10020", // Vivid Red
"CEA262", // Grayish Yellow
"817066", // Medium Gray
"007D34", // Vivid Green
"F6768E", // Strong Purplish Pink
"00538A", // Strong Blue
"FF7A5C", // Strong Yellowish Pink
"53377A", // Strong Violet
"FF8E00", // Vivid Orange Yellow
"B32851", // Strong Purplish Red
"F4C800", // Vivid Greenish Yellow
"7F180D", // Strong Reddish Brown
"93AA00", // Vivid Yellowish Green
"593315", // Deep Yellowish Brown
"F13A13", // Vivid Reddish Orange
"232C16", // Dark Olive Green
"000000", // Black
"FFFFFF", // White
"C2F4BE", // Light Green
"1EDBB9", // Medium Greenish Blue
"890000", // Dark Red
"474747", // Dark Grey
"BFA125", // Matte Gold
"A7823C", // Dark Beige
"035A5C" // Dark Greenish Blue
];

View file

@ -0,0 +1,27 @@
import * as React from "react";
import { IColumnVisibility, ISoftware } from "../../interfaces";
import { IListDetails } from "./IListDetails";
import { InfoCard } from "./infoCard";
import { LinkButtonGroup } from "./LinkButtonGroup";
import { MaintainersInfoCard } from "./maintainersInfoCard";
interface IProps {
columnVisibility: IColumnVisibility[];
list: IListDetails;
software: ISoftware[];
};
export const DetailsExpander = (props: IProps) =>
<div className="card border-primary">
<div className="card-body p-2">
<div className="container m-0">
<div className="row">
<InfoCard columnVisibility={props.columnVisibility} software={props.software} {...props.list}/>
<LinkButtonGroup {...props.list}/>
</div>
<div className="row">
<MaintainersInfoCard maintainers={props.list.maintainers}/>
</div>
</div>
</div>
</div>;

View file

@ -0,0 +1,26 @@
import { ILanguage, ILicense, IMaintainer, ISyntax, ITag } from "../../interfaces";
export interface IListDetails {
id: number;
chatUrl: string;
description: string;
descriptionSourceUrl: string;
donateUrl: string;
emailAddress: string;
forumUrl: string;
homeUrl: string;
issuesUrl: string;
languages: ILanguage[];
license: ILicense;
maintainers: IMaintainer[];
name: string;
policyUrl: string;
publishedDate: string;
//ruleCount: number;
submissionUrl: string;
syntax: ISyntax;
tags: ITag[];
//updatedDate: string;
viewUrl: string;
viewUrlMirrors: string[];
};

View file

@ -0,0 +1,41 @@
import * as React from "react";
import {
ChatButton,
DonateButton,
EmailButton,
ForumButton,
HomeButton,
IssuesButton,
PolicyButton,
SubmitButton,
SubscribeButtonGroup,
ViewButtonGroup
} from "../linkButtons";
interface IProps {
chatUrl: string;
donateUrl: string;
emailAddress: string;
forumUrl: string;
homeUrl: string;
issuesUrl: string;
name: string;
policyUrl: string;
submissionUrl: string;
viewUrl: string;
viewUrlMirrors: string[];
};
export const LinkButtonGroup = (props: IProps) =>
<div className="col-3 p-0 btn-group-vertical justify-content-start d-flex align-items-end">
<SubscribeButtonGroup name={props.name} url={props.viewUrl} urlMirrors={props.viewUrlMirrors}/>
<ViewButtonGroup name={props.name} url={props.viewUrl} urlMirrors={props.viewUrlMirrors}/>
<HomeButton name={props.name} url={props.homeUrl}/>
<PolicyButton name={props.name} url={props.policyUrl}/>
<DonateButton name={props.name} url={props.donateUrl}/>
<IssuesButton name={props.name} url={props.issuesUrl}/>
<ForumButton name={props.name} url={props.forumUrl}/>
<ChatButton name={props.name} url={props.chatUrl}/>
<SubmitButton name={props.name} url={props.submissionUrl}/>
<EmailButton name={props.name} emailAddress={props.emailAddress}/>
</div>;

View file

@ -0,0 +1,7 @@
import { DetailsExpander } from "./DetailsExpander";
import { IListDetails } from "./IListDetails";
export {
DetailsExpander,
IListDetails
};

View file

@ -0,0 +1,16 @@
import * as React from "react";
import "./description.css";
interface IProps {
description: string;
url: string;
};
export const Description = (props: IProps) =>
props.description
? <h3 className="card-header fl-description">
{props.url
? <blockquote cite={props.url} className="m-0">{props.description}</blockquote>
: props.description}
</h3>
: null;

View file

@ -0,0 +1,57 @@
import * as React from "react";
import { IColumnVisibility } from "../../../interfaces";
import { ILanguage, ILicense, ISoftware, ISyntax, ITag } from "../../../interfaces";
import { Description } from "./Description";
import { Languages } from "./Languages";
import { License } from "./License";
import { PublishedDate } from "./PublishedDate";
import { RuleCount } from "./RuleCount";
import { SoftwareIcon } from "../../softwareIcon";
import { Syntax } from "./Syntax";
import { TagGroup } from "../../TagGroup"
import { UpdatedDate } from "./UpdatedDate";
interface IProps {
columnVisibility: IColumnVisibility[];
description: string;
descriptionSourceUrl: string;
languages: ILanguage[];
license: ILicense;
name: string;
publishedDate: string;
//ruleCount: number;
software: ISoftware[];
syntax: ISyntax;
tags: ITag[];
//updatedDate: string;
};
export const InfoCard = (props: IProps) =>
<div className="col-9">
{props.columnVisibility.filter((c: IColumnVisibility) => c.column === "Tags")[0].visible
? null
: <TagGroup tags={props.tags}/>}
<div>
{props.columnVisibility.filter((c: IColumnVisibility) => c.column === "Software")[0].visible
? null
: props.syntax
? props.software.filter((s: ISoftware) => s.syntaxIds.indexOf(props.syntax.id) > -1)
.map((s: ISoftware, i: number) => <SoftwareIcon id={s.id} key={i}/>)
: null}
</div>
<Description {...props} url={props.descriptionSourceUrl}/>
<ul className="list-group list-group-flush">
<Languages {...props}/>
{/*{props.columnVisibility.filter((c: IColumnVisibility) => c.column === "Rules")[0].visible
? null
: <RuleCount {...props}/>}
{props.columnVisibility.filter((c: IColumnVisibility) => c.column === "Updated")[0].visible
? null
: <UpdatedDate {...props}/>}*/}
<PublishedDate date={props.publishedDate}/>
<Syntax {...props} />
{props.columnVisibility.filter((c: IColumnVisibility) => c.column === "License")[0].visible
? null
: <License {...props} />}
</ul>
</div>;

View file

@ -0,0 +1,20 @@
import * as React from "react";
import { ILanguage } from "../../../interfaces";
interface IProps {
languages: ILanguage[];
};
export const Languages = (props: IProps) =>
props.languages && props.languages.length > 0
? props.languages.length > 1
? <li className="list-group-item">
<p className="m-0">Languages:</p>
<ul>
{props.languages.map((language: ILanguage, i: number) => <li key={i}>{language.name}</li>)}
</ul>
</li>
: <li className="list-group-item">
<p>Language: {props.languages[0].name}</p>
</li>
: null;

View file

@ -0,0 +1,17 @@
import * as React from "react";
import { ILicense } from "../../../interfaces";
interface IProps {
license: ILicense;
};
export const License = (props: IProps) =>
props.license.name
? (props.license.descriptionUrl
? <li className="list-group-item">
<p>License: <a href={props.license.descriptionUrl}>{props.license.name}</a></p>
</li>
: <li className="list-group-item">
<p>License: {props.license.name}</p>
</li>)
: null;

View file

@ -0,0 +1,13 @@
import * as React from "react";
import * as moment from "moment";
interface IProps {
date: string;
};
export const PublishedDate = (props: IProps) =>
props.date
? <li className="list-group-item">
<p>Published: {moment(props.date).format("l")}</p>
</li>
: null;

View file

@ -0,0 +1,12 @@
import * as React from "react";
interface IProps {
ruleCount: number;
};
export const RuleCount = (props: IProps) =>
props.ruleCount > 0
? <li className="list-group-item">
<p>Rule Count: {props.ruleCount.toLocaleString()}</p>
</li>
: null;

View file

@ -0,0 +1,17 @@
import * as React from "react";
import { ISyntax } from "../../../interfaces";
interface IProps {
syntax: ISyntax;
};
export const Syntax = (props: IProps) =>
props.syntax
? (props.syntax.definitionUrl
? <li className="list-group-item">
<p>Syntax: <a href={props.syntax.definitionUrl}>{props.syntax.name}</a></p>
</li>
: <li className="list-group-item">
<p>Syntax: {props.syntax.name}</p>
</li>)
: null;

View file

@ -0,0 +1,15 @@
import * as React from "react";
import * as moment from "moment";
interface IProps {
updatedDate: string;
};
export const UpdatedDate = (props: IProps) =>
props.updatedDate
? <li className="list-group-item">
<p>Updated: {moment(props.updatedDate).isValid()
? moment(props.updatedDate).format("l")
: "N/A"}</p>
</li>
: null;

View file

@ -0,0 +1 @@
.fl-description { margin: 0 0 .5rem; }

View file

@ -0,0 +1,5 @@
import { InfoCard } from "./InfoCard";
export {
InfoCard
};

View file

@ -0,0 +1,20 @@
import * as React from "react";
//interface IProps {
// name: string;
// additionalLists: IMaintainerAdditionalListDto[];
//};
export const MaintainerAdditionalLists = (/*props: IProps*/) =>
<div className="col-9">
{ /*props.additionalLists && props.additionalLists.length > 0
? <div>
<h4>More by {props.name}:</h4>
<ul>
{props.additionalLists.map(
(l: IMaintainerAdditionalListDto, i: number) => <li key={i}>{l.name}</li>)}
</ul>
</div>
: null*/ }
</div>;

View file

@ -0,0 +1,23 @@
import * as React from "react";
import { IMaintainer } from "../../../interfaces";
import { MaintainerAdditionalLists } from "./MaintainerAdditionalLists";
import { MaintainerLinkButtonGroup } from "./MaintainerLinkButtonGroup";
interface IProps {
maintainer: IMaintainer;
};
export const MaintainerInfoCard = (props: IProps) =>
props.maintainer.name
? <div className="card">
<div className="card-body">
<h3 className="card-header">Maintained by {props.maintainer.name}</h3>
<div className="container pt-1">
<div className="row">
<MaintainerAdditionalLists/>
<MaintainerLinkButtonGroup {...props.maintainer}/>
</div>
</div>
</div>
</div>
: null;

View file

@ -0,0 +1,16 @@
import * as React from "react";
import { EmailButton, HomeButton, TwitterButton } from "../../linkButtons";
interface IProps {
emailAddress: string;
homeUrl: string;
name: string;
twitterHandle: string;
};
export const MaintainerLinkButtonGroup = (props: IProps) =>
<div className="col-3 p-0 btn-group-vertical justify-content-start d-flex align-items-end" role="group">
<HomeButton {...props} url={props.homeUrl}/>
<EmailButton {...props}/>
<TwitterButton {...props}/>
</div>;

View file

@ -0,0 +1,14 @@
import * as React from "react";
import { IMaintainer } from "../../../interfaces";
import { MaintainerInfoCard } from "./MaintainerInfoCard";
interface IProps {
maintainers: IMaintainer[];
};
export const MaintainersInfoCard = (props: IProps) =>
props.maintainers && props.maintainers.length > 0
? <div className="w-100">
{props.maintainers.map((m: IMaintainer, i: number) => <MaintainerInfoCard maintainer={m} key={i}/>)}
</div>
: null;

View file

@ -0,0 +1,5 @@
import { MaintainersInfoCard } from "./MaintainersInfoCard";
export {
MaintainersInfoCard
};

View file

@ -0,0 +1,9 @@
import { DetailsExpander } from "./detailsExpander";
import { ListsTable } from "./listsTable";
import { Oneliner } from "./Oneliner";
export {
DetailsExpander,
ListsTable,
Oneliner
};

View file

@ -0,0 +1,14 @@
import * as React from "react";
import { LinkButton } from "./LinkButton";
interface IProps {
name: string;
url: string;
};
export const ChatButton = (props: IProps) =>
props.url
? <LinkButton href={props.url}
title={`Enter the chat room for ${props.name}.`}
text="Chat"/>
: null;

View file

@ -0,0 +1,14 @@
import * as React from "react";
import { LinkButton } from "./LinkButton";
interface IProps {
name: string;
url: string;
};
export const DonateButton = (props: IProps) =>
props.url
? <LinkButton href={props.url}
title={`Donate to the maintainer of ${props.name}.`}
text="Donate"/>
: null;

View file

@ -0,0 +1,14 @@
import * as React from "react";
import { LinkButton } from "./LinkButton";
interface IProps {
name: string;
emailAddress: string;
};
export const EmailButton = (props: IProps) =>
props.emailAddress
? <LinkButton href={`mailto:${props.emailAddress}`}
title={`Email ${props.name}.`}
text="Email"/>
: null;

View file

@ -0,0 +1,14 @@
import * as React from "react";
import { LinkButton } from "./LinkButton";
interface IProps {
name: string;
url: string;
};
export const ForumButton = (props: IProps) =>
props.url
? <LinkButton href={props.url}
title={`View the forum for ${props.name}.`}
text="Forum"/>
: null;

View file

@ -0,0 +1,14 @@
import * as React from "react";
import { LinkButton } from "./LinkButton";
interface IProps {
name: string;
url: string;
};
export const HomeButton = (props: IProps) =>
props.url
? <LinkButton href={props.url}
title={`View ${props.name}'s homepage.`}
text="Home"/>
: null;

View file

@ -0,0 +1,14 @@
import * as React from "react";
import { LinkButton } from "./LinkButton";
interface IProps {
name: string;
url: string;
};
export const IssuesButton = (props: IProps) =>
props.url
? <LinkButton href={props.url}
title={`View the GitHub Issues for ${props.name}.`}
text="GH Issues"/>
: null;

View file

@ -0,0 +1,18 @@
import * as React from "react";
import "./button.css";
interface IProps {
href: string;
title?: string;
buttonClass?: string;
text: string;
};
export const LinkButton = (props: IProps) =>
props.href
? <a href={props.href}
title={props.title}
className={`btn ${props.buttonClass || "btn-primary"} fl-btn-link`}>
{props.text}
</a>
: null;

View file

@ -0,0 +1,14 @@
import * as React from "react";
import { LinkButton } from "./LinkButton";
interface IProps {
name: string;
url: string;
};
export const PolicyButton = (props: IProps) =>
props.url
? <LinkButton href={props.url}
title={`View the types of rules that ${props.name} includes.`}
text="Policy"/>
: null;

View file

@ -0,0 +1,14 @@
import * as React from "react";
import { LinkButton } from "./LinkButton";
interface IProps {
name: string;
url: string;
};
export const SubmitButton = (props: IProps) =>
props.url
? <LinkButton href={props.url}
title={`Submit a new rule to be included in ${props.name}.`}
text="Submit"/>
: null;

View file

@ -0,0 +1,62 @@
import * as React from "react";
import { LinkButton } from "./LinkButton";
interface IProps {
name: string;
url: string;
text?: string;
};
export const SubscribeButton = (props: IProps) => {
let buttonClass: string | undefined;
let titlePrefix: string;
if (props.url.indexOf(".onion/") > 0) {
buttonClass = "btn-success";
titlePrefix = "Tor address - ";
} else if (props.url.indexOf("http://") === 0) {
buttonClass = "btn-danger";
titlePrefix = "Not Secure - ";
} else {
buttonClass = undefined;
titlePrefix = "";
}
const hrefTitle = `${encodeURIComponent(props.name)}`;
let href;
if (props.url.indexOf(".tpl") > 0)
{
href = `javascript:window.external.msAddTrackingProtectionList('${encodeURIComponent(props.url)}', '${hrefTitle}')`;
} else if (props.url.indexOf(".lsrules") > 0)
{
href = `x-littlesnitch:subscribe-rules?url=${encodeURIComponent(props.url)}`;
} else if (props.url.indexOf("?hostformat=littlesnitch") > 0)
{
href = `x-littlesnitch:subscribe-rules?url=${encodeURIComponent(props.url)}`;
} else {
href = `abp:subscribe?location=${encodeURIComponent(props.url)}&amp;title=${hrefTitle}`;
};
let title;
if (props.url.indexOf(".tpl") > 0)
{
title = `${titlePrefix}Subscribe to ${props.name} with Internet Explorer's Tracking Protection List feature.`;
} else if (props.url.indexOf(".lsrules") > 0)
{
title = `${titlePrefix}Subscribe to ${props.name} with Little Snitch's rule group subscription feature.`;
} else if (props.url.indexOf("?hostformat=littlesnitch") > 0)
{
title = `${titlePrefix}Subscribe to ${props.name} with Little Snitch's rule group subscription feature.`;
} else {
title = `${titlePrefix}Subscribe to ${props.name} with a browser extension supporting the \"abp:\" protocol (e.g. uBlock Origin, Adblock Plus).`;
};
return props.url
? <LinkButton href={href}
title={title}
buttonClass={buttonClass}
text={props.text || "Subscribe"}/>
: null;
};

View file

@ -0,0 +1,52 @@
import * as React from "react";
import { SubscribeButton } from "./SubscribeButton";
interface IProps {
name: string;
url: string;
urlMirrors?: string[];
};
export const SubscribeButtonGroup = (props: IProps) =>
props.url
? (props.urlMirrors && props.urlMirrors.length > 0)
? <SubscribeButtonGroupDropdown {...props} urlMirrors={props
.urlMirrors}/>
: <SubscribeButton {...props}/>
: null;
interface ISubscribeButtonGroupDropdownProps {
name: string;
url: string;
urlMirrors: string[];
};
const SubscribeButtonGroupDropdown = (props: ISubscribeButtonGroupDropdownProps) => {
let firstButtonText: string = "Original";
let mirrorIndex: number = 0;
if (props.url.indexOf("web.archive.org") !== -1) {
firstButtonText = "Mirror 1";
mirrorIndex++;
}
return <div className="btn-group-vertical dropleft fl-btn-link" role="group">
<BtnGroupDropSubscribe/>
<div className="dropdown-menu" aria-labelledby="btnGroupDropSubscribe">
<SubscribeButton {...props} text={firstButtonText}/>
{props.urlMirrors.map(
(m: string, i: number) =>
<SubscribeButton {...props} url={m} text={`Mirror ${i + 1 + mirrorIndex}`} key={i}/>)}
</div>
</div>;
};
const BtnGroupDropSubscribe = () =>
<button id="btnGroupDropSubscribe"
type="button"
className="btn btn-primary dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
Subscribe
</button>;

View file

@ -0,0 +1,14 @@
import * as React from "react";
import { LinkButton } from "./LinkButton";
interface IProps {
name: string;
twitterHandle: string;
};
export const TwitterButton = (props: IProps) =>
props.twitterHandle
? <LinkButton href={`https://twitter.com/${props.twitterHandle}`}
title={`View ${props.name}'s Twitter profile.`}
text="Twitter"/>
: null;

View file

@ -0,0 +1,24 @@
import * as React from "react";
import { LinkButton } from "./LinkButton";
interface IProps {
name: string;
url: string;
text?: string;
};
export const ViewButton = (props: IProps) => {
let title;
if (props.url.indexOf(".onion/") > 0) { title = `Tor address - View ${props.name} in its raw format.`; }
else if (props.url.indexOf(".zip") > 0) { title = `Download ${props.name} as a ZIP compressed archive.`; }
else if (props.url.indexOf(".7z") > 0) { title = `Download ${props.name} as a 7Z compressed archive.`; }
else if (props.url.indexOf(".tar.gz") > 0) { title = `Download ${props.name} as a compressed tarball archive.`; }
else { title = `View ${props.name} in its raw format`; };
return props.url
? <LinkButton href={props.url}
title={title}
text={props.text || "View"}/>
: null;
}

View file

@ -0,0 +1,51 @@
import * as React from "react";
import { ViewButton } from "./ViewButton";
interface IProps {
name: string;
url: string;
urlMirrors?: string[];
};
export const ViewButtonGroup = (props: IProps) =>
props.url
? (props.urlMirrors && props.urlMirrors.length > 0)
? <ViewButtonGroupDropdown {...props} urlMirrors={props.urlMirrors}/>
: <ViewButton {...props}/>
: null;
interface IViewButtonGroupDropdownProps {
name: string;
url: string;
urlMirrors: string[];
};
const ViewButtonGroupDropdown = (props: IViewButtonGroupDropdownProps) => {
let firstButtonText: string = "Original";
let mirrorIndex: number = 0;
if (props.url.indexOf("web.archive.org") !== -1) {
firstButtonText = "Mirror 1";
mirrorIndex++;
}
return <div className="btn-group-vertical dropleft fl-btn-link" role="group">
<BtnGroupDropView/>
<div className="dropdown-menu" aria-labelledby="btnGroupDropView">
<ViewButton {...props} text={firstButtonText}/>
{props.urlMirrors.map(
(m: string, i: number) =>
<ViewButton {...props} url={m} text={`Mirror ${i + 1 + mirrorIndex}`} key={i}/>)}
</div>
</div>;
};
const BtnGroupDropView = () =>
<button id="btnGroupDropView"
type="button"
className="btn btn-primary dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
View
</button>;

View file

@ -0,0 +1,11 @@
.fl-btn-link {
margin-bottom: .2rem;
max-width: 100px;
width: 100px;
}
.dropdown-menu {
border: 0;
min-width: 0;
padding: 0;
}

View file

@ -0,0 +1,25 @@
import { ChatButton } from "./ChatButton";
import { DonateButton } from "./DonateButton";
import { EmailButton } from "./EmailButton";
import { ForumButton } from "./ForumButton";
import { HomeButton } from "./HomeButton";
import { IssuesButton } from "./IssuesButton";
import { PolicyButton } from "./PolicyButton";
import { SubmitButton } from "./SubmitButton";
import { SubscribeButtonGroup } from "./SubscribeButtonGroup";
import { TwitterButton } from "./TwitterButton";
import { ViewButtonGroup } from "./ViewButtonGroup";
export {
ChatButton,
DonateButton,
EmailButton,
ForumButton,
HomeButton,
IssuesButton,
PolicyButton,
SubmitButton,
SubscribeButtonGroup,
TwitterButton,
ViewButtonGroup
};

View file

@ -0,0 +1,104 @@
import * as React from "react";
import { IColumnVisibility, ILanguage, ILicense, IList, IMaintainer, ISoftware, ISyntax, ITag } from "../../interfaces";
import "../../../../utils/loader.css";
import ReactTable from "react-table";
import "react-table/react-table.css";
import "./listsTable.css";
import {
DetailsButton,
Languages,
License,
Maintainers,
Name,
//RuleCount,
Software,
SubscribeButton,
Tags,
//UpdatedDate
} from "./columns";
import { IListDetails } from "../../components/detailsExpander";
import { DetailsExpander } from "../../components";
interface IProps {
languages: ILanguage[];
licenses: ILicense[];
lists: IList[];
maintainers: IMaintainer[];
software: ISoftware[];
syntaxes: ISyntax[];
tags: ITag[];
columnVisibility: IColumnVisibility[];
pageSize: number;
};
export const ListsTable = (props: IProps) =>
props.languages.length > 0 && props.lists.length > 0 && props.software.length > 0 && props.tags.length > 0
? <ReactTable
data={props.lists}
defaultPageSize={props.pageSize}
showPageSizeOptions={false}
columns={[
Name,
Software(props.columnVisibility, props.software),
Languages(props.columnVisibility, props.languages),
Tags(props.columnVisibility, props.tags),
//UpdatedDate(props.columnVisibility),
//RuleCount(props.columnVisibility),
License(props.columnVisibility, props.licenses),
Maintainers(props.columnVisibility, props.maintainers),
SubscribeButton(props.columnVisibility),
DetailsButton
]}
defaultSorted={[{ id: "name" }]}
SubComponent={(r: any) =>
<DetailsExpander columnVisibility={props.columnVisibility}
list={mapListDetails(({
list: r.original,
languages: props.languages,
licenses: props.licenses,
maintainers: props.maintainers,
syntaxes: props.syntaxes,
tags: props.tags
} as ICreateListDtoProps))}
software={props.software}/>}
className="-striped -highlight"/>
: <div className="loader">Loading...</div>;
interface ICreateListDtoProps {
list: IList;
languages: ILanguage[];
licenses: ILicense[];
maintainers: IMaintainer[];
syntaxes: ISyntax[];
tags: ITag[];
};
const mapListDetails = (props: ICreateListDtoProps): IListDetails =>
({
id: props.list.id,
chatUrl: props.list.chatUrl,
description: props.list.description,
descriptionSourceUrl: props.list.descriptionSourceUrl,
donateUrl: props.list.donateUrl,
emailAddress: props.list.emailAddress,
forumUrl: props.list.forumUrl,
homeUrl: props.list.homeUrl,
issuesUrl: props.list.issuesUrl,
languages: props.list.languageIds
? props.languages.filter((l: ILanguage) => props.list.languageIds.indexOf(l.id) > -1)
: undefined,
license: props.licenses.filter((l: ILicense) => props.list.licenseId === l.id)[0],
maintainers: props.list.maintainerIds
? props.maintainers.filter((m: IMaintainer) => props.list.maintainerIds.indexOf(m.id) > -1)
: undefined,
name: props.list.name,
policyUrl: props.list.policyUrl,
publishedDate: props.list.publishedDate,
//ruleCount: props.list.ruleCount,
submissionUrl: props.list.submissionUrl,
syntax: props.syntaxes.filter((s: ISyntax) => props.list.syntaxId === s.id)[0],
tags: props.list.tagIds ? props.tags.filter((t: ITag) => props.list.tagIds.indexOf(t.id) > -1) : undefined,
//updatedDate: props.list.updatedDate,
viewUrl: props.list.viewUrl,
viewUrlMirrors: props.list.viewUrlMirrors
} as IListDetails);

View file

@ -0,0 +1,26 @@
import * as React from "react";
import { Column, RowRenderProps } from "react-table";
import "../../linkButtons/button.css";
export const DetailsButton = {
Header: <span title="Learn more about the FilterList.">Details</span>,
accessor: "id",
sortable: false,
expander: true,
Expander: ({ isExpanded, row }: RowRenderProps) => Expander({ isExpanded, row }),
style: { textAlign: "center" },
width: 110
} as Column;
const Expander = (props: RowRenderProps) =>
<div>
{props.isExpanded
? <button className="btn btn-primary btn-block active fl-btn-link"
title={`Collapse details about ${props.row.name}.`}>
Details
</button>
: <button className="btn btn-primary btn-block fl-btn-link"
title={`Learn more about ${props.row.name}.`}>
Details
</button>}
</div>;

View file

@ -0,0 +1,71 @@
import * as React from "react";
import { Column, Filter } from "react-table";
import { IColumnVisibility, ILanguage } from "../../../interfaces";
export const Languages = (columnVisibility: IColumnVisibility[], languages: ILanguage[]) => {
const languagesSorted = languages.sort((a, b) => a.name.localeCompare(b.name));
return ({
Header: <span title="Written forms of communication used by sites targeted by the FilterList.">Languages</span>,
accessor: "languageIds",
filterable: true,
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r),
Filter: ({ onChange, filter }: any) => Filter({ onChange, filter }, languagesSorted),
sortMethod: (a: number[], b: number[]) => sortMethod(a, b, languagesSorted),
Cell: (c: any) => Cell(c.value, languagesSorted),
style: { whiteSpace: "inherit" },
width: 95,
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Languages")[0].visible
} as Column);
};
const filterMethod = (f: Filter, r: any[]): boolean => {
const listLanguageIds = r[f.id as any];
return f.value === "any" ||
(listLanguageIds
? listLanguageIds.join(",").split(",").includes(f.value)
: f.value === "none");
};
const Filter = (props: any, languages: ILanguage[]) =>
<select onChange={(event: any) => props.onChange(event.target.value)}
style={{ width: "100%" }}
value={props.filter ? props.filter.value : "any"}>
<option value="any">Any</option>
<option value="none">None</option>
{languages.length > 0
? languages.map((l: ILanguage, i: number) =>
<option value={l.id} key={i}>
{l.name} ({l.filterListIds ? l.filterListIds.length : 0})
</option>)
: null}
</select>;
const sortMethod = (a: number[], b: number[], languages: ILanguage[]) => {
if (a && a.length > 0) {
if (b && b.length > 0) {
const aLanguageNames =
languages.filter((l: ILanguage) => a.indexOf(l.id) > -1).map((l: ILanguage) => l.name).join();
const bLanguageNames =
languages.filter((l: ILanguage) => b.indexOf(l.id) > -1).map((l: ILanguage) => l.name).join();
return aLanguageNames.toLowerCase() > bLanguageNames.toLowerCase() ? 1 : -1;
} else {
return -1;
}
} else {
return 1;
}
};
const Cell = (languageIds: number[], languages: ILanguage[]) =>
languageIds
? <div className="fl-wrap-cell">
{languageIds.map((id: number, i: number) => {
const language = languages.filter((l: ILanguage) => l.id === id)[0];
return <span className="badge badge-secondary"
title={language.name}
key={i}>
{language.iso6391}
</span>;
})}
</div>
: null;

View file

@ -0,0 +1,68 @@
import * as React from "react";
import { Column, Filter } from "react-table";
import { IColumnVisibility, ILicense } from "../../../interfaces";
export const License = (columnVisibility: IColumnVisibility[], licenses: ILicense[]) =>
({
Header: <span title="A legal document governing the use or redistribution of a FilterList.">License</span>,
accessor: "licenseId",
filterable: true,
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r, licenses),
Filter: ({ filter, onChange }: any) => Filter({ onChange, filter }, licenses),
sortMethod: (a: number, b: number) => sortMethod(a, b, licenses),
Cell: (c: any) => Cell(c.value, licenses),
width: 75,
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "License")[0].visible
} as Column);
const filterMethod = (f: Filter, r: any[], licenses: ILicense[]): boolean => {
const listLicenseId: number = r[f.id as any];
if (f.value === "any") {
return true;
} else if (listLicenseId) {
const licenseFiltered = licenses.filter((l: ILicense) => l.id === parseInt(f.value))[0];
return listLicenseId === licenseFiltered.id;
} else {
return false;
}
};
const Filter = (props: any, licenses: ILicense[]) =>
<select
onChange={(event: any) => props.onChange(event.target.value)}
style={{ width: "100%" }}
value={props.filter ? props.filter.value : "any"}>
<option value="any">Any</option>
{licenses.length > 0
? licenses.sort((a, b) => a.name.replace(/['"]+/g, "").localeCompare(b.name.replace(/['"]+/g, ""))).map(
(l: ILicense, i: number) =>
<option value={l.id} key={i}>
{l.name} ({l.filterListIds ? l.filterListIds.length : 0})
</option>)
: null}
</select>;
const sortMethod = (a: number, b: number, licenses: ILicense[]) => {
if (a) {
if (b) {
const aLicenseName = licenses.filter((l: ILicense) => l.id === a)[0].name.replace(/['"]+/g, "");
const bLicenseName = licenses.filter((l: ILicense) => l.id === b)[0].name.replace(/['"]+/g, "");
return aLicenseName.toLowerCase() > bLicenseName.toLowerCase() ? 1 : -1;
} else {
return -1;
}
} else {
return 1;
}
};
const Cell = (licenseId: number, licenses: ILicense[]) => {
const license = licenses.filter((l: ILicense) => licenseId === l.id)[0];
return license
? <div>
<span title={license.name}>
{license.descriptionUrl ? <a href={license.descriptionUrl}>{license.name}</a> : license.name}
</span>
</div>
: null;
};

View file

@ -0,0 +1,68 @@
import * as React from "react";
import { Column, Filter } from "react-table";
import { IColumnVisibility, IMaintainer } from "../../../interfaces";
export const Maintainers = (columnVisibility: IColumnVisibility[], maintainers: IMaintainer[]) =>
({
Header: <span title="Individuals, groups, or organizations who maintain the FilterList.">Maintainers</span>,
accessor: "maintainerIds",
filterable: true,
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r, maintainers),
Filter: ({ filter, onChange }: any) => Filter({ onChange, filter }, maintainers),
sortMethod: (a: number[], b: number[]) => sortMethod(a, b, maintainers),
Cell: (c: any) => Cell(c.value, maintainers),
width: 140,
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Maintainers")[0].visible
} as Column);
const filterMethod = (f: Filter, r: any[], maintainers: IMaintainer[]): boolean => {
const listMaintainerIds: number[] = r[f.id as any];
return f.value === "any" ||
(listMaintainerIds
? f.value === "none"
? false
: listMaintainerIds.indexOf(maintainers.filter((m: IMaintainer) => m.id === parseInt(f.value))[0].id) > -1
: f.value === "none");
};
const Filter = (props: any, maintainers: IMaintainer[]) =>
<select
onChange={(event: any) => props.onChange(event.target.value)}
style={{ width: "100%" }}
value={props.filter ? props.filter.value : "any"}>
<option value="any">Any</option>
<option value="none">None</option>
{maintainers.length > 0
? maintainers.sort((a, b) => a.name.localeCompare(b.name))
.map((m: IMaintainer, i: number) =>
<option value={m.id} key={i}>
{m.name} ({m.filterListIds ? m.filterListIds.length : 0})
</option>)
: null}
</select>;
const sortMethod = (a: number[], b: number[], maintainers: IMaintainer[]) => {
if (a && a.length > 0) {
if (b && b.length > 0) {
const aFirstMaintainerName = maintainers.filter((m: IMaintainer) => m.id === a[0])[0].name;
const bFirstMaintainerName = maintainers.filter((m: IMaintainer) => m.id === b[0])[0].name;
return aFirstMaintainerName.toLowerCase() > bFirstMaintainerName.toLowerCase() ? 1 : -1;
} else {
return -1;
}
} else {
return 1;
}
};
const Cell = (maintainerIds: number[], maintainers: IMaintainer[]) =>
maintainerIds
? <div className="fl-wrap-cell">
{maintainers.filter((m: IMaintainer) => maintainerIds.indexOf(m.id) > -1)
.map((m: IMaintainer, i: number) => {
return m.homeUrl
? <a href={m.homeUrl} key={i}>{m.name}</a>
: <span key={i}>{m.name}</span>;
}).reduce(((prev: JSX.Element, curr: JSX.Element): any => [prev, ", ", curr]) as any)}
</div>
: null;

View file

@ -0,0 +1,22 @@
import * as React from "react";
import { Column, Filter } from "react-table";
export const Name = {
Header: <span title="The name/title of the FilterList in title case.">Name</span>,
accessor: "name",
filterable: true,
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r),
sortMethod: (a: string, b: string) => sortMethod(a, b),
Cell: (c: any) => Cell(c.value)
} as Column;
const filterMethod = (f: Filter, r: any[]): boolean =>
r[f.id as any].toUpperCase().includes(f.value.toUpperCase());
const sortMethod = (a: string, b: string) =>
a.toLowerCase() > b.toLowerCase() ? 1 : -1;
const Cell = (name: string) =>
name
? <h2 className="mb-0">{name}</h2>
: null;

View file

@ -0,0 +1,28 @@
import * as React from "react";
import { Column } from "react-table";
import { IColumnVisibility } from "../../../interfaces";
export const RuleCount = (columnVisibility: IColumnVisibility[]) =>
({
Header: <span title="The number of rules in the FilterList.">Rules</span>,
accessor: "ruleCount",
sortMethod: (a: string, b: string) => sortMethod(a, b),
Cell: (c: any) => Cell(c.value),
style: { whiteSpace: "inherit" },
width: 85,
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Rules")[0].visible
} as Column);
const sortMethod = (a: string, b: string) =>
a
? b
? a > b
? -1
: 1
: -1
: 1;
const Cell = (ruleCount: number) =>
ruleCount
? <span>{ ruleCount.toLocaleString() }</span>
: null;

View file

@ -0,0 +1,74 @@
import * as React from "react";
import { Column, Filter } from "react-table";
import { IColumnVisibility, ISoftware } from "../../../interfaces";
import { SoftwareIcon } from "../../softwareIcon";
export const Software = (columnVisibility: IColumnVisibility[], software: ISoftware[]) => {
const softwareSorted = software.sort((a, b) => a.name.localeCompare(b.name));
return ({
Header:
<span title="Applications, browser extensions, or other utilities that consume the FilterList.">Software</span>,
accessor: "syntaxId",
filterable: true,
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r, softwareSorted),
Filter: ({ filter, onChange }: any) => Filter({ onChange, filter }, softwareSorted),
sortMethod: (a: number, b: number) => sortMethod(a, b, softwareSorted),
Cell: (c: any) => Cell(c.value, software),
width: 155,
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Software")[0].visible
} as Column);
};
const filterMethod = (f: Filter, r: any[], software: ISoftware[]): boolean => {
const isAny = f.value === "any";
const softwareFiltered = isAny ? software : software.filter((s: ISoftware) => s.id === parseInt(f.value));
const listSyntaxId: number = r[f.id as any];
const isMatch = softwareFiltered[0].syntaxIds ? softwareFiltered[0].syntaxIds.indexOf(listSyntaxId) > -1 : false;
return isAny || (listSyntaxId ? isMatch : false);
};
const Filter = (props: any, software: ISoftware[]) =>
<select
onChange={(event: any) => props.onChange(event.target.value)}
style={{ width: "100%" }}
value={props.filter ? props.filter.value : "any"}>
<option value="any">Any</option>
{software.length > 0
? software.map((s: ISoftware, i: number) => <option value={s.id} key={i}>{s.name}</option>)
: null}
</select>;
const sortMethod = (a: number, b: number, software: ISoftware[]) => {
if (a) {
if (b) {
const aSoftwareNames =
software.filter((s: ISoftware) => s.syntaxIds.indexOf(a) > -1).map((s: ISoftware) => s.name);
const bSoftwareNames =
software.filter((s: ISoftware) => s.syntaxIds.indexOf(b) > -1).map((s: ISoftware) => s.name);
if (aSoftwareNames.length === bSoftwareNames.length) {
return aSoftwareNames.join().toLowerCase() > bSoftwareNames.join().toLowerCase() ? 1 : -1;
} else if (aSoftwareNames.length > bSoftwareNames.length) {
return -1;
} else {
return 1;
}
} else {
return -1;
}
} else {
return 1;
}
};
const Cell = (listSyntaxId: number, software: ISoftware[]) =>
listSyntaxId
? <div className="fl-wrap-cell">
{software.filter((s: ISoftware) => s.syntaxIds.indexOf(listSyntaxId) > -1)
.map((s: ISoftware, i: number) =>
s.homeUrl
? <a href={s.homeUrl} key={i}>
<SoftwareIcon id={s.id}/>
</a>
: <SoftwareIcon id={s.id} key={i}/>)}
</div>
: null;

View file

@ -0,0 +1,24 @@
import * as React from "react";
import { Column, Filter } from "react-table";
import { IColumnVisibility } from "../../../interfaces";
import { SubscribeButtonGroup } from "../../linkButtons";
export const SubscribeButton = (columnVisibility: IColumnVisibility[]) => ({
Header: <span title={`Subscribe to the list with a browser extension supporting the "abp:" protocol (e.g. uBlock Origin, Adblock Plus)`}>
Subscribe
</span>,
accessor: "viewUrl",
filterable: true,
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r),
sortMethod: (a: string, b: string) => sortMethod(a, b),
Cell: (c: any) => <SubscribeButtonGroup name={c.row.name} url={c.value} urlMirrors={c.row.viewUrlMirrors}/>,
style: { textAlign: "center" },
width: 110,
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Subscribe")[0].visible
} as Column);
const filterMethod = (f: Filter, r: any[]): boolean =>
r[f.id as any].toUpperCase().includes(f.value.toUpperCase());
const sortMethod = (a: string, b: string) =>
a.toLowerCase() > b.toLowerCase() ? 1 : -1;

View file

@ -0,0 +1,46 @@
import * as React from "react";
import { Column, Filter } from "react-table";
import { IColumnVisibility, ISyntax } from "../../../interfaces";
//TODO: https://github.com/collinbarrett/FilterLists/issues/488
export const Syntax = (columnVisibility: IColumnVisibility[], syntaxes: ISyntax[]) => ({
Header: <span title="A named set of rules that govern the format of the FilterList.">Syntax</span>,
accessor: "syntaxId",
filterable: true,
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r, syntaxes),
Filter: ({ filter, onChange }: any) => Filter({ onChange, filter }, syntaxes),
Cell: (c: any) => Cell(c.value, syntaxes),
width: 155,
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Syntax")[0].visible
} as Column);
const filterMethod = (f: Filter, r: any[], syntaxes: ISyntax[]): boolean => {
const listSyntaxId: number = r[f.id as any];
if (f.value === "any") {
return true;
} else if (listSyntaxId) {
const syntaxFiltered = syntaxes.filter((s: ISyntax) => s.id === parseInt(f.value));
return syntaxFiltered[0].id
? syntaxFiltered[0].id === listSyntaxId
: false;
} else {
return false;
}
};
const Filter = (props: any, syntaxes: ISyntax[]) =>
<select
onChange={(event: any) => props.onChange(event.target.value)}
style={{ width: "100%" }}
value={props.filter ? props.filter.value : "any"}>
<option value="any">Any</option>
{syntaxes.length > 0
? syntaxes.sort((a, b) => a.name.localeCompare(b.name)).map(
(s: ISyntax, i: number) => <option value={s.id} key={i}>{s.name}</option>)
: null}
</select>;
const Cell = (listSyntaxId: number, syntaxes: ISyntax[]) =>
listSyntaxId
? syntaxes.filter((s: ISyntax) => s.id === listSyntaxId)[0].name
: null;

View file

@ -0,0 +1,63 @@
import * as React from "react";
import { Column, Filter } from "react-table";
import { TagGroup } from "../../TagGroup";
import { IColumnVisibility, ITag } from "../../../interfaces";
export const Tags = (columnVisibility: IColumnVisibility[], tags: ITag[]) => {
const tagsSorted = tags.sort((a: ITag, b: ITag) => a.name.localeCompare(b.name));
return ({
Header:
<span title="Generic taxonomies applied to the FilterList to provide information about its contents and/or purpose.">Tags</span>,
accessor: "tagIds",
filterable: true,
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r),
Filter: ({ onChange, filter }: any) => Filter({ onChange, filter }, tagsSorted),
sortMethod: (a: number[], b: number[]) => sortMethod(a, b, tagsSorted),
Cell: (c: any) => Cell(c.value, tagsSorted),
width: 260,
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Tags")[0].visible
} as Column);
};
const filterMethod = (f: Filter, r: any[]): boolean => {
const listTagIds = r[f.id as any];
return f.value === "any" ||
(listTagIds
? listTagIds.join(",").split(",").includes(f.value)
: f.value === "none");
};
const Filter = (props: any, tags: ITag[]) =>
<select onChange={(event: any) => props.onChange(event.target.value)}
style={{ width: "100%" }}
value={props.filter ? props.filter.value : "any"}>
<option value="any">Any</option>
<option value="none">None</option>
{tags.length > 0
? tags.map((t: ITag, i: number) =>
<option value={t.id} title={t.description} key={i}>
{t.name} ({t.filterListIds ? t.filterListIds.length : 0})
</option>)
: null}
</select>;
const sortMethod = (a: number[], b: number[], tags: ITag[]): any => {
return a
? b
? a.length === b.length
? tags.filter((t: ITag) => a.indexOf(t.id) > -1).map((t: ITag) => t.name).join()
.toLowerCase() >
tags.filter((t: ITag) => b.indexOf(t.id) > -1).map((t: ITag) => t.name).join().toLowerCase()
? 1
: -1
: a.length > b.length
? -1
: 1
: -1
: 1;
};
const Cell = (tagIds: number[], tags: ITag[]) =>
tagIds
? <TagGroup tags={tags.filter((t: ITag) => tagIds.indexOf(t.id) > -1)}/>
: null;

View file

@ -0,0 +1,33 @@
import * as React from "react";
import { Column } from "react-table";
import * as moment from "moment";
import { IColumnVisibility } from "../../../interfaces";
export const UpdatedDate = (columnVisibility: IColumnVisibility[]) =>
({
Header: <span title="The estimated date that the FilterList was last updated by the maintainer.">Updated</span>,
accessor: "updatedDate",
sortMethod: (a: string, b: string) => sortMethod(a, b),
Cell: (c: any) => Cell(c.value),
style: { whiteSpace: "inherit" },
width: 100,
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Updated")[0].visible
} as Column);
const sortMethod = (a: string, b: string) =>
a && moment(a).isValid()
? (b && moment(b).isValid()
? (moment(a).isBefore(b)
? 1
: -1)
: -1)
: 1;
const Cell = (updatedDate: string) =>
updatedDate
? <div>
{moment(updatedDate).isValid()
? moment(updatedDate).format("l")
: null}
</div>
: null;

View file

@ -0,0 +1,25 @@
import { DetailsButton } from "./DetailsButton";
import { Languages } from "./Languages";
import { License } from "./License";
import { Maintainers } from "./Maintainers";
import { Name } from "./Name";
import { RuleCount } from "./RuleCount";
import { Software } from "./Software";
import { SubscribeButton } from "./SubscribeButton";
import { Syntax } from "./Syntax";
import { Tags } from "./Tags";
import { UpdatedDate } from "./UpdatedDate";
export {
DetailsButton,
Languages,
License,
Maintainers,
Name,
RuleCount,
Software,
SubscribeButton,
Syntax,
Tags,
UpdatedDate
};

View file

@ -0,0 +1,5 @@
import { ListsTable } from "./ListsTable";
export {
ListsTable,
};

View file

@ -0,0 +1,13 @@
.rt-tr {
-ms-align-items: center;
-o-align-items: center;
-webkit-align-items: center;
align-items: center;
}
div.rt-tbody, div.rt-thead.-filters, div.rt-thead.-header { min-width: 320px !important; }
.fl-wrap-cell {
line-height: 1;
white-space: normal;
}

View file

@ -0,0 +1,89 @@
import * as React from "react";
import {
img1,
img2,
img3,
img4,
img5,
img6,
img7,
img8,
img10,
img11,
img12,
img13,
img14,
img15,
img16,
img17,
img18,
img19,
img20,
img21,
img22,
img23,
img24,
img25,
img26,
img27,
img28,
img29,
img30,
img31,
img32,
img33,
img34
} from "./imgs";
interface IProps {
id: number;
};
export const SoftwareIcon = (props: IProps) =>
icons[props.id]
? <img src={icons[props.id].image}
height="20"
alt={icons[props.id].imageTitle}
title={icons[props.id].imageTitle}/>
: null;
interface IIcon {
image: any;
imageTitle: string;
};
const icons: { [id: number]: IIcon; } = {
1: { image: img1, imageTitle: "uBlock Origin" },
2: { image: img2, imageTitle: "Adblock Plus" },
3: { image: img3, imageTitle: "AdGuard" },
4: { image: img4, imageTitle: "DNS66" },
5: { image: img5, imageTitle: "Nano Adblocker" },
6: { image: img6, imageTitle: "AdBlock" },
7: { image: img7, imageTitle: "AdAway" },
8: { image: img8, imageTitle: "Personal Blocklist" },
10: { image: img10, imageTitle: "Redirector" },
11: { image: img11, imageTitle: "Hosts File Editor" },
12: { image: img12, imageTitle: "Gas Mask" },
13: { image: img13, imageTitle: "MinerBlock" },
14: { image: img14, imageTitle: "Pi-hole" },
15: { image: img15, imageTitle: "uBlock" },
16: { image: img16, imageTitle: "Internet Explorer (TPL)" },
17: { image: img17, imageTitle: "Google Hit Hider by Domain" },
18: { image: img18, imageTitle: "FireHOL" },
19: { image: img19, imageTitle: "Samsung Knox" },
20: { image: img20, imageTitle: "Little Snitch" },
21: { image: img21, imageTitle: "Privoxy" },
22: { image: img22, imageTitle: "Diversion" },
23: { image: img23, imageTitle: "dnsmasq" },
24: { image: img24, imageTitle: "Slimjet" },
25: { image: img25, imageTitle: "uMatrix" },
26: { image: img26, imageTitle: "Blokada" },
27: { image: img27, imageTitle: "hostsmgr" },
28: { image: img28, imageTitle: "personalDNSfilter" },
29: { image: img29, imageTitle: "Unbound" },
30: { image: img30, imageTitle: "BIND" },
31: { image: img31, imageTitle: "AdGuard Home" },
32: { image: img32, imageTitle: "AdNauseam" },
33: { image: img33, imageTitle: "Legacy Unix derivatives" },
34: { image: img34, imageTitle: "Windows command line" },
};

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="8.5 1 111.5 127" version="1"><g fill="maroon" stroke="#fff" stroke-linecap="round" stroke-width="2"><path stroke-linejoin="round" d="M448 669c-81-57-81-57-81-200 35 0 46 0 81-28m0 228c80-57 80-57 80-200-34 0-46 0-80-28" transform="matrix(-.69452 0 0 .5611 375 -247)"/></g><g stroke="#fff" transform="translate(-18 -17)"><ellipse cx="102" cy="81" fill="none" stroke-width="6" rx="12" ry="12" transform="matrix(1.33333 0 0 1.33333 -42 -31)"/><path fill="#fff" stroke="none" stroke-width="2" d="M82 81c0 11-5 16-16 16s-16-5-16-16V57h8v24c0 7 1 8 8 8s8-1 8-8V57h8z"/></g></svg>

After

Width:  |  Height:  |  Size: 623 B

View file

@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" width="619.25" height="620.5"
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" viewBox="0 0 619.25001 620.49998">
<defs>
<linearGradient osb:paint="solid">
<stop offset="0" stop-color="#fff" />
</linearGradient>
</defs>
<g>
<path fill="#fff" d="M44.45 196.24h525.28V422.5H44.45z" />
<path fill="#fff" stroke="#dadbdb" stroke-width="3"
d="M91.8 528.68l-90.3-90.3V182.1l90.32-90.3 90.3-90.32h255l90.32 90.32 90.3 90.3V438.4l-90.3 90.3L437.1 619h-255l-90.3-90.32zm416.26-20.6l82.2-82.2V194.62l-82.2-82.2-82.2-82.17H194.6l-82.18 82.2-82.16 82.17v231.25l82.17 82.2 82.2 82.18h231.24l82.2-82.18z" />
<path fill="#e00000" stroke="#cb0000" stroke-width="3.004"
d="M111.94 508.64l-81.77-81.56V193.33l81.77-81.56 81.78-81.56h231.82l82.4 82.2 82.4 82.18V425.85l-82.4 82.18-82.4 82.2H193.7l-81.76-81.58zm231.4-94.2c31.23-6.7 48.93-26.18 51.1-56.18 1.5-20.3-4.52-37.73-16.16-46.9-5.57-4.4-17.6-9.9-21.63-9.9-3.28 0-3.16-2.23.18-3.06 10.03-2.5 21.35-14.97 25.48-28 3.1-9.74 3.4-28 .67-37.2-2.66-8.93-10.25-19.1-18.23-24.46-14.4-9.64-31.54-12.17-83-12.24l-32.26-.04v220.62l42.3-.3c33.1-.26 44.3-.77 51.57-2.33zm-50-64.74v-28.24h16.7c18.67 0 27.14 1.8 33.4 7.05 6.02 5.06 7.95 9.92 7.96 20.07 0 13-3.66 19.7-13.55 24.76-7.03 3.6-8.16 3.77-25.93 4.18l-18.58.44V349.7zm0-88.86V236.2l16.6.46c18.4.5 24.16 2.27 29.06 8.96 3.94 5.38 3.73 22.13-.36 28.7-4.98 8-10.8 10.1-29.32 10.66l-15.98.48v-24.62zm-192.5 134.68c2.58-11.5 5.24-23.05 5.9-25.62l1.22-4.7h57.35l2.74 12.2c1.5 6.7 4.14 18.23 5.85 25.62l3.1 13.44h23.22c18.02 0 23.1-.35 22.67-1.56-.3-.86-13.72-50.08-29.84-109.38l-29.3-107.8h-52.8l-3.4 12.8c-4.82 18.05-54.73 201.1-55.57 203.75-.64 2.03.95 2.2 21.72 2.2h22.4l4.7-20.95zm16.3-72.5c1.8-5.94 8.9-37.52 13.74-60.94 3.24-15.7 5.72-25.24 6.2-23.75.42 1.38 3.04 13.2 5.8 26.25 2.78 13.07 6.96 32.05 9.3 42.2l4.24 18.43h-19.97c-18.44 0-19.93-.16-19.32-2.18zm350.4 56.56V342.7h17.43c21.16 0 33.8-2.2 46.23-8.1 17.52-8.3 31.24-26.2 35.42-46.27 2.25-10.78 1.95-33.3-.57-43.12-6.4-24.85-22.82-40.23-48.65-45.5-7.68-1.58-18.28-2-51.7-2h-42.03v218.76h43.86v-36.88zm0-109.37v-32.5h16.68c19.82 0 27.7 1.87 33.87 8.02 5.55 5.55 7.54 13.1 6.83 26.04-1.3 23.42-11.5 30.95-41.96 30.95h-15.44v-32.5z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 893 B

View file

@ -0,0 +1,5 @@
<svg viewBox="0 0 25.3 25.3" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#68bc71" d="M12.6512986,0 C8.69689131,0 3.92688756,0.929914894 3.87976037e-06,2.9767234 C3.87976037e-06,7.3972766 -0.0542007083,18.4102128 12.6512986,25.935 C25.3570787,18.4102128 25.3031549,7.3972766 25.3031549,2.9767234 C21.3759904,0.929914894 16.6059867,0 12.6512986,0 L12.6512986,0 Z"/>
<path fill="#67b279" d="M12.6383754,25.9273425 C-0.0541639467,18.4027433 3.87976037e-06,7.39577733 3.87976037e-06,2.9767234 C3.9226108,0.932144068 8.68650231,0.00202686119 12.6383754,3.30925217e-06 L12.6383754,25.9273451 Z"/>
<path fill="#fff" d="M12.1898292,17.3046764 L19.8402793,6.99357039 C19.2796699,6.54422358 18.7879353,6.86136327 18.517241,7.10689078 L18.5073636,7.10767773 L12.1284291,13.7434783 L9.72501633,10.8511853 C8.57843655,9.52649092 7.01967536,10.536931 6.65554618,10.8039684 L12.1898292,17.3046764"/>
</svg>

After

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

View file

@ -0,0 +1,34 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="128.000000pt" height="128.000000pt" viewBox="0 0 128.000000 128.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.15, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,128.000000) scale(0.100000,-0.100000)"
fill="#bf3d27" stroke="none">
<path
d="M500 1204 c-321 -86 -507 -413 -413 -727 43 -146 165 -292 295 -355
327 -159 712 7 817 352 25 82 28 233 6 316 -53 199 -220 364 -420 415 -74 19
-213 18 -285 -1z m242 -85 c222 -46 388 -249 388 -473 0 -138 -46 -251 -140
-346 -242 -243 -640 -175 -786 135 -171 361 147 766 538 684z" />
<path
d="M507 1060 c-157 -50 -270 -183 -299 -350 -43 -250 146 -490 400 -507 166 -12 309 57 403 194 136 197 75 488 -129 617 -104 66 -256 84 -375 46z m133 -190 c0 26z"
fill="white" />
<path
d="M510 1072 c-78 -26 -116 -48 -167 -95 -117 -107 -171 -263 -144 -412
42 -230 246 -390 475 -372 188 14 345 136 397 307 27 91 24 228 -9 310 -44
111 -143 209 -252 251 -78 31 -226 36 -300 11z m86 -264 c-20 -29 -36 -60 -36
-68 0 -9 19 -35 43 -58 l44 -43 46 50 c26 28 47 53 47 56 0 3 -18 33 -40 66
-23 32 -37 59 -33 59 5 0 32 -16 61 -36 42 -27 59 -34 81 -29 31 7 43 -8 34
-43 -3 -13 8 -39 31 -73 20 -30 36 -57 36 -62 0 -4 -27 10 -59 33 -33 22 -63
40 -66 40 -11 0 -95 -83 -95 -94 0 -6 34 -45 76 -87 67 -67 73 -77 59 -89 -14
-12 -26 -3 -96 66 l-79 79 -78 -78 c-79 -79 -98 -89 -109 -61 -4 11 19 40 73
95 l78 79 -38 39 c-21 22 -47 41 -57 44 -11 3 -40 -10 -74 -35 -31 -22 -58
-38 -60 -36 -2 2 13 30 35 62 30 47 37 64 30 81 -11 30 5 45 40 38 21 -4 41 4
81 31 29 19 55 34 57 32 2 -3 -12 -28 -32 -58z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 88.32 129.93" preserveAspectRatio="xMinYMid"><defs><style>.cls-1{fill:url(#New_Gradient_Swatch_1);}.cls-2{fill:#980200;}.cls-3{fill:red;}</style><linearGradient id="New_Gradient_Swatch_1" x1="2.71" y1="20.04" x2="69.77" y2="20.04" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#12b212"/><stop offset="1" stop-color="lime"/></linearGradient></defs><title>NewVortex</title><g id="Layer_2" data-name="Layer 2"><g id="RedBerry"><path id="Leaf_Path" data-name="Leaf Path" class="cls-1" d="M36.56,39.93m0,0C20.34,38.2,4,25.94,2.71,0,27.88,0,41.34,14.9,42.64,38.51c4.76-28.32,27.07-25,27.07-25,1.06,16.05-12.12,25.78-27.07,26.59C38.44,31.25,13.28,9.54,13.28,9.54a.07.07,0,0,0-.11.08S37.45,30.77,36.56,39.93"/><path id="LeftBerry" class="cls-2" d="M44.16,129.93c-1.57-.09-16.22-.65-17.11-17.11-.72-10,7.18-17.37,7.18-27.08C32.44,61.53,0,64.53,0,85.74H0A19.94,19.94,0,0,0,5.83,99.88L30,124.06a19.94,19.94,0,0,0,14.14,5.83"/><path id="BottomBerry" class="cls-3" d="M88.32,85.75c-.09,1.57-.65,16.22-17.11,17.11-10,.72-17.38-7.18-27.08-7.18-24.21,1.79-21.21,34.22,0,34.22h0a19.94,19.94,0,0,0,14.14-5.83L82.46,99.9a19.94,19.94,0,0,0,5.83-14.14"/><path id="RightBerry" class="cls-2" d="M44.16,41.59c1.57.09,16.22.65,17.11,17.11.72,10-7.18,17.37-7.18,27.08,1.79,24.21,34.22,21.21,34.22,0h0a19.94,19.94,0,0,0-5.83-14.14L58.3,47.45a19.94,19.94,0,0,0-14.14-5.83"/><path id="TopBerry" class="cls-3" d="M.08,85.75c.09-1.57.65-16.22,17.11-17.11,10-.72,17.38,7.18,27.08,7.18C68.48,74,65.48,41.6,44.27,41.6h0a19.94,19.94,0,0,0-14.14,5.83L5.94,71.61A19.94,19.94,0,0,0,.11,85.75"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a">
<stop offset="0" /><stop offset="1" stop-opacity="0" />
</linearGradient>
<radialGradient cx="82.145" cy="81.055" r="22.573" xlink:href="#a" fx="82.145" fy="81.055"
gradientTransform="matrix(1 0 0 1.258 0 -20.947)" gradientUnits="userSpaceOnUse" />
</defs>
<g stroke="#fff">
<path fill="#800000" stroke-width="3" d="M1.5 36.78v54.44l35.28 35.28h54.44l35.28-35.28V36.78L91.22 1.5H36.78z" />
<g fill="#fff" stroke-width="2" style="text-align:center;line-height:125%;-inkscape-font-specification:Ubuntu"
font-size="100.219" letter-spacing="0" word-spacing="0" text-anchor="middle" font-family="Ubuntu">
<path
d="M83.97 90.23c-2.03.56-4.7 1.16-8.05 1.8-3.33.62-7.2.94-11.57.94-3.8 0-7-.62-9.6-1.85-2.6-1.23-4.7-2.97-6.3-5.22-1.58-2.25-2.72-4.9-3.42-7.96-.7-3.05-1.05-6.44-1.05-10.17V37h8.85v28.66c0 6.68.96 11.46 2.86 14.34 1.9 2.88 5.1 4.32 9.6 4.32.96 0 1.94-.03 2.96-.1 1-.07 1.97-.16 2.85-.27.9-.1 1.7-.2 2.44-.3.73-.12 1.26-.24 1.57-.38V37h8.87z"
style="-inkscape-font-specification:Ubuntu" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1 @@
<svg viewBox="0 0 256 252" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M256 131.593c0-20.238-5.232-39.25-14.408-55.772 39.11-88.514-41.906-75.55-46.417-74.667-17.164 3.359-33.044 8.751-47.691 15.587-2.16-.122-4.334-.189-6.523-.189-54.665 0-100.423 38.128-112.134 89.241C57.639 73.47 77.802 60.428 89.877 55.21a274.318 274.318 0 0 0-5.67 5.218c-.618.583-1.213 1.175-1.822 1.761a284.903 284.903 0 0 0-3.638 3.545c-.71.707-1.4 1.42-2.1 2.133a307.038 307.038 0 0 0-3.145 3.235c-.734.77-1.45 1.541-2.17 2.312a286.466 286.466 0 0 0-2.865 3.104c-.73.807-1.45 1.613-2.168 2.422a319.307 319.307 0 0 0-4.796 5.52 331.84 331.84 0 0 0-2.45 2.927c-.714.864-1.426 1.726-2.125 2.589a341.74 341.74 0 0 0-2.234 2.794c-.716.902-1.431 1.802-2.13 2.703-.666.857-1.312 1.711-1.963 2.566-.735.967-1.476 1.933-2.191 2.894-.512.688-1.003 1.37-1.506 2.055a356.843 356.843 0 0 0-12.449 18.128c-.01.014-.02.028-.027.043a367.82 367.82 0 0 0-2.875 4.557c-.05.081-.102.163-.152.246a361.864 361.864 0 0 0-2.719 4.458l-.094.155a369.532 369.532 0 0 0-6.537 11.338C11.69 160.689 6.646 173.807 6.428 174.586c-32.718 116.97 69.396 67.575 83.645 60.201 15.343 7.58 32.615 11.846 50.888 11.846 50.021 0 92.58-31.93 108.422-76.519h-60.446c-8.944 15.11-26.163 25.345-45.945 25.345-28.984 0-52.479-21.96-52.479-49.05h164.54c.624-4.851.947-9.796.947-14.816zM234.51 18.386c9.907 6.687 17.852 17.187 4.207 52.55-13.088-21.048-32.778-37.559-56.181-46.634 10.646-5.141 37.01-16.016 51.974-5.916zM23.98 234.552c-8.07-8.275-9.496-28.429 8.31-65.154 8.985 25.835 26.916 47.482 50.092 61.22-11.526 6.345-42.126 20.629-58.402 3.934zm66.279-119.601c.92-26.329 23.834-47.41 51.987-47.41 28.153 0 51.068 21.081 51.988 47.41H90.259z" fill="#1EBBEE"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

View file

@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" style="enable-background:new 0 0 56 56;" xml:space="preserve" width="688" height="880"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none" class="" style=""/>
<style type="text/css">
.st0{fill:#0097A7;}
.st1{fill:#FFFFFF;}
.st2{fill:#00BCD4;}
</style>
<g class="currentLayer" style=""><title>Layer 1</title><g id="Ebene_2" class="">
<g id="Ebene_3">
<path id="XMLID_16_" class="st0" d="M344.1184284363063,799.570125922042 V87.96842249322498 c0,0 162.09059592452832,10.99605281236583 282.4548998288811,50.26766999938664 c0,0 -1.60485738539137,127.24003968594741 -1.60485738539137,252.90921468441405 c0,175.9368449978532 -138.01773514365777,364.44060749555314 -280.85004244348966,398.99963062013137 M687.5579089100594,92.6810165556675 c-146.04202207061462,-48.6968053119058 -332.2054787760135,-62.834587499233294 -343.439480473753,-62.834587499233294 c-11.23400169773959,0 -194.1877436323557,14.13778218732749 -343.439480473753,62.834587499233294 c0,0 0,153.9447393731216 0,306.3186140587623 C0.6789479625532715,612.637228111823 172.39868819942984,838.8417431090629 344.1184284363063,879.6842249835645 c171.7197402368765,-42.41334656198248 343.439480473753,-267.0469968717415 343.439480473753,-479.11372968165387 C687.5579089100594,248.1966206162699 687.5579089100594,92.6810165556675 687.5579089100594,92.6810165556675 "/>
<polygon id="XMLID_15_" class="st1" points="623.3636474609375,600.070322238108 334.4892578125,843.5543626155804 339.30389404296875,45.555075835946354 631.387939453125,109.9605252590718 "/>
</g>
<g id="Ebene_2_1_">
<path id="XMLID_12_" class="st2" d="M344.1184284363063,783.8614790472337 V53.40939936864668 c0,0 157.27602376835424,21.99210562473165 280.85004244348966,62.834587499233294 c0,0 1.60485738539137,130.3817690609091 1.60485738539137,259.19267343433734 c0,175.9368449978532 -138.01773514365777,388.0035778077656 -280.85004244348966,422.56260093234386 M687.5579089100594,75.40150499337834 C539.9110294540535,26.704699681472533 355.3524301340459,-5.701622285414487e-9 344.1184284363063,-5.701622285414487e-9 S149.93068480395064,25.133834993991698 0.6789479625532715,75.40150499337834 c0,0 0,153.9447393731216 0,306.3186140587623 c0,212.06673280991237 170.11488285148516,438.2712478071522 343.439480473753,479.11372968165387 c171.7197402368765,-42.41334656198248 343.439480473753,-267.0469968717415 343.439480473753,-479.11372968165387 C687.5579089100594,227.77537967901907 687.5579089100594,75.40150499337834 687.5579089100594,75.40150499337834 "/>
</g>
<g id="Ebene_1_2_">
<path id="XMLID_7_" class="st2" d="M559.1693180787498,400.5704953019106 c0,-114.67312218610077 -94.68658573809081,-210.49586812243152 -215.0508896424435,-210.49586812243152 v42.41334656198248 c17.653431239305068,25.13383499969332 30.492290322436016,51.83853468686746 40.12143463478424,83.25582843648412 H344.1184284363063 v40.84248187450164 h49.75057894713245 c1.60485738539137,14.13778218732749 3.2097147707827394,26.704699687174156 3.2097147707827394,40.84248187450164 s-1.60485738539137,28.27556437465498 -3.2097147707827394,40.84248187450164 H344.1184284363063 v40.84248187450164 h40.12143463478424 c-9.629144312348222,29.84642906213581 -24.072860780870542,58.1219934367908 -40.12143463478424,83.25582843648412 v42.41334656198248 C461.2730175698763,607.9246340493805 559.1693180787498,515.2436174880113 559.1693180787498,400.5704953019106 zM509.4187391316173,356.58628405244735 c3.2097147707827394,14.13778218732749 4.814572156174111,26.704699687174156 4.814572156174111,40.84248187450164 s-1.60485738539137,28.27556437465498 -4.814572156174111,40.84248187450164 h-73.82343972800298 c1.60485738539137,-14.13778218732749 3.2097147707827394,-26.704699687174156 3.2097147707827394,-40.84248187450164 s-1.60485738539137,-28.27556437465498 -3.2097147707827394,-40.84248187450164 H509.4187391316173 L509.4187391316173,356.58628405244735 zM493.3701652777037,315.7438021779457 h-62.58943803026341 c-8.024286926956847,-26.704699687174156 -16.048573853913695,-51.83853468686746 -28.88743293704465,-73.83064031159911 C438.8050141743971,254.4800793661932 470.9021618822245,281.1847790533673 493.3701652777037,315.7438021779457 zM398.6835795396129,557.6569640499938 c12.838859083130957,-23.562970312212485 22.46800339547917,-48.6968053119058 28.88743293704465,-75.40150499907996 h62.58943803026341 C470.9021618822245,518.385346862973 438.8050141743971,545.0900465501472 398.6835795396129,557.6569640499938 z"/>
<path id="XMLID_2_" class="st1" d="M344.1184284363063,607.9246340493805 V565.511287487398 c-17.653431239305068,-25.13383499969332 -30.492290322436016,-51.83853468686746 -40.12143463478424,-83.25582843648412 H344.1184284363063 v-40.84248187450164 h-49.75057894713245 c-1.60485738539137,-14.13778218732749 -3.2097147707827394,-26.704699687174156 -3.2097147707827394,-40.84248187450164 s1.60485738539137,-28.27556437465498 3.2097147707827394,-40.84248187450164 H344.1184284363063 v-40.84248187450164 h-40.12143463478424 c9.629144312348222,-29.84642906213581 24.072860780870542,-58.1219934367908 40.12143463478424,-83.25582843648412 v-42.41334656198248 c-117.15458913356997,0 -215.0508896424435,92.68101656136912 -215.0508896424435,210.49586812243152 C129.0675387938628,515.2436174880113 226.96383930273635,607.9246340493805 344.1184284363063,607.9246340493805 zM251.0367000836069,356.58628405244735 c-1.60485738539137,14.13778218732749 -3.2097147707827394,26.704699687174156 -3.2097147707827394,40.84248187450164 s1.60485738539137,28.27556437465498 3.2097147707827394,40.84248187450164 h-72.21858234261163 c-3.2097147707827394,-14.13778218732749 -4.814572156174111,-26.704699687174156 -4.814572156174111,-40.84248187450164 s1.60485738539137,-28.27556437465498 4.814572156174111,-40.84248187450164 H251.0367000836069 L251.0367000836069,356.58628405244735 zM289.5532773329998,241.91316186634654 c-12.838859083130957,23.562970312212485 -22.46800339547917,48.6968053119058 -28.88743293704465,73.83064031159911 h-62.58943803026341 C217.33469499038816,281.1847790533673 249.43184269821558,254.4800793661932 289.5532773329998,241.91316186634654 zM194.86669159490896,482.25545905091394 h62.58943803026341 c8.024286926956847,26.704699687174156 16.048573853913695,51.83853468686746 28.88743293704465,75.40150499907996 C249.43184269821558,545.0900465501472 217.33469499038816,518.385346862973 194.86669159490896,482.25545905091394 z"/>
</g>
</g></g></svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

View file

@ -0,0 +1,9 @@
declare module "*.svg" {
const content: any;
export default content;
}
declare module "*.png" {
const content: any;
export default content;
}

View file

@ -0,0 +1,69 @@
import img1 from "./01-uBlock-Origin.svg";
import img2 from "./02-Adblock-Plus.svg";
import img3 from "./03-AdGuard.svg";
import img4 from "./04-DNS66.png";
import img5 from "./05-Nano-Adblocker.png";
import img6 from "./06-AdBlock.png";
import img7 from "./07-AdAway.png";
import img8 from "./08-Personal-Blocklist.png";
import img10 from "./10-Redirector.png";
import img11 from "./11-Hosts-File-Editor.png";
import img12 from "./12-Gas-Mask.png";
import img13 from "./13-MinerBlock.svg";
import img14 from "./14-Pi-hole.svg";
import img15 from "./15-uBlock.svg";
import img16 from "./16-Internet-Explorer-TPL.svg";
import img17 from "./17-Google-Hit-Hider-by-Domain.png";
import img18 from "./18-FireHOL.png";
import img19 from "./19-Samsung-Knox.png";
import img20 from "./20-Little-Snitch.png";
import img21 from "./21-Privoxy.png";
import img22 from "./22-Diversion.png";
import img23 from "./23-dnsmasq.png";
import img24 from "./24-Slimjet.png";
import img25 from "./25-uMatrix.png";
import img26 from "./26-Blokada.png";
import img27 from "./27-hostsmgr.png";
import img28 from "./28-personalDNSfilter.svg";
import img29 from "./29-Unbound.png";
import img30 from "./30-BIND.png";
import img31 from "./31-AdGuard-Home.png";
import img32 from "./32-AdNauseam.png";
import img33 from "./33-Legacy-Unix-Derivatives.png";
import img34 from "./34-Windows-command-line.png";
export {
img1,
img2,
img3,
img4,
img5,
img6,
img7,
img8,
img10,
img11,
img12,
img13,
img14,
img15,
img16,
img17,
img18,
img19,
img20,
img21,
img22,
img23,
img24,
img25,
img26,
img27,
img28,
img29,
img30,
img31,
img32,
img33,
img34
};

View file

@ -0,0 +1,5 @@
import { SoftwareIcon } from "./SoftwareIcon";
export {
SoftwareIcon
};

View file

@ -0,0 +1,5 @@
import { HomeContainer } from "./HomeContainer";
export {
HomeContainer as Home
};

View file

@ -0,0 +1,4 @@
export interface IColumnVisibility {
column: string;
visible: boolean;
};

View file

@ -0,0 +1,6 @@
export interface ILanguage {
id: number;
filterListIds: number[];
iso6391: string;
name: string;
};

View file

@ -0,0 +1,6 @@
export interface ILicense {
id: number;
descriptionUrl: string;
filterListIds: number[];
name: string;
};

Some files were not shown because too many files have changed in this diff Show more