web -> desktop
This commit is contained in:
49
src/app/admin-page.js
Normal file
49
src/app/admin-page.js
Normal file
@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import Page from './page';
|
||||
import Footer from './footer';
|
||||
import {Header} from './index-page'
|
||||
|
||||
import Toggle from 'material-ui/Toggle';
|
||||
|
||||
export default class AdminPage extends Page {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.setTitle('-=-= Some page =-=-');
|
||||
this.options = {}
|
||||
}
|
||||
componentDidMount() {
|
||||
this.loadSettings()
|
||||
}
|
||||
loadSettings() {
|
||||
window.torrentSocket.emit('admin', window.customLoader((options) => {
|
||||
this.options = options;
|
||||
console.log(this.options)
|
||||
this.forceUpdate();
|
||||
}));
|
||||
}
|
||||
saveSettings() {
|
||||
window.torrentSocket.emit('setAdmin', this.options)
|
||||
this.forceUpdate()
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Header />
|
||||
<div className='column center w100p pad0-75'>
|
||||
<Toggle
|
||||
style={{marginTop: '10px'}}
|
||||
label="Disable DHT scanning"
|
||||
toggled={this.options.dhtDisabled}
|
||||
thumbSwitchedStyle={{backgroundColor: 'red'}}
|
||||
trackSwitchedStyle={{backgroundColor: '#ff9d9d'}}
|
||||
onToggle={(e, checked) => {
|
||||
this.options.dhtDisabled = checked
|
||||
this.saveSettings()
|
||||
}}
|
||||
/>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
24
src/app/app.css
Normal file
24
src/app/app.css
Normal file
@ -0,0 +1,24 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #222;
|
||||
height: 150px;
|
||||
padding: 20px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-intro {
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
86
src/app/app.js
Normal file
86
src/app/app.js
Normal file
@ -0,0 +1,86 @@
|
||||
import React, { Component } from 'react';
|
||||
import './app.css';
|
||||
import './router';
|
||||
import PagesPie from './pages-pie.js';
|
||||
//import registerServiceWorker from './registerServiceWorker';
|
||||
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
|
||||
|
||||
const { ipcRenderer, remote } = require('electron');
|
||||
|
||||
//var io = require("socket.io-client");
|
||||
//window.torrentSocket = io(document.location.protocol + '//' + document.location.hostname + (process.env.NODE_ENV === 'production' ? '/' : ':8095/'));
|
||||
window.torrentSocket = {}
|
||||
window.torrentSocket.callbacks = {}
|
||||
window.torrentSocket.on = (name, func) => {
|
||||
ipcRenderer.on(name, (event, data) => {
|
||||
func(data)
|
||||
});
|
||||
}
|
||||
window.torrentSocket.off = (name, func) => {
|
||||
if(!func)
|
||||
ipcRenderer.removeListener(name);
|
||||
else
|
||||
ipcRenderer.removeListener(name, func);
|
||||
}
|
||||
window.torrentSocket.emit = (name, ...data) => {
|
||||
if(typeof data[data.length - 1] === 'function')
|
||||
{
|
||||
const id = Math.random().toString(36).substring(5)
|
||||
window.torrentSocket.callbacks[id] = data[data.length - 1];
|
||||
data[data.length - 1] = {callback: id}
|
||||
}
|
||||
ipcRenderer.send(name, data)
|
||||
}
|
||||
ipcRenderer.on('callback', (event, id, data) => {
|
||||
const callback = window.torrentSocket.callbacks[id]
|
||||
if(callback)
|
||||
callback(data)
|
||||
delete window.torrentSocket.callbacks[id]
|
||||
});
|
||||
|
||||
|
||||
// Needed for onTouchTap
|
||||
// http://stackoverflow.com/a/34015469/988941
|
||||
injectTapEventPlugin();
|
||||
|
||||
//registerServiceWorker();
|
||||
|
||||
let loadersCount = 0;
|
||||
let appReady = false;
|
||||
window.customLoader = (func, onLoading, onLoaded) => {
|
||||
loadersCount++;
|
||||
if(onLoading) {
|
||||
onLoading();
|
||||
}
|
||||
return (...args) => {
|
||||
func(...args);
|
||||
if(onLoaded) {
|
||||
onLoaded();
|
||||
}
|
||||
loadersCount--;
|
||||
}
|
||||
};
|
||||
|
||||
window.isReady = () => {
|
||||
return (appReady && loadersCount === 0)
|
||||
}
|
||||
|
||||
class App extends Component {
|
||||
componentDidMount() {
|
||||
window.router()
|
||||
appReady = true;
|
||||
}
|
||||
componentWillUnmount() {
|
||||
appReady = false;
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<MuiThemeProvider>
|
||||
<PagesPie />
|
||||
</MuiThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
252
src/app/bad-words.js
Normal file
252
src/app/bad-words.js
Normal file
@ -0,0 +1,252 @@
|
||||
let XXX_BLOCK_WORDS = ['incestcash', 'asacp', 'xondemand', 'yankscash', 'klixxx', 'cybersitter', 'safesurf',
|
||||
'surfwatch', 'netcash', 'watersport', 'fuck', 'threesome', 'tits',
|
||||
'masturbating', 'incest', 'bestiality', 'analintercourse', 'analsex', 'animesex',
|
||||
'anitablonde', 'autosex', 'blackass', 'blackasses', 'boner', 'boobcruise',
|
||||
'boobies', 'bordello', 'braless', 'brothel', 'callgirl', 'callgirls',
|
||||
'clit', 'clitoris', 'clits', 'cums', 'cybererotic', 'cybererotica',
|
||||
'cybersex', 'cybersexx', 'ejaculation', 'erotica', 'eroticfilm', 'eroticfilms',
|
||||
'eroticism', 'eroticphoto', 'eroticphotography', 'eroticphotos', 'erotics', 'eroticsex',
|
||||
'eroticsexstories', 'eroticstories', 'eroticstory', 'erotik', 'erotika', 'antasysex',
|
||||
'gaysex', 'gruppensex', 'horny', 'jackoff', 'lesbo', 'lesbos',
|
||||
'makinglove', 'motherfucker', 'horsesex', 'dogsex', 'snakesex', 'sextracker',
|
||||
'tranny', 'bdsm', 'porn', 'beastality', 'bizarre', 'blowjob',
|
||||
'bondage', 'bondages', 'celebs', 'cum', 'cunt', 'transsexual',
|
||||
'transsexuals', 'cumbath', 'cumshot', 'dildo', 'dildos', 'transvestites',
|
||||
'transvestite', 'dominatrix', 'spank', 'dungeon', 'sexbilder', 'dungeons',
|
||||
'pussie', 'ebony', 'fisting', 'gayshop', 'facesitting', 'sexshop',
|
||||
'pussy', 'bukkake', 'escort', 'blowjobs', 'facial', 'facialized',
|
||||
'facials', 'fetish', 'footfetish', 'fuckingpussy', 'fucking', 'hardcore',
|
||||
'hentai', 'hustlerlatex', 'latex', 'lolita', 'lolitas', 'mlm',
|
||||
'oralsex', 'peepshow', 'peepshows', 'phonesex', 'pornstars', 'preteen',
|
||||
'preteens', 'pussies', 'shemale', 'shemales', 'sluts', 'whores',
|
||||
'whore', 'spanking', 'strapon', 'twinks', 'upskirts', 'voyeur',
|
||||
'whip', 'whipping', 'xxx', 'sexkey', 'femdom', 'gangbang',
|
||||
'gloryhole', 'adultbuffet', 'adultcartoons', 'adultchat', 'adultchatnetwork', 'adultdvd',
|
||||
'adultentertainment', 'adultentertainmenthouse', 'adulterotica', 'adultfilms', 'adultfree', 'adultfreepics',
|
||||
'adultfreepix', 'adultfun', 'adulthardcore', 'adultimages', 'adultjpeg', 'adultlink',
|
||||
'adultlinks', 'adultmovie', 'adultmovies', 'adultmpeg', 'adultmpegs', 'adultpass',
|
||||
'adultpersonals', 'adultphotos', 'adultpic', 'adultpics', 'adultpicture', 'adultpictures',
|
||||
'adultpix', 'adultporn', 'adultpornography', 'adultsearch', 'adultsex', 'adultsights',
|
||||
'adultsites', 'adultsonly', 'adultstars', 'adultstories', 'adultvideo', 'adultvideos',
|
||||
'adultweb', 'adultxxx', 'alt.sex', 'altsex', 'alt.sex.exhibitionism', 'alt.sex.pictures',
|
||||
'altsexst', 'alt.sex.stories', 'alt.sex.voyeurism', 'amateurerotica', 'amateurfotos', 'amateurhardcore',
|
||||
'amateurindex', 'amateurnaked', 'amateurnudes', 'amateurporn', 'amateurpussy', 'amateursex',
|
||||
'amateursxxx', 'amatuerhardcore', 'amatuersex', 'ampland', 'analaction', 'analcum',
|
||||
'analfuck', 'analfucking', 'analgallery', 'analingus', 'analpics', 'analpicture',
|
||||
'analsex', 'analsexpics', 'analsexpictures', 'analxxx', 'animalbeastiality', 'animehentai',
|
||||
'animeporno', 'animexxx', 'asiacarrera', 'assfuck', 'assfucking', 'asshole',
|
||||
'assholes', 'asslick', 'asspics', 'assworship', 'autofellatio', 'babepix',
|
||||
'badgirls', 'bakedchicks', 'baldcunt', 'baldpussies', 'baldpussy', 'barecelebs',
|
||||
'barenaked', 'barepussy', 'beastiality', 'beaverboy', 'beavershots', 'bigballs',
|
||||
'bigblackboobs', 'bigblackcocks', 'bigblackdicks', 'bigblacktits', 'bigboob', 'bigboobs',
|
||||
'bigbreast', 'bigbreastlovers', 'bigbreasts', 'bigbutt', 'bigbutts', 'bigclit',
|
||||
'bigclitoris', 'bigclits', 'bigcock', 'bigcocks', 'bigcunt', 'bigcunts',
|
||||
'bigdick', 'bigdicks', 'biggestboobs', 'biggestdick', 'biggesttit', 'biggesttits',
|
||||
'bighairyballs', 'bighardons', 'bighooters', 'bignipples', 'bigpussies', 'bigpussy',
|
||||
'bigtit', 'bigtits', 'bigtitties', 'bigtitts', 'bizarresex', 'bizarrexxx',
|
||||
'bizzaresex', 'blackbondage', 'blackboobs', 'blackbooty', 'blackbutt', 'blackcock',
|
||||
'blackcocks', 'blackcum', 'blackcunt', 'blackcunts', 'blackdick', 'blackdicks',
|
||||
'blackerotica', 'blackhardcore', 'blacknudes', 'blackonblondes', 'blackporn', 'blackporno',
|
||||
'blackpornography', 'blackpussy', 'blacksex', 'blacksluts', 'blacksonblondes', 'blacktit',
|
||||
'blacktits', 'blacktwat', 'blackxxx', 'blondepussy', 'blondsgay', 'bondagefree',
|
||||
'bondagegallery', 'bondagelinks', 'bondagepics', 'bondagepictures', 'bondagesex', 'bondagestories',
|
||||
'bondagestory', 'bukake', 'bustybabes', 'bustyblondes', 'bustyceleb', 'butt-fuck',
|
||||
'buttfuck', 'buttfucker', 'buttfuckers', 'buttfucking', 'buttholes', 'buttman',
|
||||
'buttpics', 'buttplug', 'buttthumbnails', 'cartoonporn', 'cartoonsex', 'cartoonsxxx',
|
||||
'cartoonxxx', 'celebritiesnaked', 'celebritiesnude', 'celebritiesxxx', 'celebritybush', 'celebritybutts',
|
||||
'celebritynude', 'celebritynudes', 'celebrityporn', 'celebritypussy', 'celebritysex', 'celebrityxxx',
|
||||
'celebsxxx', 'celebxxx', 'centerfolds', 'chaseylain', 'chatsex', 'cheerleadersxxx',
|
||||
'cheerleaderxxx', 'chickswithdicks', 'christycanyon', 'cicciolina', 'clitpics', 'clitpictures',
|
||||
'closeuppussy', 'comixxx', 'crotchless', 'cruisingforsex', 'cumbaths', 'cumcoveredcunts',
|
||||
'cumcunt', 'cumdrinker', 'cumdrinkers', 'cumdrinking', 'cumdrops', 'cumeater',
|
||||
'cumeaters', 'cumeating', 'cumface', 'cumfaces', 'cumfacial', 'cumfacials',
|
||||
'cumgallery', 'cumgargle', 'cumguzzlers', 'cumincunt', 'cumjunkies', 'cumlovers',
|
||||
'cummpeg', 'cumpic', 'cumpics', 'cumpicture', 'cumpictures', 'cumpussy',
|
||||
'cumsluts', 'cumsucker', 'cumsuckers', 'cumsucking', 'cumswallow', 'cumswallowers',
|
||||
'cumswallowing', 'cumtasting', 'cumthumbnails', 'cunilingus', 'cunnilingus', 'cuntcum',
|
||||
'cuntfuck', 'cuntfucking', 'cuntjuice', 'cuntlapper', 'cuntlick', 'cuntlickers',
|
||||
'cuntlicking', 'cuntpics', 'cuntpictures', 'cunts', 'cuntstories', 'cuntsuckers',
|
||||
'cuntz', 'cyberlust', 'cybernude', 'cyberporn', 'cyberpornlinks', 'cyberpornsexlinks',
|
||||
'cyberslut', 'd’amateur', 'danniashe', 'dicksex', 'dicksuckers', 'dicksucking',
|
||||
'digixxx', 'directporn', 'dirtypictures', 'dirtysex', 'doggiestyle', 'doggystyle',
|
||||
'domatrix', 'dominatrixes', 'downblouse', 'drinkingcum', 'drippingcunt', 'drippingcunts',
|
||||
'drippingpussy', 'easypic.com', 'eatcum', 'eatingcum', 'eatingpussy', 'eatpussy',
|
||||
'ebony+ayres', 'ebonyporn', 'ebonypussy', 'ebonysex', 'ebonyxxx', 'enormoustits',
|
||||
'erosvillage', 'eroticanime', 'eroticart', 'eroticas', 'eroticastories', 'eroticfiction',
|
||||
'eroticlesbianstories', 'eroticmovie', 'eroticmovies', 'erotico', 'eroticos', 'eroticpics',
|
||||
'eroticpicture', 'eroticpictures', 'eroticsites', 'eroticsounds', 'erotictales', 'erotictext',
|
||||
'eroticvideo', 'eroticvideos', 'eroticwomen', 'eroticwriting', 'erotikchat', 'erotique',
|
||||
'erotismo', 'escortservice', 'escortservices', 'eurosex', 'explicitsex', 'facecum',
|
||||
'facesit', 'facesitters', 'fastporn', 'fatass', 'fatsex', 'feetfetish',
|
||||
'feetsex', 'felatio', 'fellatio', 'fellations', 'fetishwear', 'fettegirls',
|
||||
'fingerbang', 'fingerfuck', 'flesh4free', 'footjobs', 'footlicking', 'footworship',
|
||||
'fornication', 'freeanal', 'freeanalsex', 'freeass', 'freebigboobs', 'freebigtit',
|
||||
'freebigtits', 'freeblackcunt', 'freeblackpussy', 'freeblowjob', 'freeblowjobs', 'freebondage',
|
||||
'freeboobs', 'freecum', 'freecumshot', 'freecumshots', 'freecunt', 'freecunts',
|
||||
'freedick', 'freeerotic', 'freeerotica', 'freeeroticstories', 'freefuck', 'freefucking',
|
||||
'freefuckpics', 'freegay', 'freegaypics', 'freehardcore', 'freehardcorepics', 'freehardcorepictures',
|
||||
'freehardcoreporn', 'freehardcoresex', 'freehardcoresexpics', 'freehentai', 'freehooters', 'freelargehooters',
|
||||
'freelesbian', 'freelesbianporn', 'freelesbiansex', 'freenakedpic', 'freenakedpics', 'freenakedpictures',
|
||||
'freenakedwomen', 'freenude', 'freenudecelebs', 'freenudephotos', 'freenudepics', 'freenudes',
|
||||
'freeporn', 'freepornmovies', 'freeporno', 'freepornography', 'freepornopics', 'freepornopictures',
|
||||
'freepornos', 'freepornpic', 'freepornpics', 'freepornpictures', 'freeporns', 'freepornsite',
|
||||
'freepornsites', 'freepussy', 'freepussypic', 'freepussypics', 'freepussypictures', 'freesex',
|
||||
'freesexchat', 'freesexmovies', 'freesexnet', 'freesexphotos', 'freesexpic', 'freesexpics',
|
||||
'freesexpicture', 'freesexpictures', 'freesexsites', 'freesexstories', 'freesexvideos', 'freesexycam',
|
||||
'freeshemale', 'freesmut', 'freetit', 'freetits', 'freevoyeur', 'freexxx',
|
||||
'freexxxmovie', 'freexxxmovies', 'freexxxmpeg', 'freexxxphotos', 'freexxxpic', 'freexxxpics',
|
||||
'freexxxpicture', 'freexxxpictures', 'freexxxstories', 'freexxxvideo', 'freexxxx', 'fuking',
|
||||
'gangbangs', 'gratishardcoregalerie', 'hardcorecelebs', 'hardcorefisting', 'hardcorefree', 'hardcorefucking',
|
||||
'hardcorehooters', 'hardcorejunkie', 'hardcorejunky', 'hardcoremovie', 'hardcoremovies', 'hardcorepic',
|
||||
'hardcorepics', 'hardcorepictures', 'hardcorepix', 'hardcoreporn', 'hardcoreporno', 'hardcorepornography',
|
||||
'hardcorepornos', 'hardcorepussy', 'hardcoresamples', 'hardcoresex', 'hardcoresexpictures', 'hardcorestories',
|
||||
'hardcorethumbnails', 'hardcorevideo', 'hardcorevideos', 'hardcorexxx', 'harddicks', 'hardnipples',
|
||||
'hardons', 'hardporn', 'indiasex', 'interacialhardcore', 'interacialsex', 'interacialxxx',
|
||||
'intercoursepositions', 'internetsex', 'interracialfucking', 'interracialhardcore', 'interracialporn', 'interracialpornography',
|
||||
'interracialsex', 'interracialsexstories', 'interracialxxx', 'intimatesex', 'ittybittytitty', 'japaneseporn',
|
||||
'japanesesex', 'japansex', 'japanxxx', 'jennacam.com', 'jennajameson', 'jennicam',
|
||||
'jerkoff', 'jism', 'jiz', 'jizz', 'juicycunts', 'juicypussy',
|
||||
'justhardcore', 'karasamateurs', 'karasxxx', 'kascha', 'kaylakleevage', 'kobetai',
|
||||
'koreasex', 'lapdance', 'largeclits', 'largecunts', 'largedicks', 'largehooters',
|
||||
'largepussy', 'largetit', 'largetits', 'latinapussy', 'latinasex', 'latinosex',
|
||||
'latinoxxx', 'latinxxx', 'legsex', 'lesbianerotica', 'lesbianhardcore', 'lesbianorgies',
|
||||
'lesbianorgy', 'lesbianpics', 'lesbianpink', 'lesbianporn', 'lesbian-porno', 'lesbianporno',
|
||||
'lesbianpornography', 'lesbianpornos', 'lesbianpussy', 'lesbiansex', 'lesbiansexpics', 'lesbiansexstories',
|
||||
'lesbianxxx', 'lickadick', 'lickingpussy', 'lindalovelace', 'lingeriexxx', 'literotica',
|
||||
'littleboobs', 'littlepussy', 'littletits', 'livefucking', 'liveporn', 'livesex',
|
||||
'livesexcam', 'livesexcams', 'longdicks', 'lovedoll', 'lovedolls', 'magazinessex',
|
||||
'makinglove', 'malaysex', 'malenudes', 'malesex', 'mangaporno', 'mangax',
|
||||
'mangaxxx', 'manpics', 'mansex', 'marilynchambers', 'massivecocks', 'masterbating',
|
||||
'masturbation', 'masturbationvideos', 'maturefucking', 'maturesex', 'maturexxx', 'megaboobs',
|
||||
'megaporno', 'megapussy', 'megatits', 'mensdicks', 'mensex', 'm.i.l.f.',
|
||||
'milf', 'milfhunterpic', 'milfmpegsample', 'modelsex', 'mondoporn', 'monstercocks',
|
||||
'monsterdicks', 'motherfucker', 'moviepost.com', 'mpegsex', 'mpegxxx', 'muffdiving',
|
||||
'myslutwife', 'nacktfotos', 'nakedbabes', 'nakedblackwomen', 'nakedcelebrity', 'nakedcelebs',
|
||||
'nakedcheerleader', 'nakedchicks', 'nakedgirls', 'nakedguys', 'nakedladies', 'nakedlady',
|
||||
'nakedman', 'nakedmen', 'nakedness', 'nakedphotographs', 'nakedphotography', 'nakedphotos',
|
||||
'nakedpic', 'nakedpics', 'nakedpicture', 'nakedpictures', 'nakedpussy', 'nakedstars',
|
||||
'nakedwife', 'nakedwoman', 'nakedwomen', 'nastychat', 'nastypussy', 'nastysex',
|
||||
'nastythumbs', 'naturaltits', 'naughty.com', 'naughtylinks', 'naughtylinx', 'naughtylynx',
|
||||
'naughtynurses', 'netsex', 'niceass', 'nicetits', 'nikkinova', 'nikkityler',
|
||||
'nipples', 'nookie', 'nookies', 'nudeactress', 'nudeactresses', 'nudeamateur',
|
||||
'nudeamateurs', 'nudeasianwomen', 'nudebabes', 'nudebigboobs', 'nudeblack', 'nudeblackwomen',
|
||||
'nudeblondes', 'nudeceleb', 'nudeceleberties', 'nudecelebraties', 'nudecelebrites', 'nudecelebrities',
|
||||
'nudecelebrity', 'nudecelebs', 'nudecollegegirls', 'nudefemale', 'nudefemales', 'nudefree',
|
||||
'nudegay', 'nudeimages', 'nudeladies', 'nudelesbians', 'nudemale', 'nudemales',
|
||||
'nudeman', 'nudemen', 'nudepic', 'nudepics', 'nudepicture', 'nudepictures',
|
||||
'nudepornography', 'nudepussy', 'nuderaider', 'nuderedheads', 'nudes', 'nudesex',
|
||||
'nudestar', 'nudestars', 'nudevideoconferencing', 'nudewoman', 'nudewomen', 'nudism',
|
||||
'nudist', 'nudists', 'nudity', 'nylonfetish', 'nylonsex', 'nympho',
|
||||
'nymphos', 'olderbabes', 'oldersex', 'oldersluts', 'oldpussy', 'oldsex',
|
||||
'oldsluts', 'openlegs', 'openpussy', 'oral4free', 'oral-sex', 'oralsexpictures',
|
||||
'orgie', 'orgies', 'orgypics', 'orgys', 'pantyhosefetish', 'pantyhosesex',
|
||||
'peepcam', 'persiankitty', 'persiankitty.com', 'perverted', 'picsxxx', 'picturesex',
|
||||
'picturesofsex', 'picturessex', 'pimpserver', 'pimpserver.com', 'pinkpussy', 'pissing',
|
||||
'pixxx', 'poontang', 'pornagraphy', 'porncast', 'porncity', 'porndirectory',
|
||||
'porne', 'pornfree', 'pornmovies', 'pornno', 'pornnude', 'porno',
|
||||
'pornoadult', 'pornocartoons', 'pornochat', 'pornodeluxe', 'pornoe', 'pornofilms',
|
||||
'pornofree', 'pornogame', 'pornografi', 'pornografia', 'pornografie', 'pornografy',
|
||||
'pornograph', 'pornographi', 'pornographia', 'pornographic', 'pornographicpictures', 'pornographics',
|
||||
'pornographicvideo', 'pornographicvideos', 'pornographie', 'pornography', 'pornographyfree', 'pornographyphotos',
|
||||
'pornographypictures', 'pornography-sex', 'pornographysex', 'pornoland', 'pornolinks', 'pornolynx',
|
||||
'pornomagazines', 'pornomovies', 'pornompeg', 'pornophotos', 'pornopic', 'pornopics',
|
||||
'pornopicture', 'pornopictures', 'pornos', 'pornosex', 'pornosite', 'pornosites',
|
||||
'pornostar', 'pornostories', 'pornovideo', 'pornovideos', 'pornoxxx', 'pornphotos',
|
||||
'pornpics', 'pornpictures', 'pornpix', 'pornpost', 'pornqueens', 'pornrated',
|
||||
'porns', 'pornsex', 'pornstar', 'pornstories', 'porntrack', 'pornvideo',
|
||||
'pornvideos', 'pornypics', 'porstars', 'privatesex', 'privatex', 'privatexx',
|
||||
'privatexxx', 'prono', 'pronography', 'publicnudity', 'puffynipples', 'purple+passion',
|
||||
'pussyboard', 'pussycam', 'pussycams', 'pussycloseup', 'pussycloseups', 'pussycum',
|
||||
'pussycunt', 'pussyeater', 'pussyeaters', 'pussyeating', 'pussyfuck', 'pussyfucking',
|
||||
'pussyhair', 'pussyheaven', 'pussyjuice', 'pussylickers', 'pussylicking', 'pussylink',
|
||||
'pussylips', 'pussyphotos', 'pussyporno', 'pussys', 'pussysex', 'pussyshots',
|
||||
'pussythumbnails', 'pussytits', 'pusy', 'racqueldarrian', 'rape', 'rawlinks',
|
||||
'rawpussy', 'rawsex', 'realhardcore', 'realsex', 'redneckporn', 'redpussy',
|
||||
'rubberfetish', 'russianxxx', 'scat', 'schiffernude', 'seka', 'seniorsex',
|
||||
'sex4free', 'sex66', 'sexacts', 'sexadult', 'sexaids', 'sexamateur',
|
||||
'sexanal', 'sexandpictures', 'sexaphone', 'sexaudio', 'sexavi', 'sexbondage',
|
||||
'sexboner', 'sexbuttfucker', 'sexcam', 'sexcams', 'sexchat', 'sexchatrooms',
|
||||
'sexcites', 'sexclub', 'sexclubs', 'sex.com', 'sexdating', 'sexe',
|
||||
'sexfantasies', 'sexfantasy', 'sexfilm', 'sexgallery', 'sexgame', 'sexgames',
|
||||
'sexgirl', 'sexgirls', 'sexgroup', 'sexguide', 'sexhardcore', 'sexhphoto',
|
||||
'sexhungry', 'sexhungryjoe', 'sexhungryjoes', 'sexi', 'seximages', 'sexis',
|
||||
'sexjapan', 'sex.jpg', 'sexlinda', 'sexlinks', 'sexlinx', 'sexlive',
|
||||
'sexmagazine', 'sexmagazines', 'sexmature', 'sexmelayu', 'sexmodels', 'sexmovie',
|
||||
'sexmovies', 'sexmpeg', 'sexnude', 'sexnudity', 'sexo', 'sexoanal',
|
||||
'sexole', 'sexontheinternet', 'sexoral', 'sexpasswords', 'sexphoto', 'sexphotos',
|
||||
'sexpic', 'sexpicnet', 'sexpics', 'sexpicture', 'sexpictures', 'sexpicturesfree',
|
||||
'sexpix', 'sexplaza', 'sexporn', 'sexporno', 'sexpositions', 'sexpussy',
|
||||
'sexroulette', 'sexs', 'sexsamples', 'sexsearch', 'sexsex', 'sexsexsex',
|
||||
'sexshare', 'sexshops', 'sexshow', 'sexshows', 'sexsite', 'sexsites',
|
||||
'sexslave', 'sexsound', 'sexsounds', 'sexsource', 'sexspaces', 'sexspaces.com',
|
||||
'sexstories', 'sexstory', 'sexstorys', 'sextalk', 'sexteen', 'sexthumbnails',
|
||||
'sex-toons', 'sextoons', 'sextour', 'sextoy', 'sextoys', 'sextpus',
|
||||
'sextracker.com', 'sextropolis', 'sexualbondage', 'sexualfantasies', 'sexualfantasy', 'sexualintercourse',
|
||||
'sexualpictures', 'sexualpleasure', 'sexualpositions', 'sexuncensored', 'sexvideo', 'sexvision',
|
||||
'sexvote', 'sexwomen', 'sexworld', 'sexx', 'sexxx', 'sexxxx',
|
||||
'sexxxxx', 'sexyamateurs', 'sexybabes', 'sexyblack', 'sexyboobs', 'sexybookmark',
|
||||
'sexybookmarka', 'sexybookmarks', 'sexybutts', 'sexycunts', 'sexygirl', 'sexygirls',
|
||||
'sexyladies', 'sexylady', 'sexylegs', 'sexylingerie', 'sexymen', 'sexyphotos',
|
||||
'sexypics', 'sexypictures', 'sexypost', 'sexypussies', 'sexypussy', 'sexysites',
|
||||
'sexystories', 'sexywoman', 'sexywomen', 'shavedcunt', 'shavedcunts', 'shavedpussies',
|
||||
'shavedpussy', 'shavedpussypics', 'shavedsluts', 'shavedwomen', 'shavenpussies', 'shavenpussy',
|
||||
'shavepussy', 'sheboy', 'she-male', 'shemale+video', 'shemaleyum', 'showcam',
|
||||
'showcams', 'showercam', 'showercams', 'slavesex', 'smallboobs', 'smallbreasts',
|
||||
'smalldicks', 'smallpussy', 'smalltits', 'smut', 'smut69', 'smutland',
|
||||
'smutserver', 'smutshack', 'softcore', 'softporn', 'softpornography', 'spankingpage',
|
||||
'spreadpussy', 'spycamadult', 'stockingsex', 'storieserotic', 'storiessex', 'straightsex',
|
||||
'stretchedcunt', 'stripclub', 'stripclubs', 'stripshow', 'striptease', 'strokeit',
|
||||
'strokeme', 'suckdick', 'sucksex', 'superchicken', 'supersex', 'supertits',
|
||||
'swallowcum', 'swinger', 'swingerclub', 'swingers', 'swollenclits', 'syberporn',
|
||||
'sylviasaint', 'teenanal', 'teenanalsex', 'teenhardcore', 'teenie', 'teenies',
|
||||
'teennude', 'teenpics', 'teenporn', 'teenporno', 'teenpussy', 'teensex',
|
||||
'teenslut', 'teensluts', 'teensuck', 'teenxxx', 'tgp', 'thaipussy',
|
||||
'thaisex', 'thehun', 'threesomes', 'thumblords', 'thumbnailssex', 'thumbzilla',
|
||||
'tiffanytowers', 'tightcunt', 'tightpussies', 'tightpussy', 'tinypussy', 'tinytits',
|
||||
'tinytitties', 'tisandass', 'tit', 'titbondage', 'titfuck', 'titfucking',
|
||||
'tities', 'titman', 'titsandass', 'titsass', 'titties', 'titts',
|
||||
'titty', 'tittyfuck', 'tittyfucking', 'tittys', 'tokyotopless', 'tommysbookmark',
|
||||
'toplesswomen', 'trannies', 'transexual', 'transsexuels', 'twat', 'twats',
|
||||
'twink', 'ultradonkey', 'ultrahardcore', 'uncutcocks', 'vaginalintercourse', 'vaginapictures',
|
||||
'videoporno', 'videosex', 'videoxxx', 'vintageporn', 'virtualsex', 'vividtv',
|
||||
'wank', 'wankers', 'wanking', 'wendywhoppers', 'wetcunt', 'wetcunts',
|
||||
'wetdick', 'wetpanties', 'wetpussies', 'wetpussy', 'wetsex', 'wetvagina',
|
||||
'whitepussy', 'whitesex', 'whitetits', 'whitexxx', 'wifesharing', 'wifeswapping',
|
||||
'wildpussy', 'wildsex', 'womensex', 'worldsex', 'worldsexguide', 'x-rated',
|
||||
'xrated', 'x-ratedmovie', 'x-ratedmovies', 'x-ratedvideo', 'x-ratedvideos', 'xxx4free',
|
||||
'xxxadult', 'xxxadultmovies', 'xxxadultvideo', 'xxxadultvideos', 'xxxbabes', 'xxxcam',
|
||||
'xxxcelebrities', 'xxxfree', 'xxxfreepics', 'xxxgalleries', 'xxxhardcore', 'xxxjapan',
|
||||
'xxxlinks', 'xxxmodels', 'xxxmovie', 'xxxmovies', 'xxxmpeg', 'xxxpassword',
|
||||
'xxxphoto', 'xxxphotos', 'xxxpic', 'xxx-pics', 'xxxpics', 'xxxpicture',
|
||||
'xxxpictures', 'xxxpicturesfree', 'xxxpicx', 'xxxpirtures', 'xxxporn', 'xxxporno',
|
||||
'xxxpussy', 'xxx-rated', 'xxxrated', 'xxxsex', 'xxxsites', 'xxxsluts',
|
||||
'xxxstories', 'xxxteen', 'xxxthumbnails', 'xxxthumbs', 'xxxtoons', 'xxxtoys',
|
||||
'xxxvideo', 'xxxvideos', 'xxxwomen', 'xxxx', 'xxxxx', 'xxxxxx',
|
||||
'filthyfarm', 'adultcheck', 'erotic', 'mistresses', 'naughty',
|
||||
'adult', 'amateur', 'amateurs', 'anal', 'analau', 'poker',
|
||||
'anally', 'asian', 'swinging', 'ass', 'orgy', 'sexvideos',
|
||||
'babe', 'babes', 'bare', 'glamour', 'casino', 'bed',
|
||||
'vibrator', 'bikini', 'bisexual', 'blonde', 'breasted', 'breasts',
|
||||
'busty', 'butts', 'candy', 'caning', 'caps', 'catholic',
|
||||
'chained', 'chested', 'chubby', 'close-ups', 'coupling',
|
||||
'cucumber', 'doll', 'dolls', 'dolly', 'dominating', 'drinking',
|
||||
'drunk', 'fat', 'feet', 'fingering', 'fishnet', 'flasher',
|
||||
'footjob', 'foreplay', 'foreplaying', 'girlfriend', 'girlfriends', 'gymnast',
|
||||
'hairy', 'holly', 'horse', 'housewife', 'houswife', 'insertion',
|
||||
'insertions', 'interracial', 'jerka', 'juicy', 'kissing', 'kitty',
|
||||
'lactating', 'ladyboy', 'lance', 'latina', 'latins', 'leather',
|
||||
'legs', 'lesbian', 'lesbians', 'licking', 'lingerie', 'lubricants',
|
||||
'mature', 'midget', 'minor', 'mistress', 'nude',
|
||||
'older', 'olez', 'oral', 'panties', 'pantyhose', 'pedro',
|
||||
'peeing', 'penetration', 'pigtailed', 'pleasure', 'plump',
|
||||
'plumper', 'poser', 'posers', 'poses', 'posing', 'prosecuted',
|
||||
'punishment', 'reproducing', 'sex', 'shaved', 'shock', 'slave',
|
||||
'sleeping', 'speculums', 'spread', 'spreading', 'squirting', 'stripper',
|
||||
'stripping', 'swapping', 'thong', 'topless', 'toying',
|
||||
'trix', 'undressing', 'uniform', 'whipcream', 'brazzers', 'порно',
|
||||
'порн', 'лесб', 'гей', 'геи', 'прон', 'catgoddess', 'gracel', 'fatman', 'falko', 'pthc',
|
||||
'ptsc', 'yukikax', 'ls-models', '3yo', '4yo', '5yo', '6yo', '7yo', '8yo', '9yo',
|
||||
'10yo', '11yo', '12yo', '13yo', '14yo', '15yo', '16yo'
|
||||
];
|
||||
|
||||
module.exports = XXX_BLOCK_WORDS;
|
33
src/app/component.js
Normal file
33
src/app/component.js
Normal file
@ -0,0 +1,33 @@
|
||||
import React, { Component } from 'react';
|
||||
import { listenSwipe, removeSwipeListener } from './touch'
|
||||
|
||||
export default class BTComponent extends Component {
|
||||
componentDidMount() {
|
||||
// Свайп действия
|
||||
if(
|
||||
this.props.onSwipeLeft ||
|
||||
this.props.onSwipeRight ||
|
||||
this.props.onSwipeTop ||
|
||||
this.props.onSwipeBottom ||
|
||||
this.onSwipeLeft ||
|
||||
this.onSwipeRight ||
|
||||
this.onSwipeTop ||
|
||||
this.onSwipeBottom
|
||||
)
|
||||
{
|
||||
this.swipeFunctions = listenSwipe(this, {
|
||||
left: this.props.onSwipeLeft || this.onSwipeLeft,
|
||||
right: this.props.onSwipeRight || this.onSwipeRight,
|
||||
top: this.props.onSwipeTop || this.onSwipeTop,
|
||||
bottom: this.props.onSwipeBottom || this.onSwipeBottom,
|
||||
initSwipe: this.props.initSwipe || this.initSwipe,
|
||||
});
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
if(this.swipeFunctions)
|
||||
{
|
||||
removeSwipeListener(this, this.swipeFunctions);
|
||||
}
|
||||
}
|
||||
}
|
312
src/app/content.js
Normal file
312
src/app/content.js
Normal file
@ -0,0 +1,312 @@
|
||||
const ContentTypes = {
|
||||
VIDEO: 'video',
|
||||
AUDIO: 'audio',
|
||||
PICTURES: 'pictures',
|
||||
BOOKS: 'books',
|
||||
APPLICATION: 'application',
|
||||
ARCHIVE: 'archive',
|
||||
DISC: 'disc',
|
||||
}
|
||||
|
||||
const ExtesionBase = {
|
||||
webm: ContentTypes.VIDEO,
|
||||
mkv: ContentTypes.VIDEO,
|
||||
flv: ContentTypes.VIDEO,
|
||||
vob: ContentTypes.VIDEO,
|
||||
ogv: ContentTypes.VIDEO,
|
||||
drc: ContentTypes.VIDEO,
|
||||
mng: ContentTypes.VIDEO,
|
||||
avi: ContentTypes.VIDEO,
|
||||
mov: ContentTypes.VIDEO,
|
||||
qt: ContentTypes.VIDEO,
|
||||
wmv: ContentTypes.VIDEO,
|
||||
yuv: ContentTypes.VIDEO,
|
||||
rm: ContentTypes.VIDEO,
|
||||
rmvb: ContentTypes.VIDEO,
|
||||
asf: ContentTypes.VIDEO,
|
||||
amv: ContentTypes.VIDEO,
|
||||
mp4: ContentTypes.VIDEO,
|
||||
m4p: ContentTypes.VIDEO,
|
||||
m4v: ContentTypes.VIDEO,
|
||||
mpg: ContentTypes.VIDEO,
|
||||
mpeg: ContentTypes.VIDEO,
|
||||
mpv: ContentTypes.VIDEO,
|
||||
svi: ContentTypes.VIDEO,
|
||||
'3gp': ContentTypes.VIDEO,
|
||||
'3g2': ContentTypes.VIDEO,
|
||||
mxf: ContentTypes.VIDEO,
|
||||
roq: ContentTypes.VIDEO,
|
||||
nsv: ContentTypes.VIDEO,
|
||||
f4v: ContentTypes.VIDEO,
|
||||
ts: ContentTypes.VIDEO,
|
||||
divx: ContentTypes.VIDEO,
|
||||
m2ts: ContentTypes.VIDEO,
|
||||
|
||||
aa: ContentTypes.AUDIO,
|
||||
aac: ContentTypes.AUDIO,
|
||||
aax: ContentTypes.AUDIO,
|
||||
act: ContentTypes.AUDIO,
|
||||
aiff: ContentTypes.AUDIO,
|
||||
amr: ContentTypes.AUDIO,
|
||||
ape: ContentTypes.AUDIO,
|
||||
au: ContentTypes.AUDIO,
|
||||
awb: ContentTypes.AUDIO,
|
||||
dct: ContentTypes.AUDIO,
|
||||
dss: ContentTypes.AUDIO,
|
||||
dvf: ContentTypes.AUDIO,
|
||||
flac: ContentTypes.AUDIO,
|
||||
gsm: ContentTypes.AUDIO,
|
||||
iklax: ContentTypes.AUDIO,
|
||||
ivs: ContentTypes.AUDIO,
|
||||
m4a: ContentTypes.AUDIO,
|
||||
mmf: ContentTypes.AUDIO,
|
||||
mp3: ContentTypes.AUDIO,
|
||||
mpc: ContentTypes.AUDIO,
|
||||
msv: ContentTypes.AUDIO,
|
||||
ogg: ContentTypes.AUDIO,
|
||||
oga: ContentTypes.AUDIO,
|
||||
opus: ContentTypes.AUDIO,
|
||||
rm: ContentTypes.AUDIO,
|
||||
ra: ContentTypes.AUDIO,
|
||||
raw: ContentTypes.AUDIO,
|
||||
sln: ContentTypes.AUDIO,
|
||||
tta: ContentTypes.AUDIO,
|
||||
vox: ContentTypes.AUDIO,
|
||||
wav: ContentTypes.AUDIO,
|
||||
wma: ContentTypes.AUDIO,
|
||||
wv: ContentTypes.AUDIO,
|
||||
ac3: ContentTypes.AUDIO,
|
||||
|
||||
jpg: ContentTypes.PICTURES,
|
||||
jpeg: ContentTypes.PICTURES,
|
||||
exif: ContentTypes.PICTURES,
|
||||
gif: ContentTypes.PICTURES,
|
||||
tiff: ContentTypes.PICTURES,
|
||||
bmp: ContentTypes.PICTURES,
|
||||
png: ContentTypes.PICTURES,
|
||||
ppm: ContentTypes.PICTURES,
|
||||
pgm: ContentTypes.PICTURES,
|
||||
pbm: ContentTypes.PICTURES,
|
||||
pnm: ContentTypes.PICTURES,
|
||||
webp: ContentTypes.PICTURES,
|
||||
heif: ContentTypes.PICTURES,
|
||||
bpg: ContentTypes.PICTURES,
|
||||
ico: ContentTypes.PICTURES,
|
||||
tga: ContentTypes.PICTURES,
|
||||
cd5: ContentTypes.PICTURES,
|
||||
deep: ContentTypes.PICTURES,
|
||||
ecw: ContentTypes.PICTURES,
|
||||
fits: ContentTypes.PICTURES,
|
||||
flif: ContentTypes.PICTURES,
|
||||
ilbm: ContentTypes.PICTURES,
|
||||
img: ContentTypes.PICTURES,
|
||||
nrrd: ContentTypes.PICTURES,
|
||||
pam: ContentTypes.PICTURES,
|
||||
pcx: ContentTypes.PICTURES,
|
||||
pgf: ContentTypes.PICTURES,
|
||||
sgi: ContentTypes.PICTURES,
|
||||
sid: ContentTypes.PICTURES,
|
||||
vicar: ContentTypes.PICTURES,
|
||||
psd: ContentTypes.PICTURES,
|
||||
cpt: ContentTypes.PICTURES,
|
||||
psp: ContentTypes.PICTURES,
|
||||
xcf: ContentTypes.PICTURES,
|
||||
svg: ContentTypes.PICTURES,
|
||||
cgm: ContentTypes.PICTURES,
|
||||
cdr: ContentTypes.PICTURES,
|
||||
hvif: ContentTypes.PICTURES,
|
||||
odg: ContentTypes.PICTURES,
|
||||
vml: ContentTypes.PICTURES,
|
||||
wmf: ContentTypes.PICTURES,
|
||||
|
||||
cbr: ContentTypes.BOOKS,
|
||||
cbz: ContentTypes.BOOKS,
|
||||
cb7: ContentTypes.BOOKS,
|
||||
cbt: ContentTypes.BOOKS,
|
||||
cba: ContentTypes.BOOKS,
|
||||
lrf: ContentTypes.BOOKS,
|
||||
lrx: ContentTypes.BOOKS,
|
||||
chm: ContentTypes.BOOKS,
|
||||
djvu: ContentTypes.BOOKS,
|
||||
doc: ContentTypes.BOOKS,
|
||||
docx: ContentTypes.BOOKS,
|
||||
epub: ContentTypes.BOOKS,
|
||||
pdf: ContentTypes.BOOKS,
|
||||
pdb: ContentTypes.BOOKS,
|
||||
fb2: ContentTypes.BOOKS,
|
||||
xeb: ContentTypes.BOOKS,
|
||||
ceb: ContentTypes.BOOKS,
|
||||
htm: ContentTypes.BOOKS,
|
||||
html: ContentTypes.BOOKS,
|
||||
css: ContentTypes.BOOKS,
|
||||
txt: ContentTypes.BOOKS,
|
||||
ibooks: ContentTypes.BOOKS,
|
||||
inf: ContentTypes.BOOKS,
|
||||
azw3: ContentTypes.BOOKS,
|
||||
azw: ContentTypes.BOOKS,
|
||||
kf8: ContentTypes.BOOKS,
|
||||
lit: ContentTypes.BOOKS,
|
||||
prc: ContentTypes.BOOKS,
|
||||
mobi: ContentTypes.BOOKS,
|
||||
opf: ContentTypes.BOOKS,
|
||||
txt: ContentTypes.BOOKS,
|
||||
pdb: ContentTypes.BOOKS,
|
||||
rtf: ContentTypes.BOOKS,
|
||||
pdg: ContentTypes.BOOKS,
|
||||
xml: ContentTypes.BOOKS,
|
||||
tr2: ContentTypes.BOOKS,
|
||||
tr3: ContentTypes.BOOKS,
|
||||
oxps: ContentTypes.BOOKS,
|
||||
xps: ContentTypes.BOOKS,
|
||||
|
||||
exe: ContentTypes.APPLICATION,
|
||||
apk: ContentTypes.APPLICATION,
|
||||
rpm: ContentTypes.APPLICATION,
|
||||
deb: ContentTypes.APPLICATION,
|
||||
jar: ContentTypes.APPLICATION,
|
||||
bundle: ContentTypes.APPLICATION,
|
||||
com: ContentTypes.APPLICATION,
|
||||
so: ContentTypes.APPLICATION,
|
||||
dll: ContentTypes.APPLICATION,
|
||||
elf: ContentTypes.APPLICATION,
|
||||
ipa: ContentTypes.APPLICATION,
|
||||
xbe: ContentTypes.APPLICATION,
|
||||
xap: ContentTypes.APPLICATION,
|
||||
a: ContentTypes.APPLICATION,
|
||||
bin: ContentTypes.APPLICATION,
|
||||
msi: ContentTypes.APPLICATION,
|
||||
dmg: ContentTypes.APPLICATION,
|
||||
pbi: ContentTypes.APPLICATION,
|
||||
|
||||
tar: ContentTypes.ARCHIVE,
|
||||
gz: ContentTypes.ARCHIVE,
|
||||
bz2: ContentTypes.ARCHIVE,
|
||||
rar: ContentTypes.ARCHIVE,
|
||||
zip: ContentTypes.ARCHIVE,
|
||||
lz: ContentTypes.ARCHIVE,
|
||||
lzma: ContentTypes.ARCHIVE,
|
||||
lzo: ContentTypes.ARCHIVE,
|
||||
rz: ContentTypes.ARCHIVE,
|
||||
sfark: ContentTypes.ARCHIVE,
|
||||
sf2: ContentTypes.ARCHIVE,
|
||||
xz: ContentTypes.ARCHIVE,
|
||||
z: ContentTypes.ARCHIVE,
|
||||
'7z': ContentTypes.ARCHIVE,
|
||||
s7z: ContentTypes.ARCHIVE,
|
||||
ace: ContentTypes.ARCHIVE,
|
||||
afa: ContentTypes.ARCHIVE,
|
||||
arc: ContentTypes.ARCHIVE,
|
||||
ace: ContentTypes.ARCHIVE,
|
||||
arj: ContentTypes.ARCHIVE,
|
||||
b1: ContentTypes.ARCHIVE,
|
||||
car: ContentTypes.ARCHIVE,
|
||||
cfs: ContentTypes.ARCHIVE,
|
||||
cpt: ContentTypes.ARCHIVE,
|
||||
dar: ContentTypes.ARCHIVE,
|
||||
ice: ContentTypes.ARCHIVE,
|
||||
sfx: ContentTypes.ARCHIVE,
|
||||
shk: ContentTypes.ARCHIVE,
|
||||
sit: ContentTypes.ARCHIVE,
|
||||
tgz: ContentTypes.ARCHIVE,
|
||||
xar: ContentTypes.ARCHIVE,
|
||||
zz: ContentTypes.ARCHIVE,
|
||||
|
||||
iso: ContentTypes.DISC,
|
||||
mdf: ContentTypes.DISC,
|
||||
mds: ContentTypes.DISC,
|
||||
nrg: ContentTypes.DISC,
|
||||
ima: ContentTypes.DISC,
|
||||
imz: ContentTypes.DISC,
|
||||
mdx: ContentTypes.DISC,
|
||||
uif: ContentTypes.DISC,
|
||||
isz: ContentTypes.DISC,
|
||||
daa: ContentTypes.DISC,
|
||||
cso: ContentTypes.DISC,
|
||||
cue: ContentTypes.DISC,
|
||||
fvd: ContentTypes.DISC,
|
||||
ndif: ContentTypes.DISC,
|
||||
udif: ContentTypes.DISC,
|
||||
vdi: ContentTypes.DISC,
|
||||
vhd: ContentTypes.DISC,
|
||||
wim: ContentTypes.DISC,
|
||||
};
|
||||
|
||||
const ContentTypeProp = 'contentType';
|
||||
const ContentCategoryProp = 'contentCategory';
|
||||
|
||||
const XXX_BLOCK_WORDS = require('./bad-words');
|
||||
|
||||
// блокируем порнографию
|
||||
const blockBadName = (torrent, name) => {
|
||||
let splitName = name.split(/[`~!@#$%^&*()\]\[{}.,+?/\\;:\-_' "|]/);
|
||||
splitName.some((word) => {
|
||||
if (XXX_BLOCK_WORDS.some(function(v) { return word == v; })) {
|
||||
torrent[ContentCategoryProp] = 'xxx';
|
||||
}
|
||||
return torrent[ContentCategoryProp] == 'xxx';
|
||||
})
|
||||
}
|
||||
|
||||
const detectSubCategory = (torrent, files, typesPriority, contentType) => {
|
||||
let name = torrent.name.toLowerCase()
|
||||
|
||||
// блокируем порнографию
|
||||
if(contentType == ContentTypes.VIDEO || contentType == ContentTypes.PICTURES || contentType == ContentTypes.ARCHIVE)
|
||||
{
|
||||
blockBadName(torrent, name);
|
||||
// блокируем так по названию файлов
|
||||
if(torrent[ContentCategoryProp] != 'xxx')
|
||||
{
|
||||
files.some(({path}) => {
|
||||
let fileCheck = path.toLowerCase().split('.');
|
||||
if(fileCheck.length > 1)
|
||||
fileCheck.pop(); // убираем расширение на конце
|
||||
fileCheck = fileCheck.join('.');
|
||||
|
||||
blockBadName(torrent, fileCheck);
|
||||
if(torrent[ContentCategoryProp] == 'xxx')
|
||||
{
|
||||
console.log('block because file ' + path);
|
||||
}
|
||||
return torrent[ContentCategoryProp] == 'xxx';
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fileTypeDetect = (file) => {
|
||||
let name = file.path.split('/').pop();
|
||||
let extension = name.split('.').pop();
|
||||
if(name.length == 0)
|
||||
return;
|
||||
if(extension.length == 0)
|
||||
return;
|
||||
extension = extension.toLowerCase();
|
||||
|
||||
return ExtesionBase[extension];
|
||||
}
|
||||
|
||||
const torrentTypeDetect = (torrent, files) => {
|
||||
let typesPriority = {};
|
||||
|
||||
for(let i = 0; i < files.length; i++) {
|
||||
let file = files[i];
|
||||
let type = fileTypeDetect(file)
|
||||
|
||||
if(type) {
|
||||
if(!typesPriority[type])
|
||||
typesPriority[type] = 0.;
|
||||
|
||||
typesPriority[type] += file.size / torrent.size;
|
||||
}
|
||||
}
|
||||
let priority = Object.keys(typesPriority).sort(function(a, b){
|
||||
return typesPriority[b] - typesPriority[a]
|
||||
});
|
||||
if(priority.length > 0)
|
||||
torrent[ContentTypeProp] = priority[0];
|
||||
|
||||
detectSubCategory(torrent, files, typesPriority, torrent[ContentTypeProp]);
|
||||
}
|
||||
|
||||
module.exports = {torrentTypeDetect, fileTypeDetect};
|
256
src/app/css/izi/animations.css
Normal file
256
src/app/css/izi/animations.css
Normal file
@ -0,0 +1,256 @@
|
||||
.animated {
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.scale0 {
|
||||
transform: scale(0,0);
|
||||
}
|
||||
|
||||
.scale1 {
|
||||
transform: scale(1,1);
|
||||
}
|
||||
|
||||
|
||||
/* - - - - - - - - - - - - - - -
|
||||
|
||||
АНИМАЦИИ
|
||||
|
||||
- - - - - - - - - - - - - - - - */
|
||||
|
||||
.an-zoom-in {
|
||||
animation: zoom-in 0.2s;
|
||||
}
|
||||
|
||||
@keyframes zoom-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0, 0);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.an-show-op {
|
||||
animation: show-op 0.2s;
|
||||
}
|
||||
|
||||
@keyframes show-op {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.an-slide-r {
|
||||
animation: slide-r 0.2s;
|
||||
}
|
||||
|
||||
@keyframes slide-r {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(+50px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateX(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.an-slide-l {
|
||||
animation: slide-l 0.2s;
|
||||
}
|
||||
|
||||
@keyframes slide-l {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(-50px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateX(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.an-slide-t {
|
||||
animation: slide-t 0.2s;
|
||||
}
|
||||
|
||||
@keyframes slide-t {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-50px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.an-slide-b {
|
||||
animation: slide-b 0.2s;
|
||||
}
|
||||
|
||||
@keyframes slide-b {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(50px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.an-flare-red {
|
||||
animation: flare-red;
|
||||
}
|
||||
|
||||
@keyframes flare-red {
|
||||
0% {
|
||||
box-shadow: 0px 0px 0px 10px rgba(255, 42, 42, 0.0);
|
||||
}
|
||||
30% {
|
||||
box-shadow: 0px 0px 0px 25px rgba(255, 42, 42, 0.2);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0px 0px 0px 50px rgba(255, 42, 42, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
.an-flare-green {
|
||||
animation: flare-green;
|
||||
}
|
||||
|
||||
@keyframes flare-green {
|
||||
0% {
|
||||
box-shadow: 0px 0px 0px 10px rgba(54, 215, 146, 0.0);
|
||||
}
|
||||
30% {
|
||||
box-shadow: 0px 0px 0px 25px rgba(54, 215, 146, 0.2);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0px 0px 0px 50px rgba(54, 215, 146, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
.an-flare-glam {
|
||||
animation: flare-glam;
|
||||
}
|
||||
|
||||
@keyframes flare-glam {
|
||||
0% {
|
||||
box-shadow: 0px 0px 0px 10px rgba(210, 95, 210, 0.0);
|
||||
}
|
||||
30% {
|
||||
box-shadow: 0px 0px 0px 25px rgba(210, 95, 210, 0.25);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0px 0px 0px 50px rgba(210, 95, 210, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
.an-flare-orange {
|
||||
animation: flare-orange;
|
||||
}
|
||||
|
||||
@keyframes flare-orange {
|
||||
0% {
|
||||
box-shadow: 0px 0px 0px 10px rgba(255, 173, 64, 0.0);
|
||||
}
|
||||
30% {
|
||||
box-shadow: 0px 0px 0px 25px rgba(255, 173, 64, 0.25);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0px 0px 0px 50px rgba(255, 173, 64, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
.an-flare-blue {
|
||||
animation: flare-blue;
|
||||
}
|
||||
|
||||
@keyframes flare-blue {
|
||||
0% {
|
||||
box-shadow: 0px 0px 0px 10px rgba(31,225,227, 0.0);
|
||||
}
|
||||
30% {
|
||||
box-shadow: 0px 0px 0px 25px rgba(31,225,227, 0.25);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0px 0px 0px 50px rgba(31,225,227, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.an0-25 {
|
||||
animation-duration: 0.25s;
|
||||
}
|
||||
|
||||
.an0-5 {
|
||||
animation-duration: 0.5s;
|
||||
}
|
||||
|
||||
.an0-75 {
|
||||
animation-duration: 0.75s;
|
||||
}
|
||||
|
||||
.an1 {
|
||||
animation-duration: 1s;
|
||||
}
|
||||
|
||||
.an1-5 {
|
||||
animation-duration: 1.5s;
|
||||
}
|
||||
|
||||
.an2 {
|
||||
animation-duration: 2s;
|
||||
}
|
||||
|
||||
.an2-5 {
|
||||
animation-duration: 2.5s;
|
||||
}
|
||||
|
||||
.an-linear {
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.an-infinite {
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
/* точечный спиннер */
|
||||
|
||||
.spinner {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.spinner > div {
|
||||
width: 0.5em;
|
||||
height: 0.5em;
|
||||
margin: 0.25em;
|
||||
background-color: #dedede;
|
||||
border-radius: 100%;
|
||||
display: inline-block;
|
||||
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.spinner .bounce1 {
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
.spinner .bounce2 {
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
@keyframes sk-bouncedelay {
|
||||
0%, 80%, 100% {
|
||||
transform: scale(0);
|
||||
} 40% {
|
||||
transform: scale(1.0);
|
||||
}
|
||||
}
|
86
src/app/css/izi/components.css
Normal file
86
src/app/css/izi/components.css
Normal file
@ -0,0 +1,86 @@
|
||||
|
||||
.torrent-information-row .info-table {
|
||||
flex-basis: 60%;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px)
|
||||
{
|
||||
.torrent-information-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
.torrent-information-row .info-table {
|
||||
flex-basis: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 445px)
|
||||
{
|
||||
.recent-title {
|
||||
padding-left: 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 510px)
|
||||
{
|
||||
.donation-line {
|
||||
width: 65%;
|
||||
}
|
||||
}
|
||||
|
||||
.recent-torrents {
|
||||
width: 60%
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1250px)
|
||||
{
|
||||
.recent-torrents {
|
||||
width: 68%
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1100px)
|
||||
{
|
||||
.recent-torrents {
|
||||
width: 75%
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px)
|
||||
{
|
||||
.recent-torrents {
|
||||
width: 90%
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 650px)
|
||||
{
|
||||
.recent-torrents {
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
|
||||
.filter-control-border {
|
||||
top: -10px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 530px)
|
||||
{
|
||||
.filter-control-row {
|
||||
flex-wrap: wrap;
|
||||
padding-top: 28px;
|
||||
}
|
||||
|
||||
.filter-row {
|
||||
padding-top: 30px;
|
||||
align-items: inherit !important;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-control-border {
|
||||
padding-top: 5px;
|
||||
top: 6px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
}
|
178
src/app/css/izi/flex.css
Normal file
178
src/app/css/izi/flex.css
Normal file
@ -0,0 +1,178 @@
|
||||
/* FLEX */
|
||||
|
||||
.row, .column {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.row.reverse {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.column.reverse {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.row.center,
|
||||
.column.inline {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.column.center,
|
||||
.row.inline {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.row.stretch,
|
||||
.column.stretch {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.row.top,
|
||||
.column.left {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.row.bottom,
|
||||
.colum.right {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.row > .self-top {
|
||||
align-self: flex-end;
|
||||
}
|
||||
.row > .self-bottom {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.column > .self-right {
|
||||
align-self: flex-end;
|
||||
}
|
||||
.column > .self-left {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.self-center {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.self-stretch {
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.row.space-between,
|
||||
.column.space-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.row.space-around,
|
||||
.column.space-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
|
||||
.ml-auto {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.mt-auto {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.jc-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.shrink0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.shrink1 {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.baseline {
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
/* поток */
|
||||
|
||||
.column.indent0-25 > *:not(:first-child):not(.ripple) {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
.column.indent0-5 > *:not(:first-child):not(.ripple) {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
.column.indent0-75 > *:not(:first-child):not(.ripple) {
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
.column.indent1 > *:not(:first-child):not(.ripple) {
|
||||
margin-top: 1em;
|
||||
}
|
||||
.column.indent1-25 > *:not(:first-child):not(.ripple) {
|
||||
margin-top: 1.25em;
|
||||
}
|
||||
.column.indent1-5 > *:not(:first-child):not(.ripple) {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
.column.indent1-75 > *:not(:first-child):not(.ripple) {
|
||||
margin-top: 1.75em;
|
||||
}
|
||||
.column.indent2 > *:not(:first-child):not(.ripple) {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
/* строка с остступами */
|
||||
|
||||
.row.indent0-25 > *:not(:first-child):not(.ripple) {
|
||||
margin-left: 0.25em;
|
||||
}
|
||||
.row.indent0-5 > *:not(:first-child):not(.ripple) {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
.row.indent0-75 > *:not(:first-child):not(.ripple) {
|
||||
margin-left: 0.75em;
|
||||
}
|
||||
|
||||
.row.indent1 > *:not(:first-child):not(.ripple) {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.row.indent1-25 > *:not(:first-child):not(.ripple) {
|
||||
margin-left: 1.25em;
|
||||
}
|
||||
.row.indent1-5 > *:not(:first-child):not(.ripple) {
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
.row.indent1-75 > *:not(:first-child):not(.ripple) {
|
||||
margin-left: 1.75em;
|
||||
}
|
||||
.row.indent2 > *:not(:first-child):not(.ripple) {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
.row.indent.wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.row.indent.wrap > * {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
|
||||
/* размеры */
|
||||
|
||||
.row > .full-size, .column > .full-size {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
.row.solid > *:not(.soft), .column.solid > *:not(.soft) {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
}
|
164
src/app/css/izi/inputs.css
Normal file
164
src/app/css/izi/inputs.css
Normal file
@ -0,0 +1,164 @@
|
||||
/* переключалка */
|
||||
|
||||
[data-switch]:not(.butt):not(.show) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ---- инпуты ---- */
|
||||
|
||||
|
||||
|
||||
|
||||
input[type="text"],
|
||||
input[type="email"],
|
||||
input[type="password"] {
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
background: none;
|
||||
outline: none;
|
||||
border: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 0;
|
||||
font-family: inherit;
|
||||
font-size: 1em;
|
||||
background-color: transparent;
|
||||
width: 100%;
|
||||
border: none;
|
||||
color: inherit;
|
||||
}
|
||||
select:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
width: 100%;
|
||||
|
||||
resize: none;
|
||||
background: none;
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* обычный инпут */
|
||||
|
||||
.input-normal {
|
||||
font-size: 1em;
|
||||
padding: 0.75em;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.input-normal > *:not(:first-child) {
|
||||
margin-left: 0.75em;
|
||||
}
|
||||
|
||||
.input-normal svg {
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
/* лэйбл-инпут */
|
||||
|
||||
.input-label {
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
font-size: 1em;
|
||||
height: 4em;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.input-label .combo {
|
||||
position: relative;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-label .combo > * {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.input-label .placeholder {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.input-label.focused .placeholder {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
.input-label input {
|
||||
transform: scale(1, 0);
|
||||
}
|
||||
|
||||
.input-label.focused input {
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
|
||||
.input-label .underline {
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
border-bottom: 1px solid;
|
||||
transform: translateY(0.5em);
|
||||
}
|
||||
|
||||
.input-label.focused .underline {
|
||||
width: 100%;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* чекбокс-инпут */
|
||||
|
||||
label.input-checkbox {
|
||||
cursor: pointer;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
label.input-checkbox .switcher {
|
||||
position: relative;
|
||||
width: 2em;
|
||||
height: 1em;
|
||||
border-radius: 1em;
|
||||
padding: 0;
|
||||
background: #d3d3d3;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
label.input-checkbox .switcher::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.input-checkbox input[type="checkbox"]:checked ~ .switcher::before {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.input-checkbox input[type="checkbox"]:checked ~ .switcher {
|
||||
background: #3F51B5;
|
||||
}
|
||||
|
||||
.input-checkbox input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
480
src/app/css/izi/izi.css
Normal file
480
src/app/css/izi/izi.css
Normal file
@ -0,0 +1,480 @@
|
||||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*,
|
||||
:before,
|
||||
:after {
|
||||
box-sizing: inherit;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
-webkit-touch-callout: none;
|
||||
/* -webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
*/
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Roboto, Arial, Helvetica, sans-serif;
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
/* background: #ededed; */
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
svg.iconmonstr {
|
||||
padding: 0.10em;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* фикс мерцания */
|
||||
|
||||
.flick-fix {
|
||||
-webkit-backface-visibility: hidden;
|
||||
}
|
||||
|
||||
/* ссылки */
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* примитивные позиции */
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.absolute.full-size {
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
/* указатель на нажатие */
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
z-index: 0;
|
||||
transition: 0.5s;
|
||||
-webkit-user-select: none; /* Chrome all / Safari all */
|
||||
-moz-user-select: none; /* Firefox all */
|
||||
-ms-user-select: none; /* IE 10+ */
|
||||
user-select: none; /* Likely future */
|
||||
}
|
||||
|
||||
.clickable .ripple {
|
||||
display: block;
|
||||
position: absolute;
|
||||
border-radius: 100%;
|
||||
transform:scale(0);
|
||||
}
|
||||
|
||||
[class*="-bright-b"] .ripple {
|
||||
background:rgba(80, 80, 80, 0.20);
|
||||
}
|
||||
[class*="-dark-b"] .ripple {
|
||||
background:rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.an-ripple {
|
||||
animation:ripple 0.65s linear;
|
||||
}
|
||||
|
||||
@keyframes ripple {
|
||||
100% {
|
||||
opacity: 0; transform: scale(2.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* разделитель */
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
[class*="-bright-b"] .divider:not([class*="-dark-c"]) {
|
||||
color: rgba(0,0,0,0.07);
|
||||
}
|
||||
|
||||
[class*="-dark-b"] .divider:not([class*="-bright-c"]) {
|
||||
color: rgba(255,255,255,0.07);
|
||||
}
|
||||
|
||||
.border1 {
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
[class*="-bright-b"] .border1 {
|
||||
border-color: rgba(0,0,0,0.10);
|
||||
}
|
||||
|
||||
[class*="-dark-b"] .border1 {
|
||||
border-color: rgba(255,255,255,0.10);
|
||||
}
|
||||
|
||||
/* eщё разделитель */
|
||||
|
||||
[class*="-bright-b"] .underlined {
|
||||
border-bottom: 1px solid rgba(0,0,0,0.07);
|
||||
}
|
||||
|
||||
[class*="-dark-b"] .underlined {
|
||||
border-bottom: 1px solid rgba(255,255,255,0.07);
|
||||
}
|
||||
|
||||
[class*="-bright-b"] .upperlined {
|
||||
border-top: 1px solid rgba(0,0,0,0.07);
|
||||
}
|
||||
|
||||
[class*="-dark-b"] .upperlined {
|
||||
border-top: 1px solid rgba(255,255,255,0.07);
|
||||
}
|
||||
|
||||
/* круг */
|
||||
|
||||
.round {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* закруглённые углы */
|
||||
|
||||
.rounded {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* вот это повороты */
|
||||
|
||||
.rotate90 {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.rotate180 {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* текст без переносов */
|
||||
|
||||
.text {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.text-nowrap {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.text-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* переносы по буквам */
|
||||
|
||||
.break-word {
|
||||
word-break: break-word;
|
||||
min-width:0;
|
||||
}
|
||||
|
||||
/* upper-case */
|
||||
|
||||
.u-case {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* прокрутка */
|
||||
|
||||
.scroll {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.scrollY {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.scrollX {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
|
||||
РАЗМЕРЫ
|
||||
|
||||
--------------------- */
|
||||
|
||||
.full-size {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.w100p {
|
||||
width: 100%;
|
||||
}
|
||||
.h100p {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.w50p {
|
||||
width: 50%;
|
||||
}
|
||||
.h50p {
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
.mw100p {
|
||||
max-width: 100%;
|
||||
}
|
||||
.mh100p {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.w1em {
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
.h1em {
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.sz1em {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
|
||||
ПОЗИЦИИ
|
||||
|
||||
--------------------- */
|
||||
|
||||
.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.top0 {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.left0 {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.right0 {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.bottom0 {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
|
||||
ТЕНИ
|
||||
|
||||
--------------------- */
|
||||
|
||||
.shadow1 {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.shadow2 {
|
||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
|
||||
}
|
||||
|
||||
.shadow3 {
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
|
||||
}
|
||||
|
||||
.shadow4 {
|
||||
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
|
||||
}
|
||||
|
||||
.shadow5 {
|
||||
box-shadow: 0 19px 38px rgba(0, 0, 0, 0.30), 0 15px 12px rgba(0, 0, 0, 0.22);
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
|
||||
ПРОЗРАЧНОСТЬ
|
||||
|
||||
--------------------- */
|
||||
|
||||
.op75 {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.op5 {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.op25 {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
|
||||
МАРГИНЫ
|
||||
|
||||
--------------------- */
|
||||
|
||||
.mar0-25 {
|
||||
margin: 0.25em;
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
|
||||
ПАДДИНГИ
|
||||
|
||||
--------------------- */
|
||||
|
||||
.pad0-25 {
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
||||
.pad0-5 {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.pad0-75 {
|
||||
padding: 0.75em;
|
||||
}
|
||||
|
||||
.pad1 {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.pad1-25 {
|
||||
padding: 1.25em;
|
||||
}
|
||||
|
||||
.pad1-5 {
|
||||
padding: 1.5em;
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
|
||||
РАЗМЕРЫ ШРИФТА
|
||||
|
||||
--------------------- */
|
||||
|
||||
.fs0-25 {
|
||||
font-size: 0.25em;
|
||||
}
|
||||
.fs0-5 {
|
||||
font-size: 0.5em;
|
||||
}
|
||||
.fs0-65 {
|
||||
font-size: 0.65em;
|
||||
}
|
||||
.fs0-75 {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
.fs0-85 {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
.fs1 {
|
||||
font-size: 1em;
|
||||
}
|
||||
.fs1-15 {
|
||||
font-size: 1.15em;
|
||||
}
|
||||
|
||||
.fs1-25 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
.fs1-5 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.fs1-75 {
|
||||
font-size: 1.75em;
|
||||
}
|
||||
|
||||
.fs2 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.fs2-25 {
|
||||
font-size: 2.25em;
|
||||
}
|
||||
.fs2-5 {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
.fs2-75 {
|
||||
font-size: 2.75em;
|
||||
}
|
||||
|
||||
.fs3 {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
.fs3-25 {
|
||||
font-size: 3.25em;
|
||||
}
|
||||
.fs3-5 {
|
||||
font-size: 3em;
|
||||
}
|
||||
.fs3-75 {
|
||||
font-size: 3.75em;
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
|
||||
Z-ИНДЕКС
|
||||
|
||||
--------------------- */
|
||||
|
||||
.z0 {
|
||||
z-index: 0
|
||||
}
|
||||
.z1 {
|
||||
z-index: 1
|
||||
}
|
||||
.z2 {
|
||||
z-index: 2
|
||||
}
|
||||
.z3 {
|
||||
z-index: 3
|
||||
}
|
||||
.z4 {
|
||||
z-index: 4
|
||||
}
|
||||
.z5 {
|
||||
z-index: 5
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
461
src/app/css/izi/material-palette.css
Normal file
461
src/app/css/izi/material-palette.css
Normal file
@ -0,0 +1,461 @@
|
||||
/* - - - - - - - - - - - - -
|
||||
|
||||
ПАЛИТРА
|
||||
|
||||
- - - - - - - - - - - - - */
|
||||
|
||||
.veil-black-dark-c {
|
||||
color: #303030;
|
||||
fill: #303030;
|
||||
}
|
||||
|
||||
.veil-black-dark-b {
|
||||
background-color: #303030;
|
||||
}
|
||||
|
||||
/* DARK BLUE */
|
||||
|
||||
.dark-blue-dark-c {
|
||||
color: #151b27;
|
||||
fill: #151b27;
|
||||
}
|
||||
|
||||
.dark-blue-dark-b {
|
||||
background-color: #151b27;
|
||||
}
|
||||
|
||||
/* ЧЁРНЫЙ */
|
||||
|
||||
.black-dark-c {
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
fill: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
[class*="-bright-b"] {
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
fill:rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
[class*="-bright-b"] .sec-c {
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
[class*="-bright-b"] .dis-c {
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
fill:rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
|
||||
[class*="-bright-b"] ::placeholder {
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
|
||||
[class*="-bright-b"] .icon svg {
|
||||
fill:rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
[class*="-bright-b"] .dis-c .icon svg {
|
||||
fill:rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
|
||||
.black-dark-b {
|
||||
background-color: rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
/* БЕЛЫЙ */
|
||||
|
||||
.veil-white {
|
||||
background: rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
.white-bright-c {
|
||||
color: rgba(255,255,255,1);
|
||||
fill: rgba(255,255,255,1);
|
||||
}
|
||||
|
||||
[class*="-dark-b"] {
|
||||
color: rgb(255,255,255);
|
||||
fill: rgb(255, 255, 255);
|
||||
}
|
||||
[class*="-dark-b"] .sec-c {
|
||||
color: rgba(255,255,255,0.70);
|
||||
fill: rgba(255,255,255,0.70);
|
||||
}
|
||||
[class*="-dark-b"] .dis-c {
|
||||
color: rgba(255,255,255,0.50);
|
||||
fill: rgba(255,255,255,0.50);
|
||||
}
|
||||
|
||||
[class*="-dark-b"] ::placeholder {
|
||||
color: rgba(255,255,255,0.50);
|
||||
}
|
||||
|
||||
[class*="-dark-b"] .icon svg {
|
||||
fill: rgb(255,255,255);
|
||||
}
|
||||
[class*="-dark-b"] .dis-c .icon svg {
|
||||
fill: rgba(255,255,255,0.50);
|
||||
}
|
||||
|
||||
.white-bright-b {
|
||||
background-color: rgb(255,255,255);
|
||||
}
|
||||
|
||||
|
||||
/* RED */
|
||||
|
||||
.red-dark-c {
|
||||
color: #F44336;
|
||||
fill: #F44336;
|
||||
}
|
||||
|
||||
.red-dark-b {
|
||||
background-color: #F44336;
|
||||
}
|
||||
|
||||
.red-bright-c {
|
||||
color: #FF8A80;
|
||||
fill: #FF8A80;
|
||||
}
|
||||
|
||||
.red-bright-b {
|
||||
background-color: #FF8A80;
|
||||
}
|
||||
|
||||
|
||||
/* PINK */
|
||||
|
||||
.pink-dark-c {
|
||||
color: #E91E63;
|
||||
fill: #E91E63;
|
||||
}
|
||||
|
||||
.pink-dark-b {
|
||||
background-color: #E91E63;
|
||||
}
|
||||
|
||||
.pink-bright-c {
|
||||
color: #FF80AB;
|
||||
fill: #FF80AB;
|
||||
}
|
||||
|
||||
.pink-bright-b {
|
||||
background-color: #FF80AB;
|
||||
}
|
||||
|
||||
|
||||
/* PURPLE */
|
||||
|
||||
.purple-dark-c {
|
||||
color: #9C27B0;
|
||||
fill: #9C27B0;
|
||||
}
|
||||
|
||||
.purple-dark-b {
|
||||
background-color: #9C27B0;
|
||||
}
|
||||
|
||||
.purple-bright-c {
|
||||
color: #EA80FC;
|
||||
fill: #EA80FC;
|
||||
}
|
||||
|
||||
.purple-bright-b {
|
||||
background-color: #E040FB;
|
||||
}
|
||||
|
||||
|
||||
/* DEEP PURPLE */
|
||||
|
||||
.dpurple-dark-c {
|
||||
color: #673AB7;
|
||||
fill: #673AB7;
|
||||
}
|
||||
|
||||
.dpurple-dark-b {
|
||||
background-color: #673AB7;
|
||||
}
|
||||
|
||||
.dpurple-bright-c {
|
||||
color: #B388FF;
|
||||
fill: #B388FF;
|
||||
}
|
||||
|
||||
.dpurple-bright-b {
|
||||
background-color: #B388FF;
|
||||
}
|
||||
|
||||
|
||||
/* INDIGO */
|
||||
|
||||
.indigo-dark-c {
|
||||
color: #3F51B5;
|
||||
fill: #3F51B5;
|
||||
}
|
||||
|
||||
.indigo-dark-b {
|
||||
background-color: #3F51B5;
|
||||
}
|
||||
|
||||
.indigo-bright-c {
|
||||
color: #8C9EFF;
|
||||
fill: #8C9EFF;
|
||||
}
|
||||
|
||||
.indigo-bright-b {
|
||||
background-color: #8C9EFF;
|
||||
}
|
||||
|
||||
|
||||
/* BLUE */
|
||||
|
||||
.blue-dark-c {
|
||||
color: #2196F3;
|
||||
fill: #2196F3;
|
||||
}
|
||||
|
||||
.blue-dark-b {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
.blue-bright-c {
|
||||
color: #82B1FF;
|
||||
fill: #82B1FF;
|
||||
}
|
||||
|
||||
.blue-bright-b {
|
||||
background-color: #82B1FF;
|
||||
}
|
||||
|
||||
|
||||
/* LIGHT BLUE */
|
||||
|
||||
.lblue-dark-c {
|
||||
color: #039BE5;
|
||||
fill: #039BE5;
|
||||
}
|
||||
|
||||
.lblue-dark-b {
|
||||
background-color: #039BE5;
|
||||
}
|
||||
|
||||
.lblue-bright-c {
|
||||
color: #40C4FF;
|
||||
fill: #40C4FF;
|
||||
}
|
||||
|
||||
.lblue-bright-b {
|
||||
background-color: #40C4FF;
|
||||
}
|
||||
|
||||
|
||||
/* CYAN */
|
||||
|
||||
.cyan-dark-c {
|
||||
color: #0097A7;
|
||||
fill: #0097A7;
|
||||
}
|
||||
|
||||
.cyan-dark-b {
|
||||
background-color: #0097A7;
|
||||
}
|
||||
|
||||
.cyan-bright-c {
|
||||
color: #18FFFF;
|
||||
fill: #18FFFF;
|
||||
}
|
||||
|
||||
.cyan-bright-b {
|
||||
background-color: #18FFFF;
|
||||
}
|
||||
|
||||
|
||||
/* TYAL */
|
||||
|
||||
.teal-dark-c {
|
||||
color: #009688;
|
||||
fill: #009688;
|
||||
}
|
||||
|
||||
.teal-dark-b {
|
||||
background-color: #009688;
|
||||
}
|
||||
|
||||
.teal-bright-c {
|
||||
color: #64FFDA;
|
||||
fill: #64FFDA;
|
||||
}
|
||||
|
||||
.teal-bright-b {
|
||||
background-color: #64FFDA;
|
||||
}
|
||||
|
||||
|
||||
/* GREEN */
|
||||
|
||||
.green-dark-c {
|
||||
color: #43A047;
|
||||
fill: #43A047;
|
||||
}
|
||||
|
||||
.green-dark-b {
|
||||
background-color: #43A047;
|
||||
}
|
||||
|
||||
.green-bright-c {
|
||||
color: #69F0AE;
|
||||
fill: #69F0AE;
|
||||
}
|
||||
|
||||
.green-bright-b {
|
||||
background-color: #69F0AE;
|
||||
}
|
||||
|
||||
|
||||
/* LIGHT GREEN */
|
||||
|
||||
.lgreen-dark-c {
|
||||
color: #689F38;
|
||||
fill: #689F38;
|
||||
}
|
||||
|
||||
.lgreen-dark-b {
|
||||
background-color: #689F38;
|
||||
}
|
||||
|
||||
.lgreen-bright-c {
|
||||
color: #B2FF59;
|
||||
fill: #B2FF59;
|
||||
}
|
||||
|
||||
.lgreen-bright-b {
|
||||
background-color: #B2FF59;
|
||||
}
|
||||
|
||||
|
||||
/* LIME */
|
||||
|
||||
.lime-dark-c {
|
||||
color: #827717;
|
||||
fill: #827717;
|
||||
}
|
||||
|
||||
.lime-dark-b {
|
||||
background-color: #827717;
|
||||
}
|
||||
|
||||
.lime-bright-c {
|
||||
color: #EEFF41;
|
||||
fill: #EEFF41;
|
||||
}
|
||||
|
||||
.lime-bright-b {
|
||||
background-color: #EEFF41;
|
||||
}
|
||||
|
||||
|
||||
/* YELLOW */
|
||||
|
||||
.yellow-bright-c {
|
||||
color: #FFFF00;
|
||||
fill: #FFFF00;
|
||||
}
|
||||
|
||||
.yellow-bright-b {
|
||||
background-color: #FFFF00;
|
||||
}
|
||||
|
||||
|
||||
/* AMBER */
|
||||
|
||||
.amber-bright-c {
|
||||
color: #FFD740;
|
||||
fill: #FFD740;
|
||||
}
|
||||
|
||||
.amber-bright-b {
|
||||
background-color: #FFD740;
|
||||
}
|
||||
|
||||
|
||||
/* ORANGE */
|
||||
|
||||
.orange-dark-c {
|
||||
color: #EF6C00;
|
||||
fill: #EF6C00;
|
||||
}
|
||||
|
||||
.orange-dark-b {
|
||||
background-color: #EF6C00;
|
||||
}
|
||||
|
||||
.orange-bright-c {
|
||||
color: #FFAB40;
|
||||
fill: #FFAB40;
|
||||
}
|
||||
|
||||
.orange-bright-b {
|
||||
background-color: #FFAB40;
|
||||
}
|
||||
|
||||
|
||||
/* DEEP ORANGE */
|
||||
|
||||
.dorange-dark-c {
|
||||
color: #FF5722;
|
||||
fill: #FF5722;
|
||||
}
|
||||
|
||||
.dorange-dark-b {
|
||||
background-color: #FF5722;
|
||||
}
|
||||
|
||||
.dorange-bright-c {
|
||||
color: #FF6E40;
|
||||
fill: #FF6E40;
|
||||
}
|
||||
|
||||
.dorange-bright-b {
|
||||
background-color: #FF6E40;
|
||||
}
|
||||
|
||||
|
||||
/* BROWN */
|
||||
|
||||
.brown-dark-c {
|
||||
color: #795548;
|
||||
fill: #795548;
|
||||
}
|
||||
|
||||
.brown-dark-b {
|
||||
background-color: #795548;
|
||||
}
|
||||
|
||||
|
||||
/* GREY */
|
||||
|
||||
.grey-dark-c {
|
||||
color: #757575;
|
||||
fill: #757575;
|
||||
}
|
||||
|
||||
.grey-dark-b {
|
||||
background-color: #757575;
|
||||
}
|
||||
|
||||
.grey-bright-c {
|
||||
color: #9E9E9E;
|
||||
fill: #9E9E9E;
|
||||
}
|
||||
|
||||
.grey-bright-b {
|
||||
background-color: #9E9E9E;
|
||||
}
|
||||
|
||||
|
||||
/* BLUE GREY */
|
||||
|
||||
.bgrey-dark-c {
|
||||
color: #607D8B;
|
||||
fill: #607D8B;
|
||||
}
|
||||
|
||||
.bgrey-dark-b {
|
||||
background-color: #607D8B;
|
||||
}
|
33
src/app/dmca-page.js
Normal file
33
src/app/dmca-page.js
Normal file
@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import Page from './page';
|
||||
|
||||
import AppBar from 'material-ui/AppBar';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import NavigationClose from 'material-ui/svg-icons/navigation/close';
|
||||
|
||||
export default class DMCAPage extends Page {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='w100p column'>
|
||||
<AppBar
|
||||
title="DMCA / Copyright"
|
||||
iconElementLeft={<IconButton onClick={()=>{ window.router('/') }}><NavigationClose /></IconButton>}
|
||||
/>
|
||||
|
||||
<div className='column w100p pad1 center'>
|
||||
<div>RatsOnTheBoat.org is in compliance with 17 U.S.C. § 512, the Digital Millennium Copyright Act ("DMCA") and the Directive 2001/29/EC of the European Parliament.</div>
|
||||
|
||||
<div className='fs1-5 pad0-75'>Content status</div>
|
||||
|
||||
Our main goal is collect and make analysis of information from the torrent network. We don't save/distribute any real content/data and also don't save any torrents files - we are respect DMCA law.
|
||||
Information collected automaticly and the real source are torrent clients based on torrent protocol.
|
||||
|
||||
<div className='fs1-5 pad0-75'>Block mechanisms (for rightholders)</div>
|
||||
|
||||
Right holders can block/remove content that they responsible for. Contact administration or left application about content removal.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
28
src/app/footer.js
Normal file
28
src/app/footer.js
Normal file
@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
|
||||
export default (props) => {
|
||||
return (
|
||||
<div className='column center' style={{color: 'grey', marginTop: '12px'}}>
|
||||
<div className='clickable pad0-75 fs0-75' onClick={() => {
|
||||
window.router('/DMCA');
|
||||
}}>
|
||||
DMCA / Copyright
|
||||
</div>
|
||||
<div className='fs0-75 pad0-75 break-word donation-line' style={{color: 'grey'}}>Donation to support project (bitcoin): 1Ega5zeCSMPgyDn6fEMMiuGoqNNpS53ipK</div>
|
||||
<svg style={{height: '100px', fill: 'grey'}} viewBox="0 0 264.725 264.725">
|
||||
<path d="M220.195,71.427c-0.589-7.654-9.135-15.619-17.979-16.209c-8.844-0.584-17.398,0.301-12.087,6.483
|
||||
c5.308,6.188,7.074,12.091,4.423,11.212c-2.66-0.896-13.267-7.08-45.104-2.066c-4.126,1.17-21.221-12.682-44.513-12.977
|
||||
c-23.283-0.295-40.381,6.346-64.85,72.296c-2.356,5.828-18.866,19.386-27.71,25.865C3.536,162.529,0.007,169.787,0,182.763
|
||||
c-0.018,18.158,25.934,27.187,81.648,26.889c55.715-0.292,85.195-9.388,85.195-9.388c-62.789,6.773-158.907,10.52-158.907-18.687
|
||||
c0-20.641,28.321-28.47,36.281-28.184c7.958,0.3,13.562,12.673,33.307,5.603c3.247-0.295,1.48,4.423-1.18,7.369
|
||||
c-2.651,2.942-0.586,6.487,9.73,6.487c10.315,0,41.183,0.295,47.707,0c6.531-0.299,11.839-11.792-9.384-12.68
|
||||
c-18.548,0.311,12.023-5.773,15.915-21.813c0.709-3.927,8.84-4.139,15.918-4.119c20.777,0.029,34.485,38.193,38.912,38.338
|
||||
c4.416,0.15,17.979,1.621,17.683-4.273c-0.292-5.897-11.491-3.241-13.854-6.487c-2.359-3.234-10.023-15.504-7.366-21.104
|
||||
c2.65-5.59,12.674-21.229,24.463-22.988c11.789-1.777,42.451,7.361,47.459,0c5.012-7.372-6.783-11.512-15.918-28.611
|
||||
C243.779,80.572,238.768,71.728,220.195,71.427z"/>
|
||||
</svg>
|
||||
|
||||
<div className='fs0-75 pad0-75'>Don't hesitate and visit the banners, we are trying to survive among dark blue sea</div>
|
||||
</div>
|
||||
)
|
||||
}
|
8
src/app/format-bytes.js
Normal file
8
src/app/format-bytes.js
Normal file
@ -0,0 +1,8 @@
|
||||
export default function formatBytes(bytes,decimals) {
|
||||
if(bytes == 0) return '0 Byte';
|
||||
var k = 1000; // or 1024 for binary
|
||||
var dm = decimals + 1 || 3;
|
||||
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
var i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||
}
|
BIN
src/app/images/no-image-icon.png
Normal file
BIN
src/app/images/no-image-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
BIN
src/app/images/pirate-mod.jpg
Normal file
BIN
src/app/images/pirate-mod.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 KiB |
47
src/app/index-page.js
Normal file
47
src/app/index-page.js
Normal file
@ -0,0 +1,47 @@
|
||||
import React from 'react';
|
||||
import Page from './page';
|
||||
import Footer from './footer';
|
||||
|
||||
import RecentTorrents from './recent-torrents'
|
||||
import Search from './search'
|
||||
|
||||
import {Card, CardActions, CardHeader, CardMedia, CardTitle, CardText} from 'material-ui/Card';
|
||||
import Background from './images/pirate-mod.jpg'
|
||||
|
||||
const Header = (props) => {
|
||||
return (
|
||||
<Card>
|
||||
<CardMedia
|
||||
overlay={<CardTitle title="Yarrr, Landlubbers!" subtitle="Welcome to torrent project" />}
|
||||
>
|
||||
<img src={Background} />
|
||||
</CardMedia>
|
||||
<CardText>
|
||||
Welcome to BT Search! This is file search engine based on the torrents from the internet.
|
||||
Here you can easily find torrent or file that you intrested for. We are not responsible for any content of the site:
|
||||
this is only information about content that collected automatically! Content right holders and users can mark/block bad content.
|
||||
</CardText>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export {Header}
|
||||
|
||||
export default class IndexPage extends Page {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.setTitle('Rats On The Boat - Content Search Engine');
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div id='index-window'>
|
||||
<Header />
|
||||
<Search />
|
||||
<div className='column center w100p pad0-75'>
|
||||
<RecentTorrents />
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
5
src/app/index.css
Normal file
5
src/app/index.css
Normal file
@ -0,0 +1,5 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
17
src/app/index.js
Normal file
17
src/app/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './app';
|
||||
|
||||
import './css/izi/izi.css';
|
||||
import './css/izi/material-palette.css';
|
||||
import './css/izi/flex.css';
|
||||
import './css/izi/inputs.css';
|
||||
import './css/izi/components.css';
|
||||
import './css/izi/animations.css';
|
||||
|
||||
import './index.css';
|
||||
|
||||
ReactDOM.render(
|
||||
<App />,
|
||||
document.getElementById('mount-point')
|
||||
);
|
72
src/app/input-files-filter.js
Normal file
72
src/app/input-files-filter.js
Normal file
@ -0,0 +1,72 @@
|
||||
import React, { Component } from 'react';
|
||||
import InputRange from 'react-input-range';
|
||||
import './input-range.css';
|
||||
import formatBytes from './format-bytes'
|
||||
|
||||
import SelectField from 'material-ui/SelectField';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import Checkbox from 'material-ui/Checkbox';
|
||||
|
||||
export default class InputFilesFilter extends Component {
|
||||
constructor(props)
|
||||
{
|
||||
super(props)
|
||||
this.state = {
|
||||
files: this.props.value || { min: 0, max: 50 },
|
||||
enabled: false || this.props.enabled,
|
||||
filesMax: this.props.filesMax || 100 // 1mb
|
||||
}
|
||||
}
|
||||
setState(val)
|
||||
{
|
||||
if(val.filesMax && this.state.files.max > val.filesMax)
|
||||
val.files = {min: this.state.files.min, max: val.filesMax};
|
||||
|
||||
if(val.filesMax && this.state.files.min > val.filesMax)
|
||||
val.files = {min: 0, max: val.files ? val.files.max || this.state.files.max : this.state.files.max };
|
||||
|
||||
super.setState(val, () => {
|
||||
if(this.props.onChange)
|
||||
this.props.onChange({
|
||||
enabled: this.state.enabled,
|
||||
filesMax: this.state.filesMax,
|
||||
files: !this.state.enabled ? {min: 0, max: 0} : this.state.files
|
||||
})
|
||||
})
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className='filter-row row inline w100p'>
|
||||
<Checkbox
|
||||
label="Files filter"
|
||||
checked={this.state.enabled}
|
||||
style={{width: 150, display: 'flex', minWidth: 130}}
|
||||
onCheck={() => this.setState({enabled: !this.state.enabled})}
|
||||
/>
|
||||
<div className='filter-control-row row inline w100p' style={{opacity: this.state.enabled ? 1 : 0.4, transition: '0.5s', paddingLeft: 9}}>
|
||||
<InputRange
|
||||
maxValue={this.state.filesMax}
|
||||
minValue={0}
|
||||
value={this.state.files}
|
||||
style={this.props.style}
|
||||
className={this.props.className}
|
||||
onChange={files => this.setState({ files })}
|
||||
/>
|
||||
<SelectField
|
||||
floatingLabelText="Size type"
|
||||
value={this.state.filesMax}
|
||||
onChange={(event, index, value) => this.setState({filesMax: value})}
|
||||
className='filter-control-border'
|
||||
>
|
||||
<MenuItem value={10} primaryText="10 Files or less" />
|
||||
<MenuItem value={100} primaryText="100 Files or less" />
|
||||
<MenuItem value={1000} primaryText="1000 Files or less" />
|
||||
<MenuItem value={10000} primaryText="10000 Files or less" />
|
||||
<MenuItem value={100000} primaryText="100000 Files or less" />
|
||||
<MenuItem value={1000000} primaryText="1000000 Files or less" />
|
||||
</SelectField>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
83
src/app/input-range.css
Normal file
83
src/app/input-range.css
Normal file
@ -0,0 +1,83 @@
|
||||
.input-range__slider {
|
||||
appearance: none;
|
||||
background: #3f51b5;
|
||||
border: 1px solid #3f51b5;
|
||||
border-radius: 100%;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
height: 1rem;
|
||||
margin-left: -0.5rem;
|
||||
margin-top: -0.65rem;
|
||||
outline: none;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transition: transform 0.3s ease-out, box-shadow 0.3s ease-out;
|
||||
width: 1rem; }
|
||||
.input-range__slider:active {
|
||||
transform: scale(1.3); }
|
||||
.input-range__slider:focus {
|
||||
box-shadow: 0 0 0 5px rgba(63, 81, 181, 0.2); }
|
||||
.input-range--disabled .input-range__slider {
|
||||
background: #cccccc;
|
||||
border: 1px solid #cccccc;
|
||||
box-shadow: none;
|
||||
transform: none; }
|
||||
|
||||
.input-range__slider-container {
|
||||
transition: left 0.3s ease-out; }
|
||||
|
||||
.input-range__label {
|
||||
color: #aaaaaa;
|
||||
font-family: "Helvetica Neue", san-serif;
|
||||
font-size: 0.8rem;
|
||||
transform: translateZ(0);
|
||||
white-space: nowrap; }
|
||||
|
||||
.input-range__label--min,
|
||||
.input-range__label--max {
|
||||
bottom: -1.4rem;
|
||||
position: absolute; }
|
||||
|
||||
.input-range__label--min {
|
||||
left: 0; }
|
||||
|
||||
.input-range__label--max {
|
||||
right: 0; }
|
||||
|
||||
.input-range__label--value {
|
||||
position: absolute;
|
||||
top: -1.8rem; }
|
||||
|
||||
.input-range__label-container {
|
||||
left: -50%;
|
||||
position: relative; }
|
||||
.input-range__label--max .input-range__label-container {
|
||||
left: 50%; }
|
||||
|
||||
.input-range__track {
|
||||
background: #eeeeee;
|
||||
border-radius: 0.3rem;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
height: 0.3rem;
|
||||
position: relative;
|
||||
transition: left 0.3s ease-out, width 0.3s ease-out; }
|
||||
.input-range--disabled .input-range__track {
|
||||
background: #eeeeee; }
|
||||
|
||||
.input-range__track--background {
|
||||
left: 0;
|
||||
margin-top: -0.15rem;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%; }
|
||||
|
||||
.input-range__track--active {
|
||||
background: #3f51b5; }
|
||||
|
||||
.input-range {
|
||||
height: 1rem;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
|
||||
/*# sourceMappingURL=input-range.css.map */
|
73
src/app/input-size.js
Normal file
73
src/app/input-size.js
Normal file
@ -0,0 +1,73 @@
|
||||
import React, { Component } from 'react';
|
||||
import InputRange from 'react-input-range';
|
||||
import './input-range.css';
|
||||
import formatBytes from './format-bytes'
|
||||
|
||||
import SelectField from 'material-ui/SelectField';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import Checkbox from 'material-ui/Checkbox';
|
||||
|
||||
export default class InputSize extends Component {
|
||||
constructor(props)
|
||||
{
|
||||
super(props)
|
||||
this.state = {
|
||||
size: this.props.value || { min: 0, max: 500 * 1024 },
|
||||
enabled: false || this.props.enabled,
|
||||
maxSize: this.props.maxSize || 1024 * 1024 // 1mb
|
||||
}
|
||||
}
|
||||
setState(val)
|
||||
{
|
||||
if(val.maxSize && this.state.size.max > val.maxSize)
|
||||
val.size = {min: this.state.size.min, max: val.maxSize};
|
||||
|
||||
if(val.maxSize && this.state.size.min > val.maxSize)
|
||||
val.size = {min: 0, max: val.size ? val.size.max || this.state.size.max : this.state.size.max };
|
||||
|
||||
super.setState(val, () => {
|
||||
if(this.props.onChange)
|
||||
this.props.onChange({
|
||||
enabled: this.state.enabled,
|
||||
maxSize: this.state.maxSize,
|
||||
size: !this.state.enabled ? {min: 0, max: 0} : this.state.size
|
||||
})
|
||||
})
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className='filter-row row inline w100p'>
|
||||
<Checkbox
|
||||
label="Size filter"
|
||||
checked={this.state.enabled}
|
||||
style={{width: 150, display: 'flex', minWidth: 130}}
|
||||
onCheck={() => this.setState({enabled: !this.state.enabled})}
|
||||
/>
|
||||
<div className='filter-control-row row inline w100p' style={{opacity: this.state.enabled ? 1 : 0.4, transition: '0.5s', paddingLeft: 9}}>
|
||||
<InputRange
|
||||
maxValue={this.state.maxSize}
|
||||
minValue={0}
|
||||
value={this.state.size}
|
||||
formatLabel={size => formatBytes(size)}
|
||||
style={this.props.style}
|
||||
className={this.props.className}
|
||||
onChange={size => this.setState({ size })}
|
||||
/>
|
||||
<SelectField
|
||||
floatingLabelText="Size type"
|
||||
value={this.state.maxSize}
|
||||
onChange={(event, index, value) => this.setState({maxSize: value})}
|
||||
className='filter-control-border'
|
||||
>
|
||||
<MenuItem value={1024} primaryText="KB" />
|
||||
<MenuItem value={1024 * 1024} primaryText="MB" />
|
||||
<MenuItem value={1024 * 1024 * 1024} primaryText="GB" />
|
||||
<MenuItem value={10 * 1024 * 1024 * 1024} primaryText="10 GB" />
|
||||
<MenuItem value={100 * 1024 * 1024 * 1024} primaryText="100 GB" />
|
||||
<MenuItem value={1024 * 1024 * 1024 * 1024} primaryText="TB" />
|
||||
</SelectField>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
40
src/app/page.js
Normal file
40
src/app/page.js
Normal file
@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import Component from './component'
|
||||
|
||||
export default class Page extends Component {
|
||||
setTitle(title) {
|
||||
if(title) {
|
||||
document.title = title;
|
||||
}
|
||||
}
|
||||
setDescription(description) {
|
||||
this.setMetaTag('description', description);
|
||||
}
|
||||
findMetaTag(name) {
|
||||
const head = document.getElementsByTagName('head')[0];
|
||||
const headChilds = head.children;
|
||||
let meta;
|
||||
for(let i = 0; i < headChilds.length; i++) {
|
||||
if(headChilds[i].nodeName.toLowerCase() == 'meta' && headChilds[i].name.toLowerCase() == name) {
|
||||
meta = headChilds[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {head, meta};
|
||||
}
|
||||
setMetaTag(name, content) {
|
||||
let {head, meta} = this.findMetaTag(name);
|
||||
if(!meta) {
|
||||
meta = document.createElement('meta');
|
||||
head.appendChild(meta);
|
||||
}
|
||||
meta.name = name;
|
||||
meta.content = content;
|
||||
}
|
||||
removeMetaTag(name) {
|
||||
let {head, meta} = this.findMetaTag(name);
|
||||
if(meta) {
|
||||
head.removeChild(meta);
|
||||
}
|
||||
}
|
||||
}
|
89
src/app/pages-pie.js
Normal file
89
src/app/pages-pie.js
Normal file
@ -0,0 +1,89 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
export default class PagesPie extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
// синглтон
|
||||
if ( PagesPie.instance ) {
|
||||
return PagesPie.instance;
|
||||
}
|
||||
PagesPie.instance = this;
|
||||
this.pie = [];
|
||||
}
|
||||
open(pages, params) {
|
||||
if (params && params.replace) {
|
||||
if (params.replace === 'all') {
|
||||
this.pie = [];
|
||||
} else
|
||||
if (params.replace === 'last') {
|
||||
this.pie.pop();
|
||||
}
|
||||
this.forceUpdate();
|
||||
delete params.replace;
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (Array.isArray(pages)) {
|
||||
for (let i in pages) {
|
||||
this.pie.push({
|
||||
Page: pages[i],
|
||||
params: params
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.pie.push({
|
||||
Page: pages,
|
||||
params: params
|
||||
});
|
||||
}
|
||||
this.forceUpdate();
|
||||
}, 0);
|
||||
}
|
||||
close(count) {
|
||||
if (count && typeof count === 'number') {
|
||||
for (let i = 0; i < count; i++) {
|
||||
this.pie.pop();
|
||||
}
|
||||
} else {
|
||||
this.pie.pop();
|
||||
}
|
||||
this.forceUpdate();
|
||||
}
|
||||
findOpened(windowType) {
|
||||
for (let i in this.refs) {
|
||||
if(this.refs[i] instanceof windowType)
|
||||
return this.refs[i];
|
||||
}
|
||||
}
|
||||
// ОТРИСОВКА
|
||||
render() {
|
||||
if (this.pie.length > 0) {
|
||||
return (
|
||||
<div
|
||||
className={'pie full-size ' + (this.props.className || '')}
|
||||
>
|
||||
{
|
||||
this.pie.map(({Page, params}, index) => {
|
||||
let focus = false;
|
||||
if (index === this.pie.length-1) {
|
||||
focus = true;
|
||||
}
|
||||
return (
|
||||
<Page
|
||||
focused={focus}
|
||||
closeHandler={() => { index> 0 ? this.close() : null}}
|
||||
index={index}
|
||||
key={index}
|
||||
ref={index}
|
||||
{...params}
|
||||
>
|
||||
</Page>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
17
src/app/rating.js
Normal file
17
src/app/rating.js
Normal file
@ -0,0 +1,17 @@
|
||||
function biasValuation(count, min, avrg, avrg_const)
|
||||
{
|
||||
return ((count / (count + min)) * avrg) + ((min / (min + count)) * avrg_const);
|
||||
}
|
||||
|
||||
function rating(good, bad){
|
||||
if (good + bad > 0)
|
||||
{
|
||||
return biasValuation(good + bad, 9, good / (good + bad), 0.45);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = rating;
|
159
src/app/recent-torrents.js
Normal file
159
src/app/recent-torrents.js
Normal file
@ -0,0 +1,159 @@
|
||||
import React, { Component } from 'react';
|
||||
import TorrentLine from './torrent'
|
||||
import {List} from 'material-ui/List';
|
||||
import Divider from 'material-ui/Divider';
|
||||
import Subheader from 'material-ui/Subheader';
|
||||
import Paper from 'material-ui/Paper';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import RefreshIndicator from 'material-ui/RefreshIndicator';
|
||||
|
||||
export default class RecentTorrents extends Component {
|
||||
constructor() {
|
||||
super()
|
||||
this.torrents = [];
|
||||
this.torrentsAssoc = {};
|
||||
this.displayQueue = [];
|
||||
this.displayQueueAssoc = {};
|
||||
this.maxQueueSize = 1000;
|
||||
this.maxDisplaySize = 10;
|
||||
this.state = {
|
||||
pause: false,
|
||||
searchingIndicator: false
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
window.torrentSocket.emit('recentTorrents', window.customLoader((data) => {
|
||||
if(data) {
|
||||
this.torrents = data;
|
||||
//this.forceUpdate(); // вызывается через searchingIndicator
|
||||
}
|
||||
|
||||
this.displayNewTorrent = () => {
|
||||
if(!this.displayNewTorrent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.displayQueue.length == 0) {
|
||||
setTimeout(this.displayNewTorrent, 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
const speed = 850;
|
||||
|
||||
if(this.state.pause) {
|
||||
setTimeout(this.displayNewTorrent, speed);
|
||||
return;
|
||||
}
|
||||
|
||||
let torrent = this.displayQueue.shift();
|
||||
this.torrents.unshift(torrent);
|
||||
this.torrentsAssoc[torrent.hash] = torrent;
|
||||
if(this.torrents.length > this.maxDisplaySize) {
|
||||
let toDelete = this.torrents.pop()
|
||||
delete this.torrentsAssoc[toDelete.hash];
|
||||
delete this.displayQueueAssoc[toDelete.hash];
|
||||
}
|
||||
this.displayTorrentCounterValue = this.displayQueue.length;
|
||||
|
||||
this.forceUpdate();
|
||||
setTimeout(this.displayNewTorrent, speed);
|
||||
}
|
||||
this.displayNewTorrent();
|
||||
|
||||
this.displayTorrentCounterValue = 0;
|
||||
this.displayTorrentCounter = setInterval(() => {
|
||||
if(this.displayTorrentCounterValue != this.displayQueue.length) {
|
||||
this.displayTorrentCounterValue = this.displayQueue.length;
|
||||
this.forceUpdate();
|
||||
}
|
||||
}, 40);
|
||||
}, () => {
|
||||
this.setState({
|
||||
searchingIndicator: true
|
||||
});
|
||||
}, () => {
|
||||
this.setState({
|
||||
searchingIndicator: false
|
||||
});
|
||||
}));
|
||||
this.newTorrentFunc = (torrent) => {
|
||||
if(this.displayQueue.length < this.maxQueueSize) {
|
||||
this.displayQueue.push(torrent);
|
||||
this.displayQueueAssoc[torrent.hash] = torrent;
|
||||
}
|
||||
};
|
||||
window.torrentSocket.on('newTorrent', this.newTorrentFunc);
|
||||
|
||||
this.tracketUpdate = (statistic) => {
|
||||
if(statistic.hash in this.displayQueueAssoc)
|
||||
{
|
||||
Object.assign(this.displayQueueAssoc[statistic.hash], statistic);
|
||||
if(statistic.hash in this.torrentsAssoc) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
window.torrentSocket.on('trackerTorrentUpdate', this.tracketUpdate);
|
||||
}
|
||||
pauseAndContinue() {
|
||||
this.setState({
|
||||
pause: !this.state.pause
|
||||
});
|
||||
}
|
||||
componentWillUnmount() {
|
||||
if(this.newTorrentFunc)
|
||||
window.torrentSocket.off('newTorrent', this.newTorrentFunc);
|
||||
if(this.tracketUpdate)
|
||||
window.torrentSocket.off('trackerTorrentUpdate', this.tracketUpdate);
|
||||
if(this.displayNewTorrent)
|
||||
delete this.displayNewTorrent;
|
||||
if(this.displayTorrentCounter)
|
||||
clearInterval(this.displayTorrentCounter);
|
||||
}
|
||||
render() {
|
||||
const style = {
|
||||
refresh: {
|
||||
display: 'inline-block',
|
||||
position: 'relative',
|
||||
},
|
||||
};
|
||||
|
||||
if(this.state.searchingIndicator) {
|
||||
return (
|
||||
<div className='pad1'>
|
||||
<RefreshIndicator
|
||||
size={50}
|
||||
left={0}
|
||||
top={0}
|
||||
loadingColor="#FF9800"
|
||||
status="loading"
|
||||
style={style.refresh}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if(!this.torrents || this.torrents.length == 0)
|
||||
return null;
|
||||
|
||||
return (
|
||||
<List className='animated recent-torrents'>
|
||||
<Subheader className='recent-title' inset={true}>
|
||||
<FlatButton style={{marginRight: '8px'}} primary={true} label='top' labelStyle={{color: "#a4c639"}} onClick={() =>{
|
||||
window.router('/top');
|
||||
}} />
|
||||
<FlatButton style={{marginRight: '8px'}} label={!this.state.pause ? 'running' : 'stoped'} secondary={this.state.pause} primary={!this.state.pause} onClick={() =>{
|
||||
this.pauseAndContinue()
|
||||
}} />
|
||||
Most recent torrents{this.displayQueue.length > 0 ? ` (and ${this.displayQueue.length} more)` : null}
|
||||
</Subheader>
|
||||
<Divider />
|
||||
{
|
||||
this.torrents.map((torrent, index) =>{
|
||||
return <TorrentLine key={index} torrent={torrent} />;
|
||||
})
|
||||
}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
}
|
113
src/app/registerServiceWorker.js
Normal file
113
src/app/registerServiceWorker.js
Normal file
@ -0,0 +1,113 @@
|
||||
// In production, we register a service worker to serve assets from local cache.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on the "N+1" visit to a page, since previously
|
||||
// cached resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
|
||||
// This link also includes instructions on opting out of this behavior.
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
export default function register() {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (!isLocalhost) {
|
||||
// Is not local host. Just register service worker
|
||||
registerValidSW(swUrl);
|
||||
} else {
|
||||
// This is running on localhost. Lets check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the old content will have been purged and
|
||||
// the fresh content will have been added to the cache.
|
||||
// It's the perfect time to display a "New content is
|
||||
// available; please refresh." message in your web app.
|
||||
console.log('New content is available; please refresh.');
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
if (
|
||||
response.status === 404 ||
|
||||
response.headers.get('content-type').indexOf('javascript') === -1
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
return navigator.serviceWorker.ready.then(registration => {
|
||||
console.log("Unregister service worker");
|
||||
return registration.unregister();
|
||||
});
|
||||
} else {
|
||||
return new Promise(function(resolve, reject) {
|
||||
resolve();
|
||||
})
|
||||
}
|
||||
}
|
84
src/app/router.js
Normal file
84
src/app/router.js
Normal file
@ -0,0 +1,84 @@
|
||||
//import router from 'page';
|
||||
import PagesPie from './pages-pie.js';
|
||||
|
||||
import IndexPage from './index-page.js'
|
||||
import TorrentPage from './torrent-page.js'
|
||||
import DMCAPage from './dmca-page.js'
|
||||
import AdminPage from './admin-page.js'
|
||||
import TopPage from './top-page.js'
|
||||
|
||||
let routers = {}
|
||||
const router = (page, callback) => {
|
||||
if(!callback)
|
||||
{
|
||||
if(!page)
|
||||
routers['/'].callback()
|
||||
else
|
||||
{
|
||||
const p = page.split('/')
|
||||
const pg = routers[`${p[0]}/${p[1]}`]
|
||||
if(!pg)
|
||||
return
|
||||
|
||||
p.splice(0, 2)
|
||||
const params = {}
|
||||
for(let i = 0; i < p.length; i++)
|
||||
{
|
||||
params[pg.args[i]] = p[i]
|
||||
}
|
||||
console.log(params)
|
||||
|
||||
pg.callback({
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const p = page.split('/')
|
||||
routers[`${p[0]}/${p[1]}`] = {callback}
|
||||
routers[`${p[0]}/${p[1]}`].args = []
|
||||
for(let i = 2; i < p.length; i++)
|
||||
{
|
||||
if(p[i].startsWith(':'))
|
||||
routers[`${p[0]}/${p[1]}`].args.push(p[i].substring(1))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
window.router = router;
|
||||
|
||||
router('/', () => {
|
||||
//singleton
|
||||
let pie = new PagesPie;
|
||||
pie.open(IndexPage, {replace: 'all'});
|
||||
});
|
||||
|
||||
router('/torrent/:hash', (e) => {
|
||||
//singleton
|
||||
let pie = new PagesPie;
|
||||
pie.open(TorrentPage, {
|
||||
replace: 'all',
|
||||
hash: e.params.hash,
|
||||
});
|
||||
});
|
||||
|
||||
router('/DMCA', () => {
|
||||
//singleton
|
||||
let pie = new PagesPie;
|
||||
pie.open(DMCAPage, {replace: 'all'});
|
||||
});
|
||||
|
||||
|
||||
router('/admi5p', () => {
|
||||
//singleton
|
||||
let pie = new PagesPie;
|
||||
pie.open(AdminPage, {replace: 'all'});
|
||||
});
|
||||
|
||||
router('/top', () => {
|
||||
//singleton
|
||||
let pie = new PagesPie;
|
||||
pie.open(TopPage, {replace: 'all'});
|
||||
});
|
56
src/app/search-advanced-controls.js
Normal file
56
src/app/search-advanced-controls.js
Normal file
@ -0,0 +1,56 @@
|
||||
import React, { Component } from 'react';
|
||||
import SelectField from 'material-ui/SelectField';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import InputSize from './input-size';
|
||||
import FilesFilterInput from './input-files-filter';
|
||||
|
||||
export default class AdvancedSearchControl extends Component {
|
||||
constructor(props)
|
||||
{
|
||||
super(props)
|
||||
this.state = {
|
||||
type: undefined,
|
||||
size: {min: 0, max: 0},
|
||||
maxSize: 1024 * 1024 * 1024,
|
||||
sizeEnabled: false,
|
||||
filesEnabled: false,
|
||||
files: {min: 0, max: 0},
|
||||
filesMax: 100,
|
||||
}
|
||||
if(this.props.state)
|
||||
this.state = Object.assign(this.state, this.props.state)
|
||||
}
|
||||
setState(val)
|
||||
{
|
||||
super.setState(val, (v) => {
|
||||
if(this.props.onChange)
|
||||
this.props.onChange(this.state)
|
||||
})
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className='column w100p' style={{maxWidth: 750, overflow: 'hidden', padding: '0px 18px 15px'}}>
|
||||
<SelectField
|
||||
floatingLabelText="Filter content type"
|
||||
value={this.state.type}
|
||||
onChange={(event, index, value) => this.setState({type: value})}
|
||||
>
|
||||
<MenuItem value={undefined} primaryText="" />
|
||||
<MenuItem value='video' primaryText="Video" />
|
||||
<MenuItem value='audio' primaryText="Audio" />
|
||||
<MenuItem value='pictures' primaryText="Pictures" />
|
||||
<MenuItem value='books' primaryText="Books" />
|
||||
<MenuItem value='application' primaryText="Applications" />
|
||||
<MenuItem value='archive' primaryText="Archives" />
|
||||
<MenuItem value='disc' primaryText="Disk Images" />
|
||||
</SelectField>
|
||||
<div className='w100p'>
|
||||
<InputSize value={this.state.size} enabled={this.state.sizeEnabled} maxSize={this.state.maxSize} onChange={({size, maxSize, enabled}) => this.setState({size, maxSize, sizeEnabled: enabled})} />
|
||||
</div>
|
||||
<div className='w100p'>
|
||||
<FilesFilterInput value={this.state.files} filesMax={this.state.filesMax} enabled={this.state.filesEnabled} onChange={({files, filesMax, enabled}) => this.setState({files, filesMax, filesEnabled: enabled})} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
136
src/app/search-results.js
Normal file
136
src/app/search-results.js
Normal file
@ -0,0 +1,136 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import TorrentLine from './torrent'
|
||||
import {List, ListItem} from 'material-ui/List';
|
||||
import Subheader from 'material-ui/Subheader';
|
||||
import Divider from 'material-ui/Divider';
|
||||
import LinearProgress from 'material-ui/LinearProgress';
|
||||
|
||||
export default class SearchResults extends Component {
|
||||
render() {
|
||||
return (
|
||||
<List style={{minWidth: '20em'}}>
|
||||
{
|
||||
(this.props.torrentsSearchResults && this.props.torrentsSearchResults.length > 0)
|
||||
|| (this.props.filesSearchResults && this.props.filesSearchResults.length > 0)
|
||||
?
|
||||
<div>
|
||||
<Subheader inset={true}>Search results</Subheader>
|
||||
<div className='w100p row center' style={{marginTop: '-16px'}}>{this.props.resultSelector}</div>
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
this.props.torrentsSearchResults && this.props.torrentsSearchResults.length > 0
|
||||
?
|
||||
this.props.torrentsSearchResults.map((torrent, index) =>{
|
||||
return(
|
||||
<TorrentLine torrent={torrent} key={index} />
|
||||
);
|
||||
})
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
this.props.moreTorrentsEnabled && !this.props.moreTorrentsIndicator
|
||||
?
|
||||
<div>
|
||||
<ListItem innerDivStyle={{textAlign: 'center', padding: '1em'}} primaryText={<span>More Torrents</span>} onClick={() => {
|
||||
if(this.props.onMoreTorrents)
|
||||
this.props.onMoreTorrents();
|
||||
}} />
|
||||
<Divider />
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
this.props.moreTorrentsIndicator
|
||||
?
|
||||
<div style={{padding: '0.8em'}}>
|
||||
<LinearProgress mode="indeterminate" />
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
this.props.filesSearchResults && this.props.filesSearchResults.length > 0
|
||||
?
|
||||
this.props.filesSearchResults.map((torrent, index) =>{
|
||||
return(
|
||||
<TorrentLine torrent={torrent} key={index} />
|
||||
);
|
||||
})
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
this.props.moreFilesEnabled && !this.props.moreFilesIndicator
|
||||
?
|
||||
<div>
|
||||
<ListItem innerDivStyle={{textAlign: 'center', padding: '1em'}} primaryText='More Files' onClick={() => {
|
||||
if(this.props.onMoreFiles)
|
||||
this.props.onMoreFiles();
|
||||
}} />
|
||||
<Divider />
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
this.props.moreFilesIndicator
|
||||
?
|
||||
<div style={{padding: '0.8em'}}>
|
||||
<LinearProgress mode="indeterminate" />
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
this.props.torrentsSearchResults && this.props.torrentsSearchResults.length == 0
|
||||
&& this.props.filesSearchResults && this.props.filesSearchResults.length == 0
|
||||
&& !this.props.currentSearching
|
||||
?
|
||||
<div className='row inline'>
|
||||
<svg style={{fill: 'grey', height: '30px'}} viewBox="0 0 264.695 264.695">
|
||||
<g>
|
||||
<path d="M219.171,216.785c-4.762,0-10.322,2.3-16.672,6.881l-35.211-12.968l35.734-12.978
|
||||
c6.003,3.888,11.558,5.833,16.682,5.833c5.639,0,9.347-2.917,11.117-8.733c0.351-1.235,0.527-2.57,0.527-3.981
|
||||
c0-7.397-4.766-11.378-14.295-11.9c3.876-3.882,5.828-7.687,5.828-11.392c0-3.871-2.039-7.149-6.092-9.797
|
||||
c-2.118-1.049-4.325-1.584-6.615-1.584c-7.769,0-13.064,6.258-15.887,18.797l-61.941,23.039l-61.94-22.504
|
||||
c-2.823-12.885-8.125-19.332-15.885-19.332c-2.293,0-4.501,0.535-6.62,1.584c-3.876,2.647-5.82,5.926-5.82,9.797
|
||||
c0,3.705,1.944,7.51,5.82,11.392c-9.701,0.522-14.555,4.503-14.555,11.901c0,1.41,0.179,2.746,0.526,3.98
|
||||
c1.946,5.816,5.651,8.733,11.122,8.733c5.113,0,10.671-1.945,16.677-5.832l35.998,12.977l-35.476,12.698
|
||||
c-6.175-4.406-11.637-6.611-16.402-6.611c-5.654,0-9.623,2.918-11.919,8.733c-0.348,1.235-0.526,2.553-0.526,3.975
|
||||
c0,7.405,4.853,11.385,14.555,11.907c-3.876,3.883-5.82,7.688-5.82,11.393c0,3.869,1.944,7.134,5.82,9.797
|
||||
c2.477,1.412,4.854,2.105,7.153,2.105c7.227,0,12.443-6.176,15.619-18.525l61.673-22.504l61.678,22.504
|
||||
c3.178,12.35,8.475,18.525,15.882,18.525c2.121,0,4.407-0.693,6.884-2.105c4.052-2.663,6.092-5.928,6.092-9.797
|
||||
c0-3.705-1.953-7.51-5.828-11.393c9.528-0.522,14.295-4.502,14.295-11.907c0-1.422-0.177-2.739-0.527-3.975
|
||||
C228.702,219.702,224.82,216.785,219.171,216.785z"/>
|
||||
<path d="M48.436,128.904c9.703,11.114,23.379,19.242,41.035,24.346v3.986c0,4.936,1.672,9.086,5.025,12.433
|
||||
c3.35,3.358,7.498,5.211,12.441,5.563c5.116,0.357,8.905-0.528,11.378-2.646c3.879,2.817,8.204,4.229,12.974,4.229
|
||||
c4.41,0,8.474-1.316,12.175-3.963c2.471,1.934,6.087,2.738,10.856,2.381c4.937-0.528,9.089-2.426,12.44-5.689
|
||||
c3.35-3.281,5.025-7.371,5.025-12.307v-2.91c19.057-4.945,33.795-13.237,44.21-24.898c10.059-11.109,15.087-24.253,15.087-39.435
|
||||
c0-3.359-0.355-6.886-1.063-10.597c-3.525-22.571-13.938-41.201-31.229-55.844C180.612,7.856,158.464,0,132.347,0
|
||||
c-26.123,0-48.27,7.767-66.44,23.282C48.61,38.118,38.289,56.825,34.937,79.396c-0.709,3.711-1.064,7.238-1.064,10.597
|
||||
C33.873,104.817,38.724,117.778,48.436,128.904L48.436,128.904z M152.865,60.749c5.206-6.085,11.514-9.13,18.922-9.13
|
||||
c7.592,0,13.986,3.045,19.194,9.13c5.2,6.076,7.81,13.446,7.81,22.087c0,8.649-2.609,16.021-7.81,22.108
|
||||
c-5.208,6.097-11.603,9.13-19.194,9.13c-7.408,0-13.716-3.033-18.922-9.13c-5.211-6.087-7.814-13.459-7.814-22.108
|
||||
C145.05,74.195,147.654,66.825,152.865,60.749z M124.805,121.428c2.556-3.307,5.065-4.968,7.542-4.968
|
||||
c2.47,0,4.802,1.831,7.012,5.509c2.205,3.662,3.317,7.145,3.317,10.469c0,5.062-3.361,7.581-10.067,7.581
|
||||
c-4.414,0-7.677-1.136-9.792-3.396c-1.237-1.411-1.849-3.147-1.849-5.249C120.969,128.065,122.245,124.752,124.805,121.428z
|
||||
M71.465,60.749c5.295-6.085,11.65-9.13,19.059-9.13c7.406,0,13.762,3.045,19.06,9.13c5.296,6.076,7.948,13.446,7.948,22.087
|
||||
c0,8.649-2.651,16.021-7.948,22.108c-5.297,6.097-11.654,9.13-19.06,9.13c-7.409,0-13.764-3.033-19.059-9.13
|
||||
c-5.292-6.087-7.944-13.459-7.944-22.108C63.521,74.195,66.173,66.825,71.465,60.749z"/>
|
||||
</g>
|
||||
</svg>
|
||||
<div className='fs0-85 pad0-75' style={{color: 'grey'}}>no torrents for this search request were found</div>
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
}
|
407
src/app/search.js
Normal file
407
src/app/search.js
Normal file
@ -0,0 +1,407 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import SearchResults from './search-results'
|
||||
import AdvancedSearch from './search-advanced-controls'
|
||||
import TextField from 'material-ui/TextField';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import RefreshIndicator from 'material-ui/RefreshIndicator';
|
||||
|
||||
import Checkbox from 'material-ui/Checkbox';
|
||||
import Visibility from 'material-ui/svg-icons/action/visibility';
|
||||
import VisibilityOff from 'material-ui/svg-icons/action/visibility-off';
|
||||
import AddIcon from 'material-ui/svg-icons/content/add';
|
||||
import RemoveIcon from 'material-ui/svg-icons/content/remove';
|
||||
|
||||
import SelectField from 'material-ui/SelectField';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
|
||||
import formatBytes from './format-bytes'
|
||||
|
||||
let session;
|
||||
|
||||
class TorrentsStatistic extends Component {
|
||||
constructor(props)
|
||||
{
|
||||
super(props)
|
||||
|
||||
this.stats = props.stats || {}
|
||||
}
|
||||
componentDidMount()
|
||||
{
|
||||
this.newTorrentFunc = (torrent) => {
|
||||
this.stats.size += torrent.size;
|
||||
this.stats.torrents++;
|
||||
this.stats.files += torrent.files;
|
||||
this.forceUpdate()
|
||||
}
|
||||
|
||||
window.torrentSocket.on('newTorrent', this.newTorrentFunc);
|
||||
}
|
||||
componentWillUnmount()
|
||||
{
|
||||
if(this.newTorrentFunc)
|
||||
window.torrentSocket.off('newTorrent', this.newTorrentFunc);
|
||||
}
|
||||
render()
|
||||
{
|
||||
return (
|
||||
<div className='fs0-75 pad0-75' style={{color: 'rgba(0, 0, 0, 0.541176)'}}>you have information about {this.stats.torrents} torrents and around {this.stats.files} files and { formatBytes(this.stats.size, 1) } of data</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default class Search extends Component {
|
||||
constructor(props)
|
||||
{
|
||||
super(props)
|
||||
this.state = {
|
||||
searchingIndicator: false,
|
||||
safeSearchText: 'safe search enabled',
|
||||
safeSearchColor: 'rgb(0, 188, 212)',
|
||||
moreTorrentsIndicator: false,
|
||||
moreFilesIndicator: false,
|
||||
orderBy: null,
|
||||
orderDesc: false,
|
||||
advancedSearch: false,
|
||||
}
|
||||
this.searchLimit = 10
|
||||
this.advanced = {}
|
||||
this.searchError = undefined;
|
||||
|
||||
if(session)
|
||||
{
|
||||
this.searchTorrents = session.searchTorrents;
|
||||
this.searchFiles = session.searchFiles;
|
||||
this.moreSearchTorrents = session.moreSearchTorrents;
|
||||
this.moreSearchFiles = session.moreSearchFiles;
|
||||
this.currentSearch = session.currentSearch;
|
||||
this.searchValue = session.searchValue;
|
||||
Object.assign(this.state, this.setSafeSearch(session.notSafeSearch))
|
||||
this.state.orderBy = session.orderBy;
|
||||
this.state.orderDesc = session.orderDesc;
|
||||
this.state.advancedSearch = session.advancedSearch;
|
||||
this.advanced = session.advanced;
|
||||
this.searchError = session.searchError;
|
||||
}
|
||||
}
|
||||
|
||||
search(oldSearch) {
|
||||
this.setState({
|
||||
searchingIndicator: true
|
||||
});
|
||||
this.searchTorrents = [];
|
||||
this.moreSearchTorrents = true;
|
||||
this.searchFiles = [];
|
||||
this.moreSearchFiles = true;
|
||||
this.currentSearch = this.searchValue;
|
||||
let queries = 2;
|
||||
let searchTorrentsParams = {
|
||||
limit: this.searchLimit,
|
||||
safeSearch: !this.notSafeSearch,
|
||||
orderBy: this.state.orderBy,
|
||||
orderDesc: this.state.orderDesc,
|
||||
};
|
||||
if(this.state.advancedSearch && this.advanced)
|
||||
searchTorrentsParams = Object.assign(searchTorrentsParams, this.advanced);
|
||||
|
||||
window.torrentSocket.emit('searchTorrent', oldSearch ? this.currentSearch : this.searchValue, searchTorrentsParams, window.customLoader((torrents) => {
|
||||
if(torrents) {
|
||||
this.searchTorrents = torrents;
|
||||
if(torrents.length != this.searchLimit)
|
||||
this.moreSearchTorrents = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.moreSearchTorrents = false;
|
||||
}
|
||||
if(--queries == 0) {
|
||||
this.setState({
|
||||
searchingIndicator: false
|
||||
});
|
||||
} else {
|
||||
this.forceUpdate();
|
||||
}
|
||||
}));
|
||||
let searchFilesParams = {
|
||||
limit: this.searchLimit,
|
||||
safeSearch: !this.notSafeSearch,
|
||||
orderBy: this.state.orderBy,
|
||||
orderDesc: this.state.orderDesc,
|
||||
};
|
||||
if(this.state.advancedSearch && this.advanced)
|
||||
searchFilesParams = Object.assign(searchFilesParams, this.advanced);
|
||||
|
||||
window.torrentSocket.emit('searchFiles', oldSearch ? this.currentSearch : this.searchValue, searchFilesParams, window.customLoader((torrents) => {
|
||||
if(torrents) {
|
||||
this.searchFiles = torrents;
|
||||
let files = 0;
|
||||
torrents.forEach((torrent) => {
|
||||
if(torrent.path && torrent.path.length > 0)
|
||||
files += torrent.path.length
|
||||
});
|
||||
if(files != this.searchLimit)
|
||||
this.moreSearchFiles = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.moreSearchFiles = false;
|
||||
}
|
||||
if(--queries == 0) {
|
||||
this.setState({
|
||||
searchingIndicator: false
|
||||
});
|
||||
} else {
|
||||
this.forceUpdate();
|
||||
}
|
||||
}));
|
||||
}
|
||||
moreTorrents() {
|
||||
this.setState({moreTorrentsIndicator: true});
|
||||
|
||||
window.torrentSocket.emit('searchTorrent', this.currentSearch, {
|
||||
index: this.searchTorrents.length,
|
||||
limit: this.searchLimit,
|
||||
safeSearch: !this.notSafeSearch,
|
||||
orderBy: this.state.orderBy,
|
||||
orderDesc: this.state.orderDesc,
|
||||
}, window.customLoader((torrents) => {
|
||||
if(torrents) {
|
||||
this.searchTorrents = this.searchTorrents.concat(torrents);
|
||||
if(torrents.length != this.searchLimit)
|
||||
this.moreSearchTorrents = false;
|
||||
|
||||
this.setState({moreTorrentsIndicator: false});
|
||||
}
|
||||
}));
|
||||
}
|
||||
moreFiles() {
|
||||
let index = 0;
|
||||
this.searchFiles.forEach((torrent) => {
|
||||
if(torrent.path && torrent.path.length > 0)
|
||||
index += torrent.path.length;
|
||||
});
|
||||
|
||||
this.setState({moreFilesIndicator: true});
|
||||
|
||||
window.torrentSocket.emit('searchFiles', this.currentSearch, {
|
||||
index: index,
|
||||
limit: this.searchLimit,
|
||||
safeSearch: !this.notSafeSearch,
|
||||
orderBy: this.state.orderBy,
|
||||
orderDesc: this.state.orderDesc,
|
||||
}, window.customLoader((torrents) => {
|
||||
if(torrents) {
|
||||
this.searchFiles = this.searchFiles.concat(torrents);
|
||||
let files = 0;
|
||||
torrents.forEach((torrent) => {
|
||||
if(torrent.path && torrent.path.length > 0)
|
||||
files += torrent.path.length
|
||||
});
|
||||
if(files != this.searchLimit)
|
||||
this.moreSearchFiles = false;
|
||||
|
||||
this.setState({moreFilesIndicator: false});
|
||||
}
|
||||
}));
|
||||
}
|
||||
componentDidMount() {
|
||||
this.newStatisticFunc = (statistic) => {
|
||||
if(statistic) {
|
||||
this.stats = statistic;
|
||||
this.forceUpdate();
|
||||
}
|
||||
};
|
||||
window.torrentSocket.emit('statistic', window.customLoader(this.newStatisticFunc));
|
||||
window.torrentSocket.on('newStatistic', this.newStatisticFunc);
|
||||
}
|
||||
componentWillUnmount()
|
||||
{
|
||||
if(this.newStatisticFunc)
|
||||
window.torrentSocket.off('newStatistic', this.newStatisticFunc);
|
||||
|
||||
session = {
|
||||
searchTorrents: this.searchTorrents,
|
||||
searchFiles: this.searchFiles,
|
||||
moreSearchTorrents: this.moreSearchTorrents,
|
||||
moreSearchFiles: this.moreSearchFiles,
|
||||
currentSearch: this.currentSearch,
|
||||
searchValue: this.searchValue,
|
||||
notSafeSearch: this.notSafeSearch,
|
||||
orderBy: this.state.orderBy,
|
||||
orderDesc: this.state.orderDesc,
|
||||
advancedSearch: this.state.advancedSearch,
|
||||
advanced: this.advanced,
|
||||
searchError: this.searchError,
|
||||
}
|
||||
}
|
||||
setSafeSearch(ch) {
|
||||
this.notSafeSearch = ch;
|
||||
if(ch)
|
||||
{
|
||||
return {safeSearchText: 'safe search disabled', safeSearchColor: '#EC407A'}
|
||||
}
|
||||
else
|
||||
{
|
||||
return {safeSearchText: 'safe search enabled', safeSearchColor: 'rgb(0, 188, 212)'}
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const style = {
|
||||
refresh: {
|
||||
display: 'inline-block',
|
||||
position: 'relative',
|
||||
},
|
||||
};
|
||||
|
||||
const orderText = (text, field) => {
|
||||
if(field !== this.state.orderBy)
|
||||
return text;
|
||||
|
||||
if(this.state.orderDesc)
|
||||
return text + ' ⇩'
|
||||
else
|
||||
return text + ' ⇧'
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="column w100p center">
|
||||
<div className='row inline w100p pad0-75' style={{maxWidth: '30em'}}>
|
||||
<TextField
|
||||
hintText="Search torrent or file"
|
||||
floatingLabelText="What to search?"
|
||||
fullWidth={true}
|
||||
ref='searchInput'
|
||||
defaultValue={this.searchValue}
|
||||
errorText={this.searchError}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.search();
|
||||
}
|
||||
}}
|
||||
onChange={e => {
|
||||
this.searchValue = e.target.value
|
||||
if(this.searchValue.length < 3 && this.searchValue.length > 0)
|
||||
this.searchError = 'too short string for search';
|
||||
else
|
||||
this.searchError = undefined;
|
||||
this.forceUpdate()
|
||||
}}
|
||||
/>
|
||||
<RaisedButton style={{marginTop: '15px', marginLeft: '10px'}} label="Search" primary={true} onClick={() =>{
|
||||
this.search()
|
||||
}} />
|
||||
</div>
|
||||
<div className='row w100p center wrap' style={{padding: '0 8px'}}>
|
||||
<div style={{padding: '0px 17px'}}>
|
||||
<Checkbox
|
||||
ref='safeSearch'
|
||||
checked={this.notSafeSearch ? true : false}
|
||||
checkedIcon={<Visibility />}
|
||||
uncheckedIcon={<VisibilityOff />}
|
||||
label={<span className='text-nowrap' style={{fontSize: '0.87em', transition: '0.1s', color: this.state.safeSearchColor}}>{this.state.safeSearchText}</span>}
|
||||
iconStyle={{fill: this.state.safeSearchColor}}
|
||||
onCheck={(ev, ch) => {
|
||||
this.setState(this.setSafeSearch(ch));
|
||||
}}
|
||||
style={{paddingBottom: '0.8em'}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{padding: '0px 17px'}}>
|
||||
<Checkbox
|
||||
ref='advancedSearch'
|
||||
checked={this.state.advancedSearch}
|
||||
checkedIcon={<RemoveIcon />}
|
||||
uncheckedIcon={<AddIcon />}
|
||||
label={<span className='text-nowrap' style={{fontSize: '0.87em', transition: '0.1s', color: 'black'}}>advanced search</span>}
|
||||
iconStyle={{fill: 'black'}}
|
||||
onCheck={(ev, ch) => {
|
||||
this.setState({advancedSearch: ch});
|
||||
}}
|
||||
style={{paddingBottom: '0.8em'}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
this.state.advancedSearch
|
||||
&&
|
||||
<AdvancedSearch onChange={(state) => {
|
||||
this.advanced = state;
|
||||
}} state={this.advanced} />
|
||||
}
|
||||
{
|
||||
this.stats
|
||||
&&
|
||||
<TorrentsStatistic stats={this.stats} />
|
||||
}
|
||||
{
|
||||
this.state.searchingIndicator
|
||||
?
|
||||
<div className='pad1'>
|
||||
<RefreshIndicator
|
||||
size={50}
|
||||
left={0}
|
||||
top={0}
|
||||
loadingColor="#FF9800"
|
||||
status="loading"
|
||||
style={style.refresh}
|
||||
/>
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
<SearchResults
|
||||
torrentsSearchResults={this.searchTorrents}
|
||||
filesSearchResults={this.searchFiles}
|
||||
currentSearching={this.state.searchingIndicator}
|
||||
|
||||
resultSelector={
|
||||
<SelectField
|
||||
floatingLabelText="Sort by"
|
||||
floatingLabelFixed={true}
|
||||
value={this.state.orderBy}
|
||||
onChange={(event, index, value) => {
|
||||
event.preventDefault(); // fix overclick on torrent
|
||||
if(value === 'none') {
|
||||
this.setState({orderBy: null}, () => {
|
||||
this.search(true)
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
if(value === this.state.orderBy)
|
||||
{
|
||||
this.setState({orderDesc: !this.state.orderDesc}, () => {
|
||||
this.search(true)
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
orderBy: value,
|
||||
orderDesc: (value === 'seeders' || value === 'completed' || value === 'added') ? true : this.state.orderDesc
|
||||
}, () => {
|
||||
this.search(true)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<MenuItem value='none' primaryText={'None'} />
|
||||
<MenuItem value='seeders' primaryText={orderText('Seeders', 'seeders')} />
|
||||
<MenuItem value='name' primaryText={orderText('Name', 'name')} />
|
||||
<MenuItem value='files' primaryText={orderText('Files', 'files')} />
|
||||
<MenuItem value='size' primaryText={orderText('Size', 'size')} />
|
||||
<MenuItem value='added' primaryText={orderText('Added date', 'added')} />
|
||||
<MenuItem value='completed' primaryText={orderText('Completed', 'completed')} />
|
||||
</SelectField>
|
||||
}
|
||||
|
||||
moreTorrentsEnabled={this.moreSearchTorrents && !this.state.searchingIndicator}
|
||||
moreFilesEnabled={this.moreSearchFiles && !this.state.searchingIndicator}
|
||||
onMoreTorrents={() => this.moreTorrents()}
|
||||
onMoreFiles={() => this.moreFiles()}
|
||||
moreTorrentsIndicator={this.state.moreTorrentsIndicator}
|
||||
moreFilesIndicator={this.state.moreFilesIndicator}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
78
src/app/top-page.js
Normal file
78
src/app/top-page.js
Normal file
@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import Page from './page';
|
||||
import Footer from './footer';
|
||||
import { Header } from './index-page'
|
||||
|
||||
import TorrentLine from './torrent'
|
||||
import {List} from 'material-ui/List';
|
||||
import Subheader from 'material-ui/Subheader';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
|
||||
export default class TopPage extends Page {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.setTitle('Rats On The Boat - Torrents top');
|
||||
this.topTorrents = {};
|
||||
this.types = ['main', 'week', 'hours', 'month', 'video', 'audio', 'books', 'pictures', 'application', 'archive']
|
||||
this.descriptions = {
|
||||
main: 'All',
|
||||
video: 'Video',
|
||||
audio: 'Audio/Music',
|
||||
books: 'Books',
|
||||
pictures: 'Pictures/Images',
|
||||
application: 'Applications/Games',
|
||||
archive: 'Archives',
|
||||
week: 'Last week',
|
||||
hours: 'Last 24 hours',
|
||||
month: 'Last month'
|
||||
}
|
||||
}
|
||||
componentDidMount()
|
||||
{
|
||||
super.componentDidMount();
|
||||
for(const type of this.types)
|
||||
{
|
||||
window.torrentSocket.emit('topTorrents', type == 'main' ? null : type, window.customLoader((data) => {
|
||||
this.topTorrents[type] = data;
|
||||
this.forceUpdate()
|
||||
}))
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Header />
|
||||
<div className='column center w100p pad0-75'>
|
||||
<RaisedButton label="Back to main page" primary={true} onClick={() => {
|
||||
window.router('/')
|
||||
}} />
|
||||
{
|
||||
this.types.map((type, index) => {
|
||||
const torrents = this.topTorrents[type];
|
||||
|
||||
if(!torrents)
|
||||
return null;
|
||||
|
||||
return (
|
||||
<List key={index} style={{paddingBottom: '70px'}} className='animated recent-torrents'>
|
||||
<Subheader inset={true}>
|
||||
{
|
||||
this.descriptions[type]
|
||||
}
|
||||
</Subheader>
|
||||
{
|
||||
torrents.map((torrent, index) => {
|
||||
return <TorrentLine key={index} torrent={torrent} />
|
||||
})
|
||||
}
|
||||
</List>
|
||||
)
|
||||
|
||||
})
|
||||
}
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
415
src/app/torrent-page.js
Normal file
415
src/app/torrent-page.js
Normal file
@ -0,0 +1,415 @@
|
||||
import React from 'react';
|
||||
import Page from './page';
|
||||
import formatBytes from './format-bytes'
|
||||
import Footer from './footer';
|
||||
|
||||
import {List, ListItem} from 'material-ui/List';
|
||||
import Subheader from 'material-ui/Subheader';
|
||||
import Divider from 'material-ui/Divider';
|
||||
import {Tabs, Tab} from 'material-ui/Tabs';
|
||||
import ActionInfo from 'material-ui/svg-icons/action/info';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import FontIcon from 'material-ui/FontIcon';
|
||||
|
||||
import FileFolder from 'material-ui/svg-icons/file/folder';
|
||||
import NoImage from './images/no-image-icon.png'
|
||||
|
||||
var moment = require('moment');
|
||||
import RefreshIndicator from 'material-ui/RefreshIndicator';
|
||||
let rating = require('./rating');
|
||||
import LinearProgress from 'material-ui/LinearProgress';
|
||||
import {fileTypeDetect} from './content'
|
||||
import {contentIcon} from './torrent'
|
||||
|
||||
let buildFilesTree = (filesList) => {
|
||||
let rootTree = {
|
||||
__sizeBT: 0
|
||||
};
|
||||
filesList.forEach((file) => {
|
||||
let pathTree = file.path.split('/');
|
||||
let currentItem = rootTree;
|
||||
pathTree.forEach((pathItem) => {
|
||||
if(!(pathItem in currentItem))
|
||||
{
|
||||
currentItem[pathItem] = {
|
||||
__sizeBT: 0
|
||||
}
|
||||
}
|
||||
currentItem = currentItem[pathItem]
|
||||
currentItem.__sizeBT += file.size;
|
||||
})
|
||||
rootTree.__sizeBT += file.size;
|
||||
});
|
||||
return rootTree;
|
||||
}
|
||||
|
||||
const treeToTorrentFiles = (tree) => {
|
||||
let arr = [];
|
||||
for(let file in tree)
|
||||
{
|
||||
if(file == '__sizeBT')
|
||||
continue;
|
||||
|
||||
arr.push(<ListItem
|
||||
key={file}
|
||||
primaryText={file}
|
||||
secondaryText={formatBytes(tree[file].__sizeBT)}
|
||||
nestedItems={treeToTorrentFiles(tree[file])}
|
||||
primaryTogglesNestedList={true}
|
||||
innerDivStyle={{wordBreak: 'break-word'}}
|
||||
leftIcon={tree[file] && Object.keys(tree[file]).length > 1 ? <FileFolder /> : contentIcon(fileTypeDetect({path: file}))}
|
||||
/>);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
const TorrentFiles = (props) => {
|
||||
let filesList = props.torrent.filesList;
|
||||
let tree = buildFilesTree(filesList);
|
||||
return (
|
||||
<List className='w100p'>
|
||||
{
|
||||
filesList.length > 0
|
||||
?
|
||||
<div className='w100p'>
|
||||
<Subheader inset={true}>Content of the torrent:</Subheader>
|
||||
{treeToTorrentFiles(tree)}
|
||||
</div>
|
||||
:
|
||||
<div className='column center'>
|
||||
<span className='pad0-75'>Processing files...</span>
|
||||
<LinearProgress mode="indeterminate" />
|
||||
</div>
|
||||
}
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
const TorrentInformation = (props) => {
|
||||
let torrent = props.torrent;
|
||||
return (
|
||||
<List className='w100p'>
|
||||
<Subheader inset={true}>Information about torrent</Subheader>
|
||||
<ListItem
|
||||
//leftAvatar={<Avatar icon={<ActionAssignment />} backgroundColor={blue500} />}
|
||||
rightIcon={<ActionInfo />}
|
||||
primaryText="Torrent Name"
|
||||
secondaryText={<span className='break-word' style={{whiteSpace: 'normal'}}>{torrent.name}</span>}
|
||||
/>
|
||||
<ListItem
|
||||
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
|
||||
rightIcon={<ActionInfo />}
|
||||
primaryText="Torrent Size"
|
||||
secondaryText={formatBytes(torrent.size)}
|
||||
/>
|
||||
<ListItem
|
||||
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
|
||||
rightIcon={<ActionInfo />}
|
||||
primaryText="Torrent contains files"
|
||||
secondaryText={torrent.files}
|
||||
onClick={() => {
|
||||
if(!props.parent)
|
||||
return
|
||||
|
||||
props.parent.setState({
|
||||
value: 'files'
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<ListItem
|
||||
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
|
||||
rightIcon={<ActionInfo />}
|
||||
primaryText="Indexed/Added torrent date"
|
||||
secondaryText={moment(torrent.added).format('MMMM Do YYYY, h:mm:ss')}
|
||||
/>
|
||||
<ListItem
|
||||
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
|
||||
rightIcon={<ActionInfo />}
|
||||
primaryText="Content type"
|
||||
secondaryText={torrent.contentType || 'unknown'}
|
||||
/>
|
||||
<ListItem
|
||||
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
|
||||
rightIcon={<ActionInfo />}
|
||||
primaryText="Category"
|
||||
secondaryText={torrent.contentCategory || 'unknown'}
|
||||
/>
|
||||
</List>
|
||||
);
|
||||
}
|
||||
|
||||
export default class TorrentPage extends Page {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: 'info',
|
||||
searchingIndicator: false,
|
||||
voting: false,
|
||||
voted: false,
|
||||
};
|
||||
this.setTitle('Information about torrent');
|
||||
}
|
||||
|
||||
changeTab(tab) {
|
||||
if(this.state.value != tab) {
|
||||
this.setState({
|
||||
value: tab
|
||||
});
|
||||
console.log('change');
|
||||
}
|
||||
}
|
||||
onSwipeRight() {
|
||||
this.changeTab('files');
|
||||
}
|
||||
onSwipeLeft() {
|
||||
this.changeTab('info');
|
||||
}
|
||||
|
||||
handleChange = (value) => {
|
||||
if(value == 'main') {
|
||||
window.router('/');
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
value: value,
|
||||
});
|
||||
};
|
||||
getTorrentInfo() {
|
||||
window.torrentSocket.emit('torrent', this.props.hash, {files: true}, window.customLoader((data) => {
|
||||
if(data) {
|
||||
this.torrent = data
|
||||
this.setTitle(this.torrent.name + ' - RatsOnTheBoat.org');
|
||||
if(this.torrent.contentCategory == 'xxx') {
|
||||
this.setMetaTag('robots', 'noindex');
|
||||
}
|
||||
//this.forceUpdate(); // вызывается через searchingIndicator
|
||||
|
||||
// Получаем более новую статистику пира
|
||||
if((new Date).getTime() - this.torrent.trackersChecked > 10 * 60 * 1000) {
|
||||
window.torrentSocket.emit('checkTrackers', this.torrent.hash);
|
||||
}
|
||||
}
|
||||
}, () => {
|
||||
this.setState({
|
||||
searchingIndicator: true
|
||||
});
|
||||
}, () => {
|
||||
this.setState({
|
||||
searchingIndicator: false
|
||||
});
|
||||
}));
|
||||
}
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
|
||||
this.getTorrentInfo();
|
||||
this.filesUpdated = (hash) => {
|
||||
if(this.props.hash != hash)
|
||||
return;
|
||||
|
||||
this.getTorrentInfo();
|
||||
}
|
||||
window.torrentSocket.on('filesReady', this.filesUpdated);
|
||||
|
||||
this.trackerUpdate = (info) => {
|
||||
if(this.props.hash != info.hash)
|
||||
return;
|
||||
|
||||
if(!this.torrent)
|
||||
return;
|
||||
|
||||
Object.assign(this.torrent, info);
|
||||
this.forceUpdate();
|
||||
}
|
||||
window.torrentSocket.on('trackerTorrentUpdate', this.trackerUpdate);
|
||||
|
||||
this.onVote = ({hash, good, bad}) => {
|
||||
if(this.props.hash != hash)
|
||||
return;
|
||||
|
||||
if(!this.torrent)
|
||||
return;
|
||||
|
||||
this.torrent.good = good;
|
||||
this.torrent.bad = bad;
|
||||
this.forceUpdate();
|
||||
}
|
||||
window.torrentSocket.on('vote', this.onVote);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
if(this.filesUpdated)
|
||||
window.torrentSocket.off('filesReady', this.filesUpdated);
|
||||
if(this.trackerUpdate)
|
||||
window.torrentSocket.off('trackerTorrentUpdate', this.trackerUpdate);
|
||||
if(this.onVote)
|
||||
window.torrentSocket.off('vote', this.onVote);
|
||||
if(this.torrent && this.torrent.contentCategory == 'xxx') {
|
||||
this.removeMetaTag('robots');
|
||||
}
|
||||
}
|
||||
vote(good) {
|
||||
if(!this.torrent)
|
||||
return;
|
||||
|
||||
this.setState({
|
||||
voting: true
|
||||
});
|
||||
window.torrentSocket.emit('vote', this.torrent.hash, !!good, window.customLoader((success) => {
|
||||
this.setState({
|
||||
voted: true,
|
||||
voting: false
|
||||
});
|
||||
}));
|
||||
}
|
||||
render() {
|
||||
const style = {
|
||||
refresh: {
|
||||
display: 'inline-block',
|
||||
position: 'relative',
|
||||
},
|
||||
};
|
||||
|
||||
if(this.state.searchingIndicator) {
|
||||
return (
|
||||
<div className='pad1 w100p column center'>
|
||||
<RefreshIndicator
|
||||
size={50}
|
||||
left={0}
|
||||
top={0}
|
||||
loadingColor="#FF9800"
|
||||
status="loading"
|
||||
style={style.refresh}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let torrentRating;
|
||||
if(this.torrent) {
|
||||
torrentRating = Math.round(rating(this.torrent.good, this.torrent.bad) * 100);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w100p column center">
|
||||
{
|
||||
this.torrent
|
||||
?
|
||||
<Tabs
|
||||
className='w100p'
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
>
|
||||
<Tab label="Back to main" value="main" />
|
||||
<Tab label="Information" value="info" >
|
||||
<div className='column w100p'>
|
||||
<div className='row w100p torrent-information-row'>
|
||||
<div className='info-table'>
|
||||
<TorrentInformation torrent={this.torrent} parent={this} />
|
||||
</div>
|
||||
<div style={{flexBasis: '40%'}} className='column center w100p'>
|
||||
<img src={NoImage} className='pad0-75' style={{height: '200px'}} />
|
||||
<RaisedButton
|
||||
href={`magnet:?xt=urn:btih:${this.torrent.hash}`}
|
||||
target="_self"
|
||||
label="Download"
|
||||
secondary={true}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
window.open(`magnet:?xt=urn:btih:${this.torrent.hash}`, '_self')
|
||||
}}
|
||||
icon={<svg fill='white' viewBox="0 0 24 24"><path d="M17.374 20.235c2.444-2.981 6.626-8.157 6.626-8.157l-3.846-3.092s-2.857 3.523-6.571 8.097c-4.312 5.312-11.881-2.41-6.671-6.671 4.561-3.729 8.097-6.57 8.097-6.57l-3.092-3.842s-5.173 4.181-8.157 6.621c-2.662 2.175-3.76 4.749-3.76 7.24 0 5.254 4.867 10.139 10.121 10.139 2.487 0 5.064-1.095 7.253-3.765zm4.724-7.953l-1.699 2.111-1.74-1.397 1.701-2.114 1.738 1.4zm-10.386-10.385l1.4 1.738-2.113 1.701-1.397-1.74 2.11-1.699z"/></svg>}
|
||||
/>
|
||||
<div className='fs0-75 pad0-75 center column' style={{color: 'rgba(0, 0, 0, 0.541176)'}}><div>BTIH:</div><div>{this.torrent.hash}</div></div>
|
||||
{
|
||||
this.torrent.seeders || this.torrent.leechers || this.torrent.completed
|
||||
?
|
||||
<div className='fs0-85 pad0-75 center column'>
|
||||
<div className='pad0-25' style={{color: '#00C853'}}>seeders: {this.torrent.seeders}</div>
|
||||
<div className='pad0-25' style={{color: '#AA00FF'}}>leechers: {this.torrent.leechers}</div>
|
||||
<div className='pad0-25' style={{color: '#FF6D00'}}>completed: {this.torrent.completed}</div>
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
!this.state.voted && !this.state.voting
|
||||
?
|
||||
<div className='row pad0-25'>
|
||||
<RaisedButton
|
||||
label={`Good (${this.torrent.good})`}
|
||||
labelColor="white"
|
||||
backgroundColor="#00C853"
|
||||
icon={
|
||||
<svg viewBox="0 0 489.543 489.543" fill="white">
|
||||
<g>
|
||||
<path d="M270.024,0c-22.6,0-15,48.3-15,48.3s-48.3,133.2-94.5,168.7c-9.9,10.4-16.1,21.9-20,31.3l0,0l0,0
|
||||
c-0.9,2.3-1.7,4.5-2.4,6.5c-3.1,6.3-9.7,16-23.8,24.5l46.2,200.9c0,0,71.5,9.3,143.2,7.8c28.7,2.3,59.1,2.5,83.3-2.7
|
||||
c82.2-17.5,61.6-74.8,61.6-74.8c44.3-33.3,19.1-74.9,19.1-74.9c39.4-41.1,0.7-75.6,0.7-75.6s21.3-33.2-6.2-58.3
|
||||
c-34.3-31.4-127.4-10.5-127.4-10.5l0,0c-6.5,1.1-13.4,2.5-20.8,4.3c0,0-32.2,15,0-82.7C346.324,15.1,292.624,0,270.024,0z"/>
|
||||
<path d="M127.324,465.7l-35-166.3c-2-9.5-11.6-17.3-21.3-17.3h-66.8l-0.1,200.8h109.1C123.024,483,129.324,475.2,127.324,465.7z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
}
|
||||
onClick={() => this.vote(true)}
|
||||
/>
|
||||
<RaisedButton
|
||||
style={{marginLeft: '9px'}}
|
||||
label={`Bad (${this.torrent.bad})`}
|
||||
labelColor="white"
|
||||
backgroundColor="#D50000"
|
||||
icon={
|
||||
<svg viewBox="0 0 487.643 487.643" fill="white">
|
||||
<g>
|
||||
<path d="M113.869,209.443l46-200.1c0,0,71.2-9.3,142.6-7.8c28.5-2.3,58.9-2.5,83,2.7c81.9,17.4,61.4,74.5,61.4,74.5
|
||||
c44.2,33.2,19,74.6,19,74.6c39.2,41,0.7,75.3,0.7,75.3s21.2,33-6.1,58c-34.2,31.2-126.9,10.5-126.9,10.5l0,0
|
||||
c-6.4-1.1-13.3-2.5-20.7-4.2c0,0-32.1-15,0,82.4s-21.4,112.3-43.9,112.3s-15-48.1-15-48.1s-48.1-132.7-94.1-168
|
||||
c-9.9-10.4-16.1-21.8-19.9-31.2l0,0l0,0c-0.9-2.3-1.7-4.5-2.4-6.5C134.469,227.543,127.869,217.843,113.869,209.443z
|
||||
M70.869,206.643c9.7,0,19.2-7.7,21.2-17.2l34.8-165.6c2-9.5-4.3-17.2-14-17.2H4.169l0.1,200H70.869z"/>
|
||||
</g>
|
||||
</svg>
|
||||
}
|
||||
onClick={() => this.vote(false)}
|
||||
/>
|
||||
</div>
|
||||
:
|
||||
this.state.voting
|
||||
?
|
||||
<div>voting...</div>
|
||||
:
|
||||
<div>Thank you for voting!</div>
|
||||
}
|
||||
{
|
||||
this.torrent.good > 0 || this.torrent.bad > 0
|
||||
?
|
||||
<div className='w100p' style={{padding: '7px 35px', marginTop: '10px'}}>
|
||||
<LinearProgress
|
||||
mode="determinate"
|
||||
value={torrentRating}
|
||||
color={torrentRating >= 50 ? '#00E676' : '#FF3D00'}
|
||||
style={{
|
||||
height: '5px',
|
||||
}}
|
||||
/>
|
||||
<div className='row center pad0-75 fs0-85' style={{color: torrentRating >= 50 ? '#00E676' : '#FF3D00'}}>Torrent rating: {torrentRating}%</div>
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab label="Files" value="files" >
|
||||
<TorrentFiles torrent={this.torrent} />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
:
|
||||
null
|
||||
}
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
247
src/app/torrent.js
Normal file
247
src/app/torrent.js
Normal file
@ -0,0 +1,247 @@
|
||||
import React, { Component } from 'react';
|
||||
import formatBytes from './format-bytes'
|
||||
import {ListItem} from 'material-ui/List';
|
||||
import Divider from 'material-ui/Divider';
|
||||
|
||||
const contentIcon = (type, category) => {
|
||||
if(category == 'xxx')
|
||||
{
|
||||
return (
|
||||
<svg viewBox="0 0 18.282 18.282" fill="grey">
|
||||
<g>
|
||||
<path d="M16.435,3.832H1.847C0.827,3.832,0,4.659,0,5.678v6.925c0,1.021,0.827,1.848,1.847,1.848h14.588
|
||||
c1.021,0,1.847-0.827,1.847-1.848V5.678C18.282,4.659,17.455,3.832,16.435,3.832z M3.194,7.123H2.583v4.042h0.611v0.54H1.876V6.583
|
||||
h1.318V7.123z M6.197,10.986l-0.392-0.784C5.644,9.9,5.541,9.676,5.419,9.425H5.406c-0.09,0.251-0.199,0.476-0.334,0.777
|
||||
l-0.36,0.784H3.593l1.254-2.191L3.638,6.654h1.125l0.379,0.791C5.27,7.709,5.367,7.921,5.47,8.165h0.013
|
||||
c0.103-0.276,0.187-0.47,0.296-0.72l0.366-0.791h1.118L6.042,8.768l1.285,2.218C7.327,10.986,6.197,10.986,6.197,10.986z
|
||||
M10.068,10.986l-0.392-0.784C9.515,9.9,9.412,9.676,9.29,9.425H9.277c-0.091,0.251-0.2,0.476-0.335,0.777l-0.359,0.784H7.464
|
||||
l1.253-2.191L7.509,6.654h1.125l0.379,0.791c0.128,0.264,0.225,0.476,0.328,0.72h0.013c0.103-0.276,0.186-0.47,0.295-0.72
|
||||
l0.366-0.791h1.119L9.913,8.768l1.284,2.218C11.197,10.986,10.068,10.986,10.068,10.986z M13.94,10.986l-0.393-0.784
|
||||
c-0.16-0.302-0.263-0.526-0.386-0.777h-0.012c-0.091,0.251-0.2,0.476-0.335,0.777l-0.36,0.784h-1.117l1.253-2.191l-1.209-2.141
|
||||
h1.125l0.379,0.791c0.129,0.264,0.226,0.476,0.328,0.72h0.013c0.104-0.276,0.187-0.47,0.296-0.72l0.366-0.791h1.118l-1.221,2.114
|
||||
l1.286,2.218C15.071,10.986,13.94,10.986,13.94,10.986z M16.756,11.705h-1.311v-0.54h0.611V7.116h-0.611v-0.54h1.311V11.705z"/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case 'video':
|
||||
return (
|
||||
<svg viewBox="0 0 491.858 491.858" fill="grey">
|
||||
<path d="M357.714,423.331c0,9.328-10.676,16.891-23.847,16.891H23.847C10.676,440.222,0,432.659,0,423.331V203.735
|
||||
c0-9.33,10.676-16.892,23.847-16.892h310.02c13.171,0,23.847,7.564,23.847,16.892V423.331L357.714,423.331z"/>
|
||||
<circle cx="89.428" cy="118.706" r="59.619"/>
|
||||
<circle cx="253.381" cy="103.801" r="74.524"/>
|
||||
<path d="M491.858,447.677c0,0-1.986,14.904-15.899,14.904c-13.912,0-103.34-83.42-103.34-94.397V258.882
|
||||
c0-10.976,87.443-94.398,103.34-94.398c15.899,0,15.899,14.905,15.899,14.905V447.677z"/>
|
||||
</svg>
|
||||
)
|
||||
case 'audio':
|
||||
return (
|
||||
<svg viewBox="0 0 46 46" fill="grey">
|
||||
<path d="M28.38,0c-0.551,0-1.097,0.153-1.579,0.444c-0.046,0.027-0.09,0.059-0.13,0.093L13.121,12H2.487c-0.553,0-1,0.447-1,1v19
|
||||
c0,0.553,0.447,1,1,1h10.61L26.64,45.436c0.05,0.046,0.104,0.086,0.161,0.12C27.284,45.847,27.83,46,28.38,46
|
||||
c1.713,0,3.106-1.416,3.106-3.156V3.156C31.487,1.416,30.093,0,28.38,0z M14.487,31c0,0.553-0.447,1-1,1s-1-0.447-1-1v-4
|
||||
c0-0.553,0.447-1,1-1s1,0.447,1,1V31z M14.487,18c0,0.553-0.447,1-1,1s-1-0.447-1-1v-4c0-0.553,0.447-1,1-1s1,0.447,1,1V18z"/>
|
||||
<path d="M44.513,22.5c0-5.972-4.009-11.302-9.749-12.962c-0.533-0.151-1.084,0.152-1.238,0.684
|
||||
c-0.153,0.53,0.152,1.085,0.684,1.238c4.889,1.413,8.304,5.953,8.304,11.04s-3.415,9.627-8.304,11.04
|
||||
c-0.531,0.153-0.837,0.708-0.684,1.238c0.127,0.438,0.526,0.723,0.961,0.723c0.092,0,0.185-0.013,0.277-0.039
|
||||
C40.504,33.802,44.513,28.472,44.513,22.5z"/>
|
||||
</svg>
|
||||
|
||||
)
|
||||
case 'pictures':
|
||||
return (
|
||||
<svg viewBox="0 0 58 58" fill="grey">
|
||||
<path d="M57,6H1C0.448,6,0,6.447,0,7v44c0,0.553,0.448,1,1,1h56c0.552,0,1-0.447,1-1V7C58,6.447,57.552,6,57,6z M16,17
|
||||
c3.071,0,5.569,2.498,5.569,5.569c0,3.07-2.498,5.568-5.569,5.568s-5.569-2.498-5.569-5.568C10.431,19.498,12.929,17,16,17z
|
||||
M52.737,35.676c-0.373,0.406-1.006,0.435-1.413,0.062L40.063,25.414l-9.181,10.054l4.807,4.807c0.391,0.391,0.391,1.023,0,1.414
|
||||
s-1.023,0.391-1.414,0L23.974,31.389L7.661,45.751C7.471,45.918,7.235,46,7,46c-0.277,0-0.553-0.114-0.751-0.339
|
||||
c-0.365-0.415-0.325-1.047,0.09-1.412l17.017-14.982c0.396-0.348,0.994-0.329,1.368,0.044l4.743,4.743l9.794-10.727
|
||||
c0.179-0.196,0.429-0.313,0.694-0.325c0.264-0.006,0.524,0.083,0.72,0.262l12,11C53.083,34.636,53.11,35.269,52.737,35.676z"/>
|
||||
</svg>
|
||||
)
|
||||
case 'application':
|
||||
return (
|
||||
<svg viewBox="0 0 483.85 483.85" fill="grey">
|
||||
<path d="M471.325,211.856l-56.9-56.9c-23.4-23.4-9.1-48.1,16.4-49.6c42-2.6,65.6-47.4,31.3-84.7c-37.3-34.2-81.9-10.7-84.5,31.2
|
||||
c-1.6,25.5-26.5,39.9-49.8,16.6l-55.7-55.7c-16.7-16.7-43.8-16.7-60.5,0l-56.4,56.4c-23.4,23.4-48.2,8.9-49.8-16.6
|
||||
c-2.6-42-47.6-65.9-84.9-31.6c-34.4,37.4-10.5,82.4,31.5,85c25.5,1.6,40,26.5,16.7,49.9l-56.2,56.1c-16.7,16.7-16.7,43.8,0,60.5
|
||||
l55.7,55.7c23.4,23.3,9.5,47.6-16,49.2c-42,2.6-65.5,47.3-31.2,84.6c37.3,34.3,81.8,10.9,84.4-31.1c1.6-25.5,26-39.5,49.4-16.2
|
||||
l56.2,56.2c17,17,44.8,17,61.8,0.1l39.4-39.4l16.9-16.9c22.1-23.1,7.8-47.4-17.4-49c-42-2.6-65.8-47.6-31.5-84.9
|
||||
c37.3-34.3,82.3-10.4,84.9,31.6c1.6,25.2,25.8,39.4,48.9,17.3l15.3-15.3l41.2-41.2c0.1-0.1,0.1-0.1,0.2-0.2l0.6-0.6
|
||||
C488.025,255.656,488.025,228.556,471.325,211.856z"/>
|
||||
</svg>
|
||||
)
|
||||
case 'books':
|
||||
return (
|
||||
<svg viewBox="0 0 296.999 296.999" fill="grey">
|
||||
<g>
|
||||
<path d="M45.432,35.049c-0.008,0-0.017,0-0.025,0c-2.809,0-5.451,1.095-7.446,3.085c-2.017,2.012-3.128,4.691-3.128,7.543
|
||||
v159.365c0,5.844,4.773,10.61,10.641,10.625c24.738,0.059,66.184,5.215,94.776,35.136V84.023c0-1.981-0.506-3.842-1.461-5.382
|
||||
C115.322,40.849,70.226,35.107,45.432,35.049z"/>
|
||||
<path d="M262.167,205.042V45.676c0-2.852-1.111-5.531-3.128-7.543c-1.995-1.99-4.639-3.085-7.445-3.085c-0.009,0-0.018,0-0.026,0
|
||||
c-24.793,0.059-69.889,5.801-93.357,43.593c-0.955,1.54-1.46,3.401-1.46,5.382v166.779
|
||||
c28.592-29.921,70.038-35.077,94.776-35.136C257.394,215.651,262.167,210.885,262.167,205.042z"/>
|
||||
<path d="M286.373,71.801h-7.706v133.241c0,14.921-12.157,27.088-27.101,27.125c-20.983,0.05-55.581,4.153-80.084,27.344
|
||||
c42.378-10.376,87.052-3.631,112.512,2.171c3.179,0.724,6.464-0.024,9.011-2.054c2.538-2.025,3.994-5.052,3.994-8.301V82.427
|
||||
C297,76.568,292.232,71.801,286.373,71.801z"/>
|
||||
<path d="M18.332,205.042V71.801h-7.706C4.768,71.801,0,76.568,0,82.427v168.897c0,3.25,1.456,6.276,3.994,8.301
|
||||
c2.545,2.029,5.827,2.78,9.011,2.054c25.46-5.803,70.135-12.547,112.511-2.171c-24.502-23.19-59.1-27.292-80.083-27.342
|
||||
C30.49,232.13,18.332,219.963,18.332,205.042z"/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
case 'archive':
|
||||
return (
|
||||
<svg viewBox="0 0 390 390" fill="grey">
|
||||
<g>
|
||||
<path d="M182.681,205.334c0,5.21,4.227,9.436,9.436,9.436h5.765c5.21,0,9.436-4.226,9.436-9.436c0-5.211-4.226-9.438-9.436-9.438
|
||||
h-5.765C186.908,195.897,182.681,200.123,182.681,205.334z"/>
|
||||
<path d="M383.889,126.058c-4.478-5.191-10.868-8.314-17.674-8.686V64.899c0-25.562-20.797-46.359-46.361-46.359h-75.278
|
||||
c-25.052,0-38.351,10.578-48.062,18.303c-7.807,6.208-12.518,9.955-22.626,9.955H65.099c-22.78,0-41.313,18.062-41.313,40.264
|
||||
v30.311c-6.806,0.371-13.196,3.494-17.674,8.686c-4.78,5.541-6.912,12.888-5.839,20.125l30.194,203.803
|
||||
c1.828,12.338,12.417,21.475,24.89,21.475h279.286c12.473,0,23.063-9.137,24.891-21.475l30.195-203.802
|
||||
C390.801,138.945,388.669,131.599,383.889,126.058z M161.833,320.412v-10.428c0-2.399,1.945-4.345,4.345-4.345h32.443
|
||||
c2.399,0,4.345,1.945,4.345,4.345v10.428c0,2.4-1.945,4.345-4.345,4.345h-32.443C163.778,324.757,161.833,322.813,161.833,320.412z
|
||||
M195,160.323c4.274,0,7.738-3.467,7.738-7.738v-10.718h6.07c4.615,0,8.445,3.564,8.776,8.167l4.893,67.999
|
||||
c0.175,2.438-0.671,4.838-2.336,6.626c-1.664,1.79-3.996,2.806-6.441,2.806h-37.401c-2.444,0-4.777-1.016-6.441-2.806
|
||||
c-1.665-1.788-2.511-4.188-2.336-6.626l4.893-67.999c0.331-4.603,4.161-8.167,8.776-8.167h6.07v10.718
|
||||
C187.262,156.856,190.726,160.323,195,160.323z M198.621,244.81c2.399,0,4.345,1.945,4.345,4.345v10.429
|
||||
c0,2.399-1.945,4.345-4.345,4.345h-32.443c-2.4,0-4.345-1.945-4.345-4.345v-10.429c0-2.399,1.945-4.345,4.345-4.345H198.621z
|
||||
M228.167,350.538c0,2.399-1.945,4.345-4.345,4.345h-32.443c-2.399,0-4.345-1.945-4.345-4.345V340.11
|
||||
c0-2.4,1.946-4.346,4.345-4.346h32.443c2.399,0,4.345,1.945,4.345,4.346V350.538z M228.167,289.708c0,2.4-1.945,4.345-4.345,4.345
|
||||
h-32.443c-2.399,0-4.345-1.944-4.345-4.345V279.28c0-2.4,1.946-4.346,4.345-4.346h32.443c2.399,0,4.345,1.945,4.345,4.346V289.708z
|
||||
M335.919,117.333H54.081V87.062c0-6.239,5.602-9.968,11.019-9.968h108.788c20.687,0,32.219-9.173,41.484-16.542
|
||||
c8.552-6.803,14.732-11.717,29.204-11.717h75.278c8.858,0,16.066,7.206,16.066,16.064V117.333z"/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
case 'disc':
|
||||
return (
|
||||
<svg viewBox="0 0 49.652 49.652" fill="grey">
|
||||
<g>
|
||||
<circle cx="24.826" cy="24.825" r="3.529"/>
|
||||
<path d="M42.381,7.271C37.693,2.582,31.458,0,24.826,0C18.195,0,11.96,2.583,7.271,7.271c-9.68,9.68-9.68,25.43,0,35.11
|
||||
c4.689,4.688,10.923,7.271,17.555,7.271c6.632,0,12.867-2.582,17.555-7.271C52.061,32.701,52.061,16.951,42.381,7.271z
|
||||
M24.86,45.002l0.039-12.587c-1.967,0.019-3.941-0.719-5.442-2.22c-2.965-2.965-2.964-7.772,0-10.737
|
||||
c0.022-0.022,0.047-0.04,0.069-0.062l-8.935-8.936c4.059-4.072,9.234-6.027,14.363-5.91l-0.039,12.689
|
||||
c1.915,0.022,3.82,0.759,5.28,2.219c2.942,2.942,2.96,7.699,0.063,10.668l8.967,8.968C35.166,43.164,29.99,45.119,24.86,45.002z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<svg viewBox="0 0 123.769 123.769" fill="grey">
|
||||
<g>
|
||||
<path d="M76.05,1.568l-10.101,9.3c-2.3,2.1-5.8,2.1-8.1,0l-10.2-9.2c-3.1-2.8-8-1.7-9.6,2.1l-8.3,20h64.2l-8.3-20.1
|
||||
C84.05-0.131,79.149-1.231,76.05,1.568z"/>
|
||||
<path d="M10.749,42.068c-2.9,1.4-1.8,5.7,1.3,5.7h49.8h49.701c3.199,0,4.199-4.3,1.399-5.7l-12.2-6.3h-77.8L10.749,42.068z"/>
|
||||
<path d="M0.549,90.168l5.3,28.801c0.5,2.899,3,4.8,5.9,4.8h50.1h50.201c2.899,0,5.399-2,5.899-4.8l5.3-28.801
|
||||
c0.5-2.8-1-5.6-3.699-6.699c-12.801-5-26.2-7.7-36.801-9.301c-2.699-0.399-5.3,1.101-6.3,3.5l-10.1,22.9c-1.8,4-7.5,4-9.201-0.1
|
||||
l-9.8-22.7c-1.1-2.5-3.7-4-6.4-3.601c-10.6,1.5-24,4.301-36.7,9.301C1.549,84.469-0.051,87.269,0.549,90.168z"/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
};
|
||||
export {contentIcon}
|
||||
|
||||
export default (props) => {
|
||||
|
||||
const torrent = props.torrent;
|
||||
return (
|
||||
<div>
|
||||
<ListItem
|
||||
onClick={(e) => {
|
||||
const link = '/torrent/' + torrent.hash;
|
||||
if(e.button === 1)
|
||||
return false;
|
||||
|
||||
if(e.ctrlKey && e.button === 0) {
|
||||
let win = window.open(link, '_blank');
|
||||
//win.focus();
|
||||
return true;
|
||||
}
|
||||
|
||||
window.router(link)
|
||||
}}
|
||||
primaryText={
|
||||
<a href={'/torrent/' + torrent.hash} ref={(node) => {
|
||||
if(node)
|
||||
node.onclick = () => { return false }
|
||||
}}>
|
||||
<span className='break-word' style={{
|
||||
color: torrent.contentCategory != 'xxx' ? 'black' : 'grey'
|
||||
}}>
|
||||
{torrent.name}
|
||||
</span>
|
||||
</a>
|
||||
}
|
||||
secondaryText={
|
||||
<a href={'/torrent/' + torrent.hash} ref={(node) => {
|
||||
if(node)
|
||||
node.onclick = () => { return false }
|
||||
}}>
|
||||
<div className='column' style={{height: 'auto', whiteSpace: 'normal', paddingTop: '0.30em'}}>
|
||||
<div>
|
||||
{
|
||||
formatBytes(torrent.size, 1) + ' (' + torrent.files + ' files)'
|
||||
}
|
||||
</div>
|
||||
{
|
||||
torrent.path && torrent.path.length > 0
|
||||
?
|
||||
torrent.path.map((path, index) => {
|
||||
return <div key={index} className='break-word fs0-75' style={{paddingTop: '0.3em', marginLeft: '0.6em'}}>{path}</div>
|
||||
})
|
||||
:
|
||||
null
|
||||
}
|
||||
{
|
||||
torrent.seeders || torrent.leechers || torrent.completed
|
||||
?
|
||||
<div className='break-word fs0-85' style={{paddingTop: '0.35em'}}>
|
||||
<span style={{color: (torrent.seeders > 0 ? '#00C853' : 'grey')}}>{torrent.seeders} seeders</span>
|
||||
<span style={{color: (torrent.leechers > 0 ? '#AA00FF' : 'grey'), marginLeft: '12px'}}>{torrent.leechers} leechers</span>
|
||||
<span style={{color: (torrent.completed > 0 ? '#FF6D00' : 'grey'), marginLeft: '12px'}}>{torrent.completed} completed</span>
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
leftIcon={contentIcon(torrent.contentType, torrent.contentCategory)}
|
||||
rightIcon={
|
||||
<a href={`magnet:?xt=urn:btih:${torrent.hash}`}>
|
||||
<svg style={{
|
||||
height: '24px',
|
||||
fill: torrent.contentCategory != 'xxx' ? 'black' : 'grey'
|
||||
}} onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var win = window.open(`magnet:?xt=urn:btih:${torrent.hash}`, '_self');
|
||||
}} viewBox="0 0 24 24">
|
||||
<path d="M15.82 10.736l-5.451 6.717c-.561.691-1.214 1.042-1.94 1.042-1.144
|
||||
0-2.327-.899-2.753-2.091-.214-.6-.386-1.76.865-2.784 3.417-2.794 6.716-5.446
|
||||
6.716-5.446l-3.363-4.174s-4.532 3.657-6.771 5.487c-2.581 2.108-3.123 4.468-3.123
|
||||
6.075 0 4.416 4.014 8.438 8.42 8.438 1.604 0 3.963-.543 6.084-3.128 1.835-2.237
|
||||
5.496-6.773 5.496-6.773l-4.18-3.363zm-2.604 9.079c-1.353 1.647-3.01 2.519-4.796
|
||||
2.519-3.471 0-6.753-3.291-6.753-6.771 0-1.789.867-3.443 2.51-4.785 1.206-.986
|
||||
2.885-2.348 4.18-3.398l1.247 1.599c-1.074.87-2.507 2.033-4.118 3.352-1.471
|
||||
1.202-1.987 2.935-1.38 4.634.661 1.853 2.479 3.197 4.322 3.197h.001c.86 0
|
||||
2.122-.288 3.233-1.658l3.355-4.134 1.572 1.294c-1.044 1.291-2.392 2.954-3.373
|
||||
4.151zm6.152-7.934l4.318-2.88-1.575-.638 1.889-2.414-4.421 2.788 1.716.695-1.927
|
||||
2.449zm-7.292-7.186l4.916-1.667-1.356-1.022 2.448-2.006-4.991 1.712
|
||||
1.478 1.114-2.495 1.869z"/></svg>
|
||||
</a>
|
||||
}
|
||||
/>
|
||||
<Divider />
|
||||
</div>
|
||||
)
|
||||
}
|
95
src/app/touch.js
Normal file
95
src/app/touch.js
Normal file
@ -0,0 +1,95 @@
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
let listenSwipe = (component, handlers) =>
|
||||
{
|
||||
let element = ReactDOM.findDOMNode(component),
|
||||
startX,
|
||||
startY,
|
||||
distanceX,
|
||||
distanceY,
|
||||
threshold = 110,
|
||||
allowedTime = 280,
|
||||
thresholdAlternativeAxis = 145,
|
||||
elapsedTime,
|
||||
startTime;
|
||||
|
||||
let touchFunctions = {
|
||||
touchstart : (e) => {
|
||||
let touchObject = e.changedTouches[0]
|
||||
|
||||
distanceX = 0;
|
||||
distanceY = 0;
|
||||
|
||||
startX = touchObject.pageX
|
||||
startY = touchObject.pageY
|
||||
|
||||
startTime = new Date().getTime()
|
||||
|
||||
if(handlers && handlers.preventDefault)
|
||||
e.preventDefault();
|
||||
|
||||
if(handlers && handlers.initSwipe)
|
||||
handlers.initSwipe.call(component);
|
||||
},
|
||||
touchmove : (e) => {
|
||||
if(handlers && handlers.preventDefault)
|
||||
e.preventDefault();
|
||||
},
|
||||
touchend : (e) => {
|
||||
let touchObject = e.changedTouches[0];
|
||||
|
||||
distanceX = touchObject.pageX - startX;
|
||||
distanceY = touchObject.pageY - startY;
|
||||
|
||||
elapsedTime = new Date().getTime() - startTime; // get time elapsed
|
||||
|
||||
let params = {
|
||||
startX,
|
||||
startY,
|
||||
endX: touchObject.pageX,
|
||||
endY: touchObject.pageY,
|
||||
distanceX,
|
||||
distanceY
|
||||
};
|
||||
|
||||
if (elapsedTime <= allowedTime)
|
||||
{
|
||||
if(distanceX >= threshold && Math.abs(distanceY) <= thresholdAlternativeAxis)
|
||||
{
|
||||
if(handlers && handlers.left)
|
||||
handlers.left.call(component, params);
|
||||
} else
|
||||
if(-distanceX >= threshold && Math.abs(distanceY) <= thresholdAlternativeAxis) {
|
||||
if(handlers && handlers.right)
|
||||
handlers.right.call(component, params);
|
||||
} else
|
||||
if(distanceY >= threshold && Math.abs(distanceX) <= thresholdAlternativeAxis) {
|
||||
if(handlers && handlers.top)
|
||||
handlers.top.call(component, params);
|
||||
} else
|
||||
if(-distanceY >= threshold && Math.abs(distanceX) <= thresholdAlternativeAxis) {
|
||||
if(handlers && handlers.bottom)
|
||||
handlers.bottom.call(component, params);
|
||||
}
|
||||
}
|
||||
|
||||
if(handlers && handlers.preventDefault)
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
element.addEventListener('touchstart', touchFunctions.touchstart, false);
|
||||
element.addEventListener('touchmove', touchFunctions.touchmove, false);
|
||||
element.addEventListener('touchend', touchFunctions.touchend, false);
|
||||
|
||||
return touchFunctions;
|
||||
}
|
||||
|
||||
let removeSwipeListener = (component, touchFunctions) => {
|
||||
let element = ReactDOM.findDOMNode(component);
|
||||
element.removeEventListener('touchstart', touchFunctions.touchstart);
|
||||
element.removeEventListener('touchmove', touchFunctions.touchmove);
|
||||
element.removeEventListener('touchend', touchFunctions.touchend);
|
||||
}
|
||||
|
||||
export { listenSwipe, removeSwipeListener }
|
Reference in New Issue
Block a user