diff --git a/client.go b/client.go deleted file mode 100644 index 81710551..00000000 --- a/client.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "fmt" - "github.com/anacrolix/torrent" - "time" -) - -// ClientError formats errors coming from the client. -type ClientError struct { - Type string - Origin error -} - -func (clientError ClientError) Error() string { - return fmt.Sprintf("Error %s: %s\n", clientError.Type, clientError.Origin) -} - -type ClientConfig struct { - TorrentPath string - Port int - TorrentPort int - Seed bool - TCP bool - MaxConnections int - DownloadDir string -} - - -type Client struct { - Client *torrent.Client - Torrent *torrent.Torrent - Name string - Progress int64 - Status string - Seeds int - Peers int - DownloadSpeed int64 - UploadSpeed int64 - ETA time.Duration - Ratio int - Avail int - Config ClientConfig -} - - diff --git a/config.toml b/config.toml index e804c313..73fd5487 100644 --- a/config.toml +++ b/config.toml @@ -1,59 +1,101 @@ + +[serverConfig] + + ServerPort: 8000 + ServerAddr: "" #blank will bind to localhost + [torrentClientConfig] -DataDir = "downloads" #the full OR relative path of the default download directory for torrents -#The address to listen for new uTP and TCP bittorrent protocolconnections. DHT shares a UDP socket with uTP unless configured otherwise. -ListenAddr = "" #Leave Blank for default, syntax "HOST:PORT" -#Don't announce to trackers. This only leaves DHT to discover peers. -DisableTrackers = false #boolean -DisablePEX = false # boolean -# Don't create a DHT. -NoDHT = false #boolean -# Overrides the default DHT configuration, see dhtServerConfig -DHTConfig = false # boolean, set to true and edit dhtServerConfig table to utilize -# Never send chunks to peers. -NoUpload = false #boolean -#seed after download -Seed = true #boolean -# Events are data bytes sent in pieces. The burst must be large enough to fit a whole chunk. -UploadRateLimiter = "" #*rate.Limiter -#The events are bytes read from connections. The burst must be biggerthan the largest Read performed on a Conn minus one. This is likely to -#be the larger of the main read loop buffer (~4096), and the requested chunk size (~16KiB). -DownloadRateLimiter = "" #*rate.Limiter -#User-provided Client peer ID. If not present, one is generated automatically. -PeerID = "" #string -#For the bittorrent protocol. -DisableUTP = false #bool -#For the bittorrent protocol. -DisableTCP = false #bool -#Called to instantiate storage for each added torrent. Builtin backends -# are in the storage package. If not set, the "file" implementation is used. -DefaultStorage = dht.ServerConfig #storage.ClientImpl + DownloadDir = "downloads" #the full OR relative path of the default download directory for torrents -#encryption policy -IPBlocklist = "" #iplist.Ranger -DisableIPv6 = false #boolean -Debug = false #boolean + #The address to listen for new uTP and TCP bittorrent protocolconnections. DHT shares a UDP socket with uTP unless configured otherwise. + ListenAddr = "" #Leave Blank for default, syntax "HOST:PORT" + #Don't announce to trackers. This only leaves DHT to discover peers. + DisableTrackers = false #boolean + + DisablePEX = false # boolean + + # Don't create a DHT. + NoDHT = false #boolean + + # Never send chunks to peers. + NoUpload = false #boolean + + #seed after download + Seed = true #boolean + + # Events are data bytes sent in pieces. The burst must be large enough to fit a whole chunk. + UploadRateLimiter = "" #*rate.Limiter + + #The events are bytes read from connections. The burst must be biggerthan the largest Read performed on a Conn minus one. This is likely to + #be the larger of the main read loop buffer (~4096), and the requested chunk size (~16KiB). + DownloadRateLimiter = "" #*rate.Limiter + + #User-provided Client peer ID. If not present, one is generated automatically. + PeerID = "" #string + + #For the bittorrent protocol. + DisableUTP = false #bool + + #For the bittorrent protocol. + DisableTCP = false #bool + + #Called to instantiate storage for each added torrent. Builtin backends + # are in the storage package. If not set, the "file" implementation is used. + DefaultStorage = "storage.ClientImpl" + + #encryption policy + IPBlocklist = "" #of type iplist.Ranger + + DisableIPv6 = false #boolean + + Debug = false #boolean + + #HTTP *http.Client + + HTTPUserAgent = "" # HTTPUserAgent changes default UserAgent for HTTP requests + + ExtendedHandshakeClientVersion = "" + + Bep20 = "" + + # Overrides the default DHT configuration, see dhtServerConfig #advanced.. so be careful + DHTConfig = "" # default is "dht.ServerConfig" + +[EncryptionPolicy] + + DisableEncryption = false + ForceEncryption = false + PreferNoEncryption = true [dhtServerConfig] -# Set NodeId Manually. Caller must ensure that if NodeId does not conform to DHT Security Extensions, that NoSecurity is also set. -NodeId = "" #[20]byte -Conn = "" # https://godoc.org/net#PacketConn #not implemented -# Don't respond to queries from other nodes. -Passive = false # boolean -# the default addressses are "router.utorrent.com:6881","router.bittorrent.com:6881","dht.transmissionbt.com:6881","dht.aelitis.com:6881", -#https://github.com/anacrolix/dht/blob/master/dht.go -StartingNodes = "dht.GlobalBootstrapAddrs" -#Disable the DHT security extension: http://www.libtorrent.org/dht_sec.html. -NoSecurity = false -#Initial IP blocklist to use. Applied before serving and bootstrapping begins. -IPBlocklist = "" #iplist.Ranger -#Used to secure the server's ID. Defaults to the Conn's LocalAddr(). Set to the IP that remote nodes will see, -#as that IP is what they'll use to validate our ID. -PublicIP = "" #net.IP + # Set NodeId Manually. Caller must ensure that if NodeId does not conform to DHT Security Extensions, that NoSecurity is also set. + NodeId = "" #[20]byte -#Hook received queries. Return true if you don't want to propagate to the default handlers. -OnQuery = "func(query *krpc.Msg, source net.Addr) (propagate bool)" -#Called when a peer successfully announces to us. -OnAnnouncePeer = "func(infoHash metainfo.Hash, peer Peer)" -#How long to wait before resending queries that haven't received a response. Defaults to a random value between 4.5 and 5.5s. -QueryResendDelay = "func() time.Duration" \ No newline at end of file + Conn = "" # https:#godoc.org/net#PacketConn #not implemented + + # Don't respond to queries from other nodes. + Passive = false # boolean + + # the default addressses are "router.utorrent.com:6881","router.bittorrent.com:6881","dht.transmissionbt.com:6881","dht.aelitis.com:6881", + #https:#github.com/anacrolix/dht/blob/master/dht.go + StartingNodes = "dht.GlobalBootstrapAddrs" + + #Disable the DHT security extension: http:#www.libtorrent.org/dht_sec.html. + NoSecurity = false + + #Initial IP blocklist to use. Applied before serving and bootstrapping begins. + IPBlocklist = "" #of type iplist.Ranger + + #Used to secure the server's ID. Defaults to the Conn's LocalAddr(). Set to the IP that remote nodes will see, + #as that IP is what they'll use to validate our ID. + PublicIP = "" #net.IP + + #Hook received queries. Return true if you don't want to propagate to the default handlers. + OnQuery = "func(query *krpc.Msg, source net.Addr) (propagate bool)" + + #Called when a peer successfully announces to us. + OnAnnouncePeer = "func(infoHash metainfo.Hash, peer Peer)" + + #How long to wait before resending queries that haven't received a response. Defaults to a random value between 4.5 and 5.5s. + QueryResendDelay = "func() time.Duration" \ No newline at end of file diff --git a/engine/clientStructs.go b/engine/clientStructs.go index 6ef92997..bc72f872 100644 --- a/engine/clientStructs.go +++ b/engine/clientStructs.go @@ -17,6 +17,19 @@ type Message struct { //Next are the messages the server sends to the client +//RSSJSONList is a slice of gofeed.Feeds sent to the client +type RSSJSONList struct { + MessageType string + TotalRSSFeeds int + RSSFeeds []RSSFeedsNames //strings of the full rss feed +} + +//RSSFeedsNames stores all of the feeds by name and with URL +type RSSFeedsNames struct { + RSSName string + RSSFeedURL string +} + //TorrentList struct contains the torrent list that is sent to the client type TorrentList struct { //helps create the JSON structure that react expects to recieve MessageType string `json:"MessageType"` @@ -74,4 +87,6 @@ type ClientDB struct { //TODO maybe seperate out the internal bits into another DataBytesRead int64 //Internal used for calculating dl speed UpdatedAt time.Time //Internal used for calculating speeds of upload and download TorrentHash metainfo.Hash //Used to create string for TorrentHashString... not sure why I have it... make that a TODO I guess + NumberofFiles int + NumberofPieces int } diff --git a/engine/cronJobs.go b/engine/cronJobs.go new file mode 100644 index 00000000..d3589233 --- /dev/null +++ b/engine/cronJobs.go @@ -0,0 +1,48 @@ +package engine + +import ( + "fmt" + + "github.com/asdine/storm" + Storage "github.com/deranjer/goTorrent/storage" + "github.com/mmcdole/gofeed" + "github.com/robfig/cron" +) + +//InitializeCronEngine initializes and starts the cron engine so we can add tasks as needed, returns pointer to the engine +func InitializeCronEngine() *cron.Cron { //TODO add a cron to inspect cron jobs and log the outputs + c := cron.New() + c.Start() + return c +} + +//RefreshRSSCron refreshes all of the RSS feeds on an hourly basis +func RefreshRSSCron(c *cron.Cron, db *storm.DB) { + c.AddFunc("@hourly", func() { + RSSFeedStore := Storage.FetchRSSFeeds(db) + singleRSSTorrent := Storage.SingleRSSTorrent{} + newFeedStore := Storage.RSSFeedStore{ID: RSSFeedStore.ID} //creating a new feed store just using old one to parse for new torrents + fp := gofeed.NewParser() + for _, singleFeed := range RSSFeedStore.RSSFeeds { + feed, err := fp.ParseURL(singleFeed.URL) + if err != nil { + fmt.Println("Unable to parse URL", singleFeed.URL, err) + } + for _, RSSTorrent := range feed.Items { + singleRSSTorrent.Link = RSSTorrent.Link + singleRSSTorrent.Title = RSSTorrent.Title + singleRSSTorrent.PubDate = RSSTorrent.Published + singleFeed.Torrents = append(singleFeed.Torrents, singleRSSTorrent) + + } + newFeedStore.RSSFeeds = append(newFeedStore.RSSFeeds, singleFeed) + } + Storage.UpdateRSSFeeds(db, newFeedStore) //Calling this to fully update storage will all rss feeds + }) + +} + +//LogCronStatus prints out the status of the cron jobs to the log +func LogCronStatus(c *cron.Cron) { + +} diff --git a/engine/engine.go b/engine/engine.go index 72bf7e60..88175f4d 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -2,6 +2,7 @@ package engine //main file for all the calculations and data gathering needed fo import ( "fmt" + "io/ioutil" "os" "strconv" "strings" @@ -11,9 +12,58 @@ import ( "github.com/anacrolix/torrent/metainfo" "github.com/asdine/storm" Storage "github.com/deranjer/goTorrent/storage" + "github.com/mmcdole/gofeed" ) -func timeOutInfo(clientTorrent *torrent.Torrent, seconds time.Duration) (deleted bool) { //forcing a timeout of the torrent if it doesn't load from program restart +//RefreshSingleRSSFeed refreshing a single RSS feed to send to the client (so no updating database) mainly by updating the torrent list to display any changes +func RefreshSingleRSSFeed(db *storm.DB, RSSFeed Storage.SingleRSSFeed) Storage.SingleRSSFeed { //Todo.. duplicate as cron job... any way to merge these to reduce duplication? + singleRSSFeed := Storage.SingleRSSFeed{URL: RSSFeed.URL, Name: RSSFeed.Name} + singleRSSTorrent := Storage.SingleRSSTorrent{} + + fp := gofeed.NewParser() + feed, err := fp.ParseURL(RSSFeed.URL) + if err != nil { + fmt.Println("Unable to parse URL", RSSFeed.URL, err) + } + for _, RSSTorrent := range feed.Items { + singleRSSTorrent.Link = RSSTorrent.Link + singleRSSTorrent.Title = RSSTorrent.Title + singleRSSTorrent.PubDate = RSSTorrent.Published + singleRSSFeed.Torrents = append(singleRSSFeed.Torrents, singleRSSTorrent) + + } + return singleRSSFeed + +} + +//ForceRSSRefresh forces a refresh (in addition to the cron schedule) to add the new RSS feed +func ForceRSSRefresh(db *storm.DB, RSSFeedStore Storage.RSSFeedStore) { //Todo.. duplicate as cron job... any way to merge these to reduce duplication? + singleRSSTorrent := Storage.SingleRSSTorrent{} + newFeedStore := Storage.RSSFeedStore{ID: RSSFeedStore.ID} //creating a new feed store just using old one to parse for new torrents + fp := gofeed.NewParser() + fmt.Println("Length of RSS feeds (should be ONE)", len(RSSFeedStore.RSSFeeds)) + for _, singleFeed := range RSSFeedStore.RSSFeeds { + feed, err := fp.ParseURL(singleFeed.URL) + if err != nil { + fmt.Println("Unable to parse URL", singleFeed.URL, err) + } + fmt.Println("SingleFeed is: ", singleFeed) + for _, RSSTorrent := range feed.Items { + singleRSSTorrent.Link = RSSTorrent.Link + singleRSSTorrent.Title = RSSTorrent.Title + singleRSSTorrent.PubDate = RSSTorrent.Published + singleFeed.Torrents = append(singleFeed.Torrents, singleRSSTorrent) + + } + newFeedStore.RSSFeeds = append(newFeedStore.RSSFeeds, singleFeed) + } + fmt.Println("ABOUT TO WRITE TO DB", newFeedStore.RSSFeeds) + Storage.UpdateRSSFeeds(db, newFeedStore) //Calling this to fully update storage will all rss feeds +} + +//timeOutInfo forcing a timeout of the torrent if it doesn't load from program restart +func timeOutInfo(clientTorrent *torrent.Torrent, seconds time.Duration) (deleted bool) { + fmt.Println("Attempting to pull information for torrent... ", clientTorrent.Name()) timeout := make(chan bool, 1) //creating a timeout channel for our gotinfo go func() { time.Sleep(seconds * time.Second) @@ -21,11 +71,11 @@ func timeOutInfo(clientTorrent *torrent.Torrent, seconds time.Duration) (deleted }() select { case <-clientTorrent.GotInfo(): //attempting to retrieve info for torrent - fmt.Println("Recieved torrent info for...", clientTorrent.Name()) + //fmt.Println("Recieved torrent info for...", clientTorrent.Name()) clientTorrent.DownloadAll() return false case <-timeout: // getting info for torrent has timed out so purging the torrent - fmt.Println("Dropping Torrent") + fmt.Println("Dropping Torrent from information timeout...", clientTorrent.Name()) clientTorrent.Drop() return true } @@ -33,23 +83,37 @@ func timeOutInfo(clientTorrent *torrent.Torrent, seconds time.Duration) (deleted } //StartTorrent creates the storage.db entry and starts A NEW TORRENT and adds to the running torrent array -func StartTorrent(clientTorrent *torrent.Torrent, torrentLocalStorage Storage.TorrentLocal, torrentDbStorage *storm.DB, dataDir string, torrentFile string, torrentFileName string) { - +func StartTorrent(clientTorrent *torrent.Torrent, torrentLocalStorage Storage.TorrentLocal, torrentDbStorage *storm.DB, dataDir string, torrentType string, torrentFileName string) { timeOutInfo(clientTorrent, 45) //seeing if adding the torrrent times out (giving 45 seconds) var TempHash metainfo.Hash TempHash = clientTorrent.InfoHash() fmt.Println(clientTorrent.Info().Source) torrentLocalStorage.Hash = TempHash.String() // we will store the infohash to add it back later on client restart (if needed) + torrentLocalStorage.InfoBytes = clientTorrent.Metainfo().InfoBytes torrentLocalStorage.DateAdded = time.Now().Format("Jan _2 2006") torrentLocalStorage.StoragePath = dataDir //TODO check full path information for torrent storage torrentLocalStorage.TorrentName = clientTorrent.Name() - torrentLocalStorage.TorrentStatus = "downloading" //by default start all the torrents as downloading. - torrentLocalStorage.TorrentType = torrentFile //either "file" or "magnet" maybe more in the future - if torrentFile == "file" { + torrentLocalStorage.TorrentStatus = "Running" //by default start all the torrents as downloading. + torrentLocalStorage.TorrentType = torrentType //either "file" or "magnet" maybe more in the future + if torrentType == "file" { //if it is a file read the entire file into the database for us to spit out later torrentLocalStorage.TorrentFileName = torrentFileName - } else { - torrentLocalStorage.TorrentFileName = "" + torrentfile, err := ioutil.ReadFile(torrentFileName) + if err != nil { + fmt.Println("Unable to read the torrent file...") + } + torrentLocalStorage.TorrentFile = torrentfile //storing the entire file in to database } + torrentFiles := clientTorrent.Files() //storing all of the files in the database along with the priority + var TorrentFilePriorityArray = []Storage.TorrentFilePriority{} + for _, singleFile := range torrentFiles { //creating the database setup for the file array + var torrentFilePriority = Storage.TorrentFilePriority{} + torrentFilePriority.TorrentFilePath = singleFile.DisplayPath() + torrentFilePriority.TorrentFilePriority = "Normal" + + TorrentFilePriorityArray = append(TorrentFilePriorityArray, torrentFilePriority) + + } + torrentLocalStorage.TorrentFilePriority = TorrentFilePriorityArray fmt.Printf("%+v\n", torrentLocalStorage) Storage.AddTorrentLocalStorage(torrentDbStorage, torrentLocalStorage) //writing all of the data to the database clientTorrent.DownloadAll() //starting the download @@ -64,12 +128,27 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto if element.TorrentType == "file" { //if it is a file pull it from the uploaded torrent folder //fmt.Println("Filename", element.TorrentFileName) + tempFile, err := ioutil.TempFile("", "TorrentFileTemp") + if err != nil { + fmt.Println("Unable to create a temp file for adding file torrent in", err) + } + + defer os.Remove(tempFile.Name()) + + if _, err := tempFile.Write(element.TorrentFile); err != nil { + fmt.Println("Unable to write to the temp file...", err) + } + if err := tempFile.Close(); err != nil { + fmt.Println("Error closing Temp file", err) + } + singleTorrent, _ = tclient.AddTorrentFromFile(tempFile.Name()) if _, err := os.Stat(element.TorrentFileName); err == nil { //if we CAN find the torrent, add it //fmt.Println("Adding file name...", element.TorrentFileName) singleTorrent, _ = tclient.AddTorrentFromFile(element.TorrentFileName) + } else { //if we cant find the torrent delete it fmt.Println("File Error", err) - Storage.DelTorrentLocalStorage(db, element) + Storage.DelTorrentLocalStorage(db, element.Hash) continue } @@ -77,11 +156,17 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto elementMagnet := "magnet:?xt=urn:btih:" + element.Hash //For magnet links just need to prepend the magnet part to the hash to readd singleTorrent, _ = tclient.AddMagnet(elementMagnet) } + var TempHash metainfo.Hash + TempHash = singleTorrent.InfoHash() - timeOut := timeOutInfo(singleTorrent, 45) + singleTorrentStorageInfo := Storage.FetchTorrentFromStorage(db, TempHash.String()) + singleTorrent.SetInfoBytes(singleTorrentStorageInfo.InfoBytes) //setting the infobytes back into the torrent + + /* timeOut := timeOutInfo(singleTorrent, 45) //Shouldn't need this anymore as we pull in the infohash from the database if timeOut == true { // if we did timeout then drop the torrent from the boltdb database - Storage.DelTorrentLocalStorage(db, element) //purging torrent from the local database - } + Storage.DelTorrentLocalStorage(db, element.Hash) //purging torrent from the local database + continue + } */ fullClientDB := new(ClientDB) fullStruct := singleTorrent.Stats() @@ -97,10 +182,7 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto } activePeersString := strconv.Itoa(fullStruct.ActivePeers) //converting to strings totalPeersString := fmt.Sprintf("%v", fullStruct.TotalPeers) - var TempHash metainfo.Hash - TempHash = singleTorrent.InfoHash() - - singleTorrentStorageInfo := Storage.FetchTorrentFromStorage(db, TempHash.String()) //fetching all the info from the database + //fetching all the info from the database var torrentTypeTemp string torrentTypeTemp = singleTorrentStorageInfo.TorrentType //either "file" or "magnet" maybe more in the future if torrentTypeTemp == "file" { @@ -110,8 +192,8 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto } fullClientDB.StoragePath = singleTorrentStorageInfo.StoragePath //grabbed from database - totalSizeHumanized := HumanizeBytes(float32(singleTorrent.BytesCompleted())) //convert size to GB if needed - downloadedSizeHumanized := HumanizeBytes(float32(singleTorrent.Length())) + downloadedSizeHumanized := HumanizeBytes(float32(singleTorrent.BytesCompleted())) //convert size to GB if needed + totalSizeHumanized := HumanizeBytes(float32(singleTorrent.Length())) //grabbed from torrent client fullClientDB.DownloadedSize = downloadedSizeHumanized @@ -127,6 +209,7 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto fullClientDB.TorrentName = element.TorrentName fullClientDB.DateAdded = element.DateAdded fullClientDB.BytesCompleted = singleTorrent.BytesCompleted() + fullClientDB.NumberofFiles = len(singleTorrent.Files()) CalculateTorrentETA(singleTorrent, fullClientDB) //calculating the ETA for the torrent fullClientDB.TotalUploadedBytes = singleTorrentStorageInfo.UploadedBytes @@ -139,7 +222,11 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto tickUpdateStruct.Hash = fullClientDB.TorrentHashString //needed for index Storage.UpdateStorageTick(db, tickUpdateStruct) - CalculateTorrentStatus(singleTorrent, fullClientDB) //calculate the status of the torrent, ie downloading seeding etc + if singleTorrentStorageInfo.TorrentStatus != "Stopped" { //if the torrent is not stopped, try to discern the status of the torrent + CalculateTorrentStatus(singleTorrent, fullClientDB) //calculate the status of the torrent, ie downloading seeding etc + } else { + fullClientDB.Status = "Stopped" + } RunningTorrentArray = append(RunningTorrentArray, *fullClientDB) @@ -156,12 +243,20 @@ func CreateFileListArray(tclient *torrent.Client, selectedHash string) TorrentFi tempHash := singleTorrent.InfoHash().String() if tempHash == selectedHash { // if our selection hash equals our torrent hash torrentFilesRaw := singleTorrent.Files() + fmt.Println(torrentFilesRaw) for _, singleFile := range torrentFilesRaw { TorrentFileStruct.TorrentHashString = tempHash TorrentFileStruct.FileName = singleFile.DisplayPath() TorrentFileStruct.FilePath = singleFile.Path() - TorrentFileStruct.FilePercent = fmt.Sprintf("%.2f", float32(singleFile.Length())/float32(singleFile.Length())) //TODO figure out downloaded size of file - TorrentFileStruct.FilePriority = "Normal" //TODO, figure out how to store this per file in storage and also tie a priority to a file + PieceState := singleFile.State() + var downloadedBytes int64 + for _, piece := range PieceState { + if piece.Complete { + downloadedBytes = downloadedBytes + piece.Bytes //adding up the bytes in the completed pieces + } + } + TorrentFileStruct.FilePercent = fmt.Sprintf("%.2f", float32(downloadedBytes)/float32(singleFile.Length())) + TorrentFileStruct.FilePriority = "Normal" //TODO, figure out how to store this per file in storage and also tie a priority to a file TorrentFileStruct.FileSize = HumanizeBytes(float32(singleFile.Length())) TorrentFileListSelected.FileList = append(TorrentFileListSelected.FileList, TorrentFileStruct) } @@ -205,7 +300,6 @@ func CreateTorrentDetailJSON(tclient *torrent.Client, selectedHash string, torre if tempHash == selectedHash { fmt.Println("CreateTorrentDetail", localTorrentInfo) return TorrentDetailStruct - break //only looking for one result } } return TorrentDetailStruct diff --git a/engine/settings.go b/engine/settings.go index 6027b189..b43acdf0 100644 --- a/engine/settings.go +++ b/engine/settings.go @@ -1,32 +1,109 @@ -package engine //Settings.go contains all of the program settings +package engine import ( + "fmt" + + "golang.org/x/time/rate" + "github.com/anacrolix/dht" + "github.com/anacrolix/torrent" + "github.com/spf13/viper" ) -//FullCLientSettings struct is a struct that can be read into anacrolix/torrent to setup a torrent client type FullClientSettings struct { - Version int - torrent.Config + Version int + TorrentConfig torrent.Config TFileUploadFolder string } -//FullClientSettingsNew creates a new torrent client config TODO read from a TOML file -func FullClientSettingsNew() FullClientSettings { - //Config := fullClientSettings //generate a new struct - +func defaultConfig() FullClientSettings { var Config FullClientSettings - Config.Version = 1.0 - Config.DataDir = "downloads" //the full OR relative path of the default download directory for torrents + Config.TorrentConfig.DataDir = "downloads" //the full OR relative path of the default download directory for torrents Config.TFileUploadFolder = "uploadedTorrents" - Config.Seed = true + Config.TorrentConfig.Seed = true - Config.DHTConfig = dht.ServerConfig{ + Config.TorrentConfig.DHTConfig = dht.ServerConfig{ StartingNodes: dht.GlobalBootstrapAddrs, } + return Config +} + +func dhtServerSettings(dhtConfig dht.ServerConfig) dht.ServerConfig { + + viper.UnmarshalKey("DHTConfig", &dhtConfig) + fmt.Println("dhtconfig", dhtConfig) + + return dhtConfig +} + +func FullClientSettingsNew() FullClientSettings { + viper.SetConfigName("config") + viper.AddConfigPath("./") + err := viper.ReadInConfig() + if err != nil { + fmt.Println("Error reading in config, using defaults", err) + FullClientSettings := defaultConfig() + return FullClientSettings + } + + dataDir := viper.GetString("torrentClientConfig.DownloadDir") + listenAddr := viper.GetString("torrentClientConfig.ListenAddr") + disablePex := viper.GetBool("torrentClientConfig.DisablePEX") + noDHT := viper.GetBool("torrentClientConfig.NoDHT") + noUpload := viper.GetBool("torrentClientConfig.NoUpload") + seed := viper.GetBool("torrentClientConfig.Seed") + peerID := viper.GetString("torrentClientConfig.PeerID") + disableUTP := viper.GetBool("torrentClientConfig.DisableUTP") + disableTCP := viper.GetBool("torrentClientConfig.DisableTCP") + disableIPv6 := viper.GetBool("torrentClientConfig.DisableIPv6") + debug := viper.GetBool("torrentClientConfig.Debug") + + dhtServerConfig := dht.ServerConfig{ + StartingNodes: dht.GlobalBootstrapAddrs, + } + if viper.IsSet("DHTConfig") { + fmt.Println("Reading in custom DHT config") + dhtServerConfig = dhtServerSettings(dhtServerConfig) + } + + uploadRateLimiter := new(rate.Limiter) + viper.UnmarshalKey("UploadRateLimiter", &uploadRateLimiter) + + downloadRateLimiter := new(rate.Limiter) + viper.UnmarshalKey("DownloadRateLimiter", &downloadRateLimiter) + + rreferNoEncryption := viper.GetBool("EncryptionPolicy.PreferNoEncryption") + fmt.Println("Encryption", rreferNoEncryption) + + encryptionPolicy := torrent.EncryptionPolicy{ + DisableEncryption: viper.GetBool("EncryptionPolicy.DisableEncryption"), + ForceEncryption: viper.GetBool("EncryptionPolicy.ForceEncryption"), + PreferNoEncryption: viper.GetBool("EncryptionPolicy.PreferNoEncryption"), + } + + tConfig := torrent.Config{ + DataDir: dataDir, + ListenAddr: listenAddr, + DisablePEX: disablePex, + NoDHT: noDHT, + DHTConfig: dhtServerConfig, + NoUpload: noUpload, + Seed: seed, + //UploadRateLimiter: uploadRateLimiter, + //DownloadRateLimiter: downloadRateLimiter, + PeerID: peerID, + DisableUTP: disableUTP, + DisableTCP: disableTCP, + DisableIPv6: disableIPv6, + Debug: debug, + EncryptionPolicy: encryptionPolicy, + } + + Config := FullClientSettings{TorrentConfig: tConfig, TFileUploadFolder: "uploadedTorrents"} + return Config } diff --git a/main.go b/main.go index 72e862f8..86a6e38e 100644 --- a/main.go +++ b/main.go @@ -13,13 +13,22 @@ import ( "github.com/anacrolix/torrent" "github.com/asdine/storm" - //"github.com/boltdb/bolt" Engine "github.com/deranjer/goTorrent/engine" Storage "github.com/deranjer/goTorrent/storage" "github.com/gorilla/mux" "github.com/gorilla/websocket" + "github.com/mmcdole/gofeed" ) +//SingleRSSFeedMessage will most likley be deprecated as this is the only way I could get it working currently +type SingleRSSFeedMessage struct { //TODO had issues with getting this to work with Storage or Engine + MessageType string + URL string //the URL of the individual RSS feed + Name string + TotalTorrents int + Torrents []Storage.SingleRSSTorrent //name of the torrentss +} + var ( httpAddr = flag.String("addr", ":8000", "Http server address") baseTmpl string = "templates/base.tmpl" @@ -43,14 +52,12 @@ func updateClient(torrentstats []Engine.ClientDB, conn *websocket.Conn) { //get } func main() { - //setting up the torrent client Config := Engine.FullClientSettingsNew() //grabbing from settings.go os.Mkdir(Config.TFileUploadFolder, os.ModeDir) //creating a directory to store uploaded torrent files torrentLocalStorage := Storage.TorrentLocal{} //creating a new struct that stores all of our local storage info + fmt.Printf("%+v\n", Config) - //fmt.Printf("%+v\n", Config) - - tclient, err := torrent.NewClient(&Config.Config) //pulling out the torrent specific config to use + tclient, err := torrent.NewClient(&Config.TorrentConfig) //pulling out the torrent specific config to use if err != nil { log.Fatalf("error creating client: %s", err) } @@ -61,14 +68,21 @@ func main() { } defer db.Close() //defering closing the database until the program closes + cronEngine := Engine.InitializeCronEngine() //Starting the cron engine for tasks + Engine.RefreshRSSCron(cronEngine, db) // Refresing the RSS feeds on an hourly basis + var TorrentLocalArray = []*Storage.TorrentLocal{} //this is an array of ALL of the local storage torrents, they will be added back in via hash var RunningTorrentArray = []Engine.ClientDB{} //this stores ALL of the torrents that are running, used for client update pushes combines Local Storage and Running tclient info var PreviousTorrentArray = []Engine.ClientDB{} TorrentLocalArray = Storage.ReadInTorrents(db) //pulling in all the already added torrents - if TorrentLocalArray != nil { //the first creation of the running torrent array - RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db) //Updates the RunningTorrentArray with the current client data as well + if TorrentLocalArray != nil { //the first creation of the running torrent array //since we are adding all of them in we use a coroutine... just allows the web ui to load then it will load in the torrents + go func() { //TODO instead of running all torrent fetches in coroutine see if possible to run each single one in a routine so we don't wait for ALL of them to be verified + RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db) + }() + + //RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db) //Updates the RunningTorrentArray with the current client data as well } else { fmt.Println("Database is empty!") @@ -98,7 +112,7 @@ func main() { fmt.Println("Error adding Torrent from file: ", fileName.Name()) } else { fmt.Println("Adding Torrent via file", fileName) - Engine.StartTorrent(clientTorrent, torrentLocalStorage, db, Config.DataDir, "file", fileName.Name()) + Engine.StartTorrent(clientTorrent, torrentLocalStorage, db, Config.TorrentConfig.DataDir, "file", fileName.Name()) // the starttorrent can take a LONG time on startup } }) @@ -121,12 +135,14 @@ func main() { log.Println("Generic websocket error", err) return } + MessageLoop: //Tagging this so we can break out of it with any errors we encounter that are failing for { runningTorrents := tclient.Torrents() //getting running torrents here since multiple cases ask for the running torrents msg := Engine.Message{} readJSONError := conn.ReadJSON(&msg) if readJSONError != nil { fmt.Println("Unable to read JSON client message", err) + break MessageLoop } fmt.Println("MessageFull", msg) @@ -134,7 +150,7 @@ func main() { case "torrentListRequest": //fmt.Println("client Requested TorrentList Update") - TorrentLocalArray = Storage.ReadInTorrents(db) + TorrentLocalArray = Storage.ReadInTorrents(db) //Required to re-read th database since we write to the DB and this will pull the changes from it RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db) //Updates the RunningTorrentArray with the current client data as well PreviousTorrentArray = RunningTorrentArray var torrentlistArray = new(Engine.TorrentList) @@ -144,43 +160,96 @@ func main() { //fmt.Println("%+v\n", PreviousTorrentArray) //fmt.Printf("%+v\n", torrentlistArray) conn.WriteJSON(torrentlistArray) - break //updateClient(RunningTorrentArray, conn) // sending the client update information over the websocket case "torrentFileListRequest": //client requested a filelist update - fmt.Println("client Requested Filelist update") + //fmt.Println("client Requested Filelist update") FileListArray := Engine.CreateFileListArray(tclient, msg.Payload[0]) conn.WriteJSON(FileListArray) //writing the JSON to the client - break - case "torrentDetailedInfo": //TODO Figure out how to get single torrent info correctly fmt.Println("client requested detailed Torrent Info") torrentDetailArray := Engine.CreateTorrentDetailJSON(tclient, msg.Payload[0], db) conn.WriteJSON(torrentDetailArray) - break case "torrentPeerListRequest": fmt.Println("client requested peer list") torrentPeerList := Engine.CreatePeerListArray(tclient, msg.Payload[0]) //fmt.Printf("%+v\n", torrentPeerList) - //JSONTEST, _ := json.Marshal(torrentPeerList) - //fmt.Println(JSONTEST) - conn.WriteJSON(torrentPeerList) - break + + case "rssFeedRequest": + fmt.Println("client requested RSS feed") + + RSSList := Storage.FetchRSSFeeds(db) + RSSJSONFeed := Engine.RSSJSONList{MessageType: "rssListRequest", TotalRSSFeeds: len(RSSList.RSSFeeds)} + RSSsingleFeed := Engine.RSSFeedsNames{} + for _, singleFeed := range RSSList.RSSFeeds { + RSSsingleFeed.RSSName = singleFeed.Name + RSSsingleFeed.RSSFeedURL = singleFeed.URL + RSSJSONFeed.RSSFeeds = append(RSSJSONFeed.RSSFeeds, RSSsingleFeed) + } + + conn.WriteJSON(RSSJSONFeed) + + case "addRSSFeed": + fmt.Println("Adding RSSFeed", msg.Payload[0]) + newRSSFeed := msg.Payload[0] //there will only be one RSS feed (hopefully) + fullRSSFeeds := Storage.FetchRSSFeeds(db) + fmt.Println("Pulled full RSS feeds from database: ", fullRSSFeeds) + for _, singleFeed := range fullRSSFeeds.RSSFeeds { + if newRSSFeed == singleFeed.URL || newRSSFeed == "" { + fmt.Println("Empty URL or Duplicate RSS URL to one already in database! Rejecting submission") + break MessageLoop + } + } + fp := gofeed.NewParser() + feed, err := fp.ParseURL(newRSSFeed) + if err != nil { + fmt.Println("Unable to parse the URL as valid RSS.. cannot add RSS...", newRSSFeed) + break MessageLoop + } + fmt.Println("Have feed from URL...", feed.Title) + newRSSFeedFull := Storage.SingleRSSFeed{} + newRSSFeedFull.Name = feed.Title + newRSSFeedFull.URL = msg.Payload[0] + fullRSSFeeds.RSSFeeds = append(fullRSSFeeds.RSSFeeds, newRSSFeedFull) // add the new RSS feed to the stack + + Engine.ForceRSSRefresh(db, fullRSSFeeds) + //forcing an RSS refresh to fully populate all rss feeds TODO maybe just push the update of the new RSS feed and leave cron to update? But user would most likely expect and immediate update + + case "deleteRSSFeed": + fmt.Println("Deleting RSS Feed", msg.Payload[0]) + removingRSSFeed := msg.Payload[0] + Storage.DeleteRSSFeed(db, removingRSSFeed) + fullRSSFeeds := Storage.FetchRSSFeeds(db) + Engine.ForceRSSRefresh(db, fullRSSFeeds) + + case "rssTorrentsRequest": + fmt.Println("Requesting Torrent List for feed", msg.Payload[0]) + RSSFeedURL := msg.Payload[0] + fullRSSFeeds := Storage.FetchRSSFeeds(db) + for _, singleFeed := range fullRSSFeeds.RSSFeeds { + fmt.Println("URL", singleFeed.URL) + } + UpdatedRSSFeed := Engine.RefreshSingleRSSFeed(db, Storage.FetchSpecificRSSFeed(db, RSSFeedURL)) + TorrentRSSList := SingleRSSFeedMessage{MessageType: "rssTorrentList", URL: RSSFeedURL, Name: UpdatedRSSFeed.Name, TotalTorrents: len(UpdatedRSSFeed.Torrents), Torrents: UpdatedRSSFeed.Torrents} + conn.WriteJSON(TorrentRSSList) case "magnetLinkSubmit": //if we detect a magnet link we will be adding a magnet torrent - clientTorrent, err := tclient.AddMagnet(msg.Payload[0]) //reading the payload into the torrent client - if err != nil { - fmt.Println("Magnet Error", err) - } - fmt.Println(clientTorrent) - fmt.Printf("Adding Magnet Link") - Engine.StartTorrent(clientTorrent, torrentLocalStorage, db, Config.DataDir, "magnet", "") //starting the torrent and creating local DB entry - break + for _, magnetLink := range msg.Payload { + clientTorrent, err := tclient.AddMagnet(magnetLink) //reading the payload into the torrent client + if err != nil { + fmt.Println("Magnet Error could not add torrent! ", err) + break MessageLoop //break out of the loop entirely for this message since we hit an error + } + fmt.Println(clientTorrent) + fmt.Printf("Adding Magnet Link") + Engine.StartTorrent(clientTorrent, torrentLocalStorage, db, Config.TorrentConfig.DataDir, "magnet", "") //starting the torrent and creating local DB entry + + } case "stopTorrents": TorrentListCommands := msg.Payload @@ -189,11 +258,15 @@ func main() { for _, singleSelection := range TorrentListCommands { if singleTorrent.InfoHash().String() == singleSelection { fmt.Println("Matched for stopping torrents") - singleTorrent.SetMaxEstablishedConns(0) //Forcing the max amount of connections allowed to zero effectively stopping it + tempTorrentLocal := Storage.TorrentLocal{} + tempTorrentLocal.Hash = singleTorrent.InfoHash().String() //required since this is the ID that stormdb requires + tempTorrentLocal.TorrentStatus = "Stopped" + oldMax := singleTorrent.SetMaxEstablishedConns(0) //Forcing the max amount of connections allowed to zero effectively stopping it + fmt.Println("Setting max connections from ", oldMax, " to 0") + Storage.UpdateStorageTick(db, tempTorrentLocal) //Updating the torrent status } } } - break case "deleteTorrents": for _, singleTorrent := range runningTorrents { @@ -201,12 +274,10 @@ func main() { for _, singleSelection := range msg.Payload { if singleTorrent.InfoHash().String() == singleSelection { fmt.Println("Matched for deleting torrents") - singleTorrent.Drop() - //Storage.DelTorrentLocalStorage(db) + Storage.DelTorrentLocalStorage(db, singleTorrent.InfoHash().String()) } } } - break case "startTorrents": fmt.Println("Starting torrents", msg.Payload) @@ -215,11 +286,76 @@ func main() { for _, singleSelection := range msg.Payload { if singleTorrent.InfoHash().String() == singleSelection { fmt.Println("Matched for starting torrents", singleSelection) - singleTorrent.DownloadAll() + tempTorrentLocal := Storage.TorrentLocal{} + tempTorrentLocal.Hash = singleTorrent.InfoHash().String() //required since this is the ID that stormdb requires + tempTorrentLocal.TorrentStatus = "Running" //Setting the status back to running + oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String()) //Fetching the old max connections setting from the database + if oldTorrentInfo.MaxConnections == 0 { //if somehow the old max was set at zero change it to 80 + oldTorrentInfo.MaxConnections = 80 + Storage.UpdateStorageTick(db, oldTorrentInfo) + } + + oldMax := singleTorrent.SetMaxEstablishedConns(oldTorrentInfo.MaxConnections) //Forcing the max amount of connections allowed to zero effectively stopping it + fmt.Println("Setting max connections from 0 to:", oldMax) + + Storage.UpdateStorageTick(db, tempTorrentLocal) //Updating the torrent status + } + } + } + + case "setFilePriority": //TODO have one priority second message determines what priority + fmt.Println("Setting file priority", msg.Payload) + priorityRequested := msg.Payload[1] //storing the priority requested + infoHash := msg.Payload[0] //storing our infohash + fileList := append(msg.Payload[:0], msg.Payload[2:]...) //removing the filehash and priority from the array leaving just the filepath + fmt.Println("fileList after stripping out", fileList) + for _, singleTorrent := range runningTorrents { + if singleTorrent.InfoHash().String() == infoHash { + fmt.Println("Matched for changing file prio torrents", singleTorrent) + for _, file := range singleTorrent.Files() { + for _, sentFile := range fileList { + if file.Path() == sentFile { + if priorityRequested == "High" { + fileRead := singleTorrent.NewReader() + fileRead.Seek(file.Offset(), 0) + fileRead.SetReadahead(file.Length()) + fmt.Println("Setting priority for HIGH", file.DisplayPath()) + activeTorrentStruct := Storage.FetchTorrentFromStorage(db, infoHash) //fetching all the data from the db to update certain fields then write it all back + for i, specificFile := range activeTorrentStruct.TorrentFilePriority { //searching for that specific file + if specificFile.TorrentFilePath == file.DisplayPath() { + activeTorrentStruct.TorrentFilePriority[i].TorrentFilePriority = "High" //writing just that field to the current struct + } + } + Storage.UpdateStorageTick(db, activeTorrentStruct) //rewritting essentially that entire struct right back into the database + } + if priorityRequested == "Normal" { + file.Download() + fmt.Println("Setting priority for Normal", file.DisplayPath()) + activeTorrentStruct := Storage.FetchTorrentFromStorage(db, infoHash) //fetching all the data from the db to update certain fields then write it all back + for i, specificFile := range activeTorrentStruct.TorrentFilePriority { //searching for that specific file + if specificFile.TorrentFilePath == file.DisplayPath() { + activeTorrentStruct.TorrentFilePriority[i].TorrentFilePriority = "Normal" //writing just that field to the current struct + } + } + Storage.UpdateStorageTick(db, activeTorrentStruct) //rewritting essentially that entire struct right back into the database + } + if priorityRequested == "Cancel" { + file.Cancel() + fmt.Println("Canceling File", file.DisplayPath()) + activeTorrentStruct := Storage.FetchTorrentFromStorage(db, infoHash) //fetching all the data from the db to update certain fields then write it all back + for i, specificFile := range activeTorrentStruct.TorrentFilePriority { //searching for that specific file + if specificFile.TorrentFilePath == file.DisplayPath() { + activeTorrentStruct.TorrentFilePriority[i].TorrentFilePriority = "Canceled" //writing just that field to the current struct + } + } + Storage.UpdateStorageTick(db, activeTorrentStruct) //rewritting essentially that entire struct right back into the database + } + + } + } } } } - break default: //conn.Close() diff --git a/public/static/js/bundle.js b/public/static/js/bundle.js index 59ae24d0..224495a8 100644 --- a/public/static/js/bundle.js +++ b/public/static/js/bundle.js @@ -60,7 +60,7 @@ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 301); +/******/ return __webpack_require__(__webpack_require__.s = 306); /******/ }) /************************************************************************/ /******/ ([ @@ -89,11 +89,11 @@ if (process.env.NODE_ENV !== 'production') { // By explicitly using `prop-types` you are opting into new development behavior. // http://fb.me/prop-types-in-prod var throwOnDirectAccess = true; - module.exports = __webpack_require__(434)(isValidElement, throwOnDirectAccess); + module.exports = __webpack_require__(439)(isValidElement, throwOnDirectAccess); } else { // By explicitly using `prop-types` you are opting into new production behavior. // http://fb.me/prop-types-in-prod - module.exports = __webpack_require__(437)(); + module.exports = __webpack_require__(442)(); } /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2))) @@ -106,9 +106,9 @@ if (process.env.NODE_ENV !== 'production') { /* WEBPACK VAR INJECTION */(function(process) { if (process.env.NODE_ENV === 'production') { - module.exports = __webpack_require__(302); + module.exports = __webpack_require__(307); } else { - module.exports = __webpack_require__(303); + module.exports = __webpack_require__(308); } /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2))) @@ -312,7 +312,7 @@ process.umask = function() { return 0; }; exports.__esModule = true; -var _assign = __webpack_require__(195); +var _assign = __webpack_require__(196); var _assign2 = _interopRequireDefault(_assign); @@ -365,7 +365,7 @@ Object.defineProperty(exports, "__esModule", { }); exports.sheetsManager = undefined; -var _keys = __webpack_require__(27); +var _keys = __webpack_require__(30); var _keys2 = _interopRequireDefault(_keys); @@ -397,11 +397,11 @@ var _objectWithoutProperties2 = __webpack_require__(4); var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2); -var _map = __webpack_require__(442); +var _map = __webpack_require__(447); var _map2 = _interopRequireDefault(_map); -var _minSafeInteger = __webpack_require__(458); +var _minSafeInteger = __webpack_require__(463); var _minSafeInteger2 = _interopRequireDefault(_minSafeInteger); @@ -417,45 +417,45 @@ var _warning = __webpack_require__(15); var _warning2 = _interopRequireDefault(_warning); -var _hoistNonReactStatics = __webpack_require__(461); +var _hoistNonReactStatics = __webpack_require__(466); var _hoistNonReactStatics2 = _interopRequireDefault(_hoistNonReactStatics); -var _wrapDisplayName = __webpack_require__(54); +var _wrapDisplayName = __webpack_require__(55); var _wrapDisplayName2 = _interopRequireDefault(_wrapDisplayName); -var _getDisplayName = __webpack_require__(217); +var _getDisplayName = __webpack_require__(218); var _getDisplayName2 = _interopRequireDefault(_getDisplayName); -var _contextTypes = __webpack_require__(462); +var _contextTypes = __webpack_require__(467); var _contextTypes2 = _interopRequireDefault(_contextTypes); -var _jss = __webpack_require__(120); +var _jss = __webpack_require__(123); -var _jssPresetDefault = __webpack_require__(230); +var _jssPresetDefault = __webpack_require__(231); var _jssPresetDefault2 = _interopRequireDefault(_jssPresetDefault); -var _ns = __webpack_require__(231); +var _ns = __webpack_require__(232); var ns = _interopRequireWildcard(_ns); -var _createMuiTheme = __webpack_require__(126); +var _createMuiTheme = __webpack_require__(129); var _createMuiTheme2 = _interopRequireDefault(_createMuiTheme); -var _themeListener = __webpack_require__(119); +var _themeListener = __webpack_require__(122); var _themeListener2 = _interopRequireDefault(_themeListener); -var _createGenerateClassName = __webpack_require__(495); +var _createGenerateClassName = __webpack_require__(500); var _createGenerateClassName2 = _interopRequireDefault(_createGenerateClassName); -var _getStylesCreator = __webpack_require__(496); +var _getStylesCreator = __webpack_require__(501); var _getStylesCreator2 = _interopRequireDefault(_getStylesCreator); @@ -463,7 +463,7 @@ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -var babelPluginFlowReactPropTypes_proptype_HigherOrderComponent = __webpack_require__(78).babelPluginFlowReactPropTypes_proptype_HigherOrderComponent || __webpack_require__(0).any; // weak +var babelPluginFlowReactPropTypes_proptype_HigherOrderComponent = __webpack_require__(84).babelPluginFlowReactPropTypes_proptype_HigherOrderComponent || __webpack_require__(0).any; // weak // New JSS instance. var jss = (0, _jss.create)((0, _jssPresetDefault2.default)()); @@ -792,7 +792,7 @@ exports.default = withStyles; exports.__esModule = true; -var _defineProperty = __webpack_require__(109); +var _defineProperty = __webpack_require__(112); var _defineProperty2 = _interopRequireDefault(_defineProperty); @@ -872,7 +872,7 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! /* 8 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = { "default": __webpack_require__(410), __esModule: true }; +module.exports = { "default": __webpack_require__(415), __esModule: true }; /***/ }), /* 9 */ @@ -898,7 +898,7 @@ exports.default = function (instance, Constructor) { exports.__esModule = true; -var _defineProperty = __webpack_require__(109); +var _defineProperty = __webpack_require__(112); var _defineProperty2 = _interopRequireDefault(_defineProperty); @@ -931,7 +931,7 @@ exports.default = function () { exports.__esModule = true; -var _typeof2 = __webpack_require__(70); +var _typeof2 = __webpack_require__(76); var _typeof3 = _interopRequireDefault(_typeof2); @@ -954,15 +954,15 @@ exports.default = function (self, call) { exports.__esModule = true; -var _setPrototypeOf = __webpack_require__(427); +var _setPrototypeOf = __webpack_require__(432); var _setPrototypeOf2 = _interopRequireDefault(_setPrototypeOf); -var _create = __webpack_require__(431); +var _create = __webpack_require__(436); var _create2 = _interopRequireDefault(_create); -var _typeof2 = __webpack_require__(70); +var _typeof2 = __webpack_require__(76); var _typeof3 = _interopRequireDefault(_typeof2); @@ -1010,11 +1010,11 @@ if (process.env.NODE_ENV !== 'production') { // By explicitly using `prop-types` you are opting into new development behavior. // http://fb.me/prop-types-in-prod var throwOnDirectAccess = true; - module.exports = __webpack_require__(616)(isValidElement, throwOnDirectAccess); + module.exports = __webpack_require__(711)(isValidElement, throwOnDirectAccess); } else { // By explicitly using `prop-types` you are opting into new production behavior. // http://fb.me/prop-types-in-prod - module.exports = __webpack_require__(619)(); + module.exports = __webpack_require__(714)(); } /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2))) @@ -1058,9 +1058,9 @@ if (process.env.NODE_ENV === 'production') { // DCE check should happen before ReactDOM bundle executes so that // DevTools can report bad minification during injection. checkDCE(); - module.exports = __webpack_require__(306); + module.exports = __webpack_require__(311); } else { - module.exports = __webpack_require__(309); + module.exports = __webpack_require__(314); } /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2))) @@ -1144,7 +1144,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); -var _SvgIcon = __webpack_require__(513); +var _SvgIcon = __webpack_require__(518); Object.defineProperty(exports, 'default', { enumerable: true, @@ -1297,19 +1297,19 @@ var _temp = function () { exports.__esModule = true; -var _shouldUpdate = __webpack_require__(563); +var _shouldUpdate = __webpack_require__(567); var _shouldUpdate2 = _interopRequireDefault(_shouldUpdate); -var _shallowEqual = __webpack_require__(566); +var _shallowEqual = __webpack_require__(570); var _shallowEqual2 = _interopRequireDefault(_shallowEqual); -var _setDisplayName = __webpack_require__(254); +var _setDisplayName = __webpack_require__(255); var _setDisplayName2 = _interopRequireDefault(_setDisplayName); -var _wrapDisplayName = __webpack_require__(255); +var _wrapDisplayName = __webpack_require__(256); var _wrapDisplayName2 = _interopRequireDefault(_wrapDisplayName); @@ -1349,11 +1349,11 @@ Object.defineProperty(exports, "__esModule", { value: true }); -var _typeof2 = __webpack_require__(70); +var _typeof2 = __webpack_require__(76); var _typeof3 = _interopRequireDefault(_typeof2); -var _keys = __webpack_require__(27); +var _keys = __webpack_require__(30); var _keys2 = _interopRequireDefault(_keys); @@ -1460,10 +1460,10 @@ var babelPluginFlowReactPropTypes_proptype_TransitionClasses = { /* 23 */ /***/ (function(module, exports, __webpack_require__) { -var global = __webpack_require__(29); +var global = __webpack_require__(32); var core = __webpack_require__(20); -var ctx = __webpack_require__(43); -var hide = __webpack_require__(38); +var ctx = __webpack_require__(46); +var hide = __webpack_require__(42); var PROTOTYPE = 'prototype'; var $export = function (type, name, source) { @@ -1580,11 +1580,54 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! /***/ }), /* 25 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__components_Provider__ = __webpack_require__(356); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_connectAdvanced__ = __webpack_require__(192); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__connect_connect__ = __webpack_require__(364); +/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "Provider", function() { return __WEBPACK_IMPORTED_MODULE_0__components_Provider__["b"]; }); +/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "createProvider", function() { return __WEBPACK_IMPORTED_MODULE_0__components_Provider__["a"]; }); +/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "connectAdvanced", function() { return __WEBPACK_IMPORTED_MODULE_1__components_connectAdvanced__["a"]; }); +/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "connect", function() { return __WEBPACK_IMPORTED_MODULE_2__connect_connect__["a"]; }); + + + + + + +/***/ }), +/* 26 */ /***/ (function(module, exports, __webpack_require__) { -var store = __webpack_require__(106)('wks'); -var uid = __webpack_require__(68); -var Symbol = __webpack_require__(29).Symbol; +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var SORTLIST = exports.SORTLIST = 'SORTLIST'; +var CHANGE_SELECTION = exports.CHANGE_SELECTION = 'CHANGE_SELECTION'; +var CHANGE_FILTER = exports.CHANGE_FILTER = 'CHANGE_FILTER'; +var TORRENT_LIST = exports.TORRENT_LIST = 'TORRENT_LIST'; +var SET_BUTTON_STATE = exports.SET_BUTTON_STATE = 'BUTTON_STATE'; +var SELECTION_HASHES = exports.SELECTION_HASHES = 'SELECTION_HASHES'; +var SELECTED_TAB = exports.SELECTED_TAB = 'SELECTED_TAB'; +var PEER_LIST = exports.PEER_LIST = 'PEER_LIST'; +var FILE_LIST = exports.FILE_LIST = 'FILE_LIST'; +var CHANGE_FILE_SELECTION = exports.CHANGE_FILE_SELECTION = 'CHANGE_FILE_SELECTION'; +var NEW_RSS_FEED_STORE = exports.NEW_RSS_FEED_STORE = 'NEW_RSS_FEED_STORE'; +var RSS_MODAL_OPEN = exports.RSS_MODAL_OPEN = 'RSS_MODAL_OPEN'; +var RSS_TORRENT_LIST = exports.RSS_TORRENT_LIST = 'RSS_TORRENT_LIST'; + +/***/ }), +/* 27 */ +/***/ (function(module, exports, __webpack_require__) { + +var store = __webpack_require__(109)('wks'); +var uid = __webpack_require__(74); +var Symbol = __webpack_require__(32).Symbol; var USE_SYMBOL = typeof Symbol == 'function'; var $exports = module.exports = function (name) { @@ -1596,15 +1639,64 @@ $exports.store = store; /***/ }), -/* 26 */ +/* 28 */ /***/ (function(module, exports, __webpack_require__) { -var anObject = __webpack_require__(44); -var IE8_DOM_DEFINE = __webpack_require__(197); -var toPrimitive = __webpack_require__(100); +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _MuiThemeProvider = __webpack_require__(407); + +Object.defineProperty(exports, 'MuiThemeProvider', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_MuiThemeProvider).default; + } +}); + +var _withStyles = __webpack_require__(5); + +Object.defineProperty(exports, 'withStyles', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_withStyles).default; + } +}); + +var _withTheme = __webpack_require__(65); + +Object.defineProperty(exports, 'withTheme', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_withTheme).default; + } +}); + +var _createMuiTheme = __webpack_require__(129); + +Object.defineProperty(exports, 'createMuiTheme', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_createMuiTheme).default; + } +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/***/ }), +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { + +var anObject = __webpack_require__(47); +var IE8_DOM_DEFINE = __webpack_require__(198); +var toPrimitive = __webpack_require__(103); var dP = Object.defineProperty; -exports.f = __webpack_require__(30) ? Object.defineProperty : function defineProperty(O, P, Attributes) { +exports.f = __webpack_require__(33) ? Object.defineProperty : function defineProperty(O, P, Attributes) { anObject(O); P = toPrimitive(P, true); anObject(Attributes); @@ -1618,13 +1710,13 @@ exports.f = __webpack_require__(30) ? Object.defineProperty : function definePro /***/ }), -/* 27 */ +/* 30 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = { "default": __webpack_require__(440), __esModule: true }; +module.exports = { "default": __webpack_require__(445), __esModule: true }; /***/ }), -/* 28 */ +/* 31 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -1783,7 +1875,7 @@ var _temp = function () { ; /***/ }), -/* 29 */ +/* 32 */ /***/ (function(module, exports) { // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 @@ -1795,17 +1887,17 @@ if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef /***/ }), -/* 30 */ +/* 33 */ /***/ (function(module, exports, __webpack_require__) { // Thank's IE8 for his funny defineProperty -module.exports = !__webpack_require__(45)(function () { +module.exports = !__webpack_require__(48)(function () { return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7; }); /***/ }), -/* 31 */ +/* 34 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -1813,19 +1905,19 @@ module.exports = !__webpack_require__(45)(function () { exports.__esModule = true; -var _shouldUpdate = __webpack_require__(553); +var _shouldUpdate = __webpack_require__(558); var _shouldUpdate2 = _interopRequireDefault(_shouldUpdate); -var _shallowEqual = __webpack_require__(555); +var _shallowEqual = __webpack_require__(560); var _shallowEqual2 = _interopRequireDefault(_shallowEqual); -var _setDisplayName = __webpack_require__(253); +var _setDisplayName = __webpack_require__(254); var _setDisplayName2 = _interopRequireDefault(_setDisplayName); -var _wrapDisplayName = __webpack_require__(54); +var _wrapDisplayName = __webpack_require__(55); var _wrapDisplayName2 = _interopRequireDefault(_wrapDisplayName); @@ -1846,98 +1938,45 @@ var pure = function pure(BaseComponent) { exports.default = pure; /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2))) -/***/ }), -/* 32 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__components_Provider__ = __webpack_require__(351); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_connectAdvanced__ = __webpack_require__(191); -/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__connect_connect__ = __webpack_require__(359); -/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "Provider", function() { return __WEBPACK_IMPORTED_MODULE_0__components_Provider__["b"]; }); -/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "createProvider", function() { return __WEBPACK_IMPORTED_MODULE_0__components_Provider__["a"]; }); -/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "connectAdvanced", function() { return __WEBPACK_IMPORTED_MODULE_1__components_connectAdvanced__["a"]; }); -/* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "connect", function() { return __WEBPACK_IMPORTED_MODULE_2__connect_connect__["a"]; }); - - - - - - -/***/ }), -/* 33 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -var SORTLIST = exports.SORTLIST = 'SORTLIST'; -var CHANGE_SELECTION = exports.CHANGE_SELECTION = 'CHANGE_SELECTION'; -var CHANGE_FILTER = exports.CHANGE_FILTER = 'CHANGE_FILTER'; -var TORRENT_LIST = exports.TORRENT_LIST = 'TORRENT_LIST'; -var SET_BUTTON_STATE = exports.SET_BUTTON_STATE = 'BUTTON_STATE'; -var SELECTION_HASHES = exports.SELECTION_HASHES = 'SELECTION_HASHES'; -var SELECTED_TAB = exports.SELECTED_TAB = 'SELECTED_TAB'; -var PEER_LIST = exports.PEER_LIST = 'PEER_LIST'; -var FILE_LIST = exports.FILE_LIST = 'FILE_LIST'; - -/***/ }), -/* 34 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _MuiThemeProvider = __webpack_require__(402); - -Object.defineProperty(exports, 'MuiThemeProvider', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_MuiThemeProvider).default; - } -}); - -var _withStyles = __webpack_require__(5); - -Object.defineProperty(exports, 'withStyles', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_withStyles).default; - } -}); - -var _withTheme = __webpack_require__(62); - -Object.defineProperty(exports, 'withTheme', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_withTheme).default; - } -}); - -var _createMuiTheme = __webpack_require__(126); - -Object.defineProperty(exports, 'createMuiTheme', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_createMuiTheme).default; - } -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - /***/ }), /* 35 */ /***/ (function(module, exports, __webpack_require__) { +/* WEBPACK VAR INJECTION */(function(process) {/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +if (process.env.NODE_ENV !== 'production') { + var REACT_ELEMENT_TYPE = (typeof Symbol === 'function' && + Symbol.for && + Symbol.for('react.element')) || + 0xeac7; + + var isValidElement = function(object) { + return typeof object === 'object' && + object !== null && + object.$$typeof === REACT_ELEMENT_TYPE; + }; + + // By explicitly using `prop-types` you are opting into new development behavior. + // http://fb.me/prop-types-in-prod + var throwOnDirectAccess = true; + module.exports = __webpack_require__(337)(isValidElement, throwOnDirectAccess); +} else { + // By explicitly using `prop-types` you are opting into new production behavior. + // http://fb.me/prop-types-in-prod + module.exports = __webpack_require__(340)(); +} + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2))) + +/***/ }), +/* 36 */ +/***/ (function(module, exports, __webpack_require__) { + "use strict"; @@ -2027,7 +2066,51 @@ function createBreakpoints(breakpoints) { } /***/ }), -/* 36 */ +/* 37 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _Icon = __webpack_require__(502); + +Object.defineProperty(exports, 'default', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_Icon).default; + } +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/***/ }), +/* 38 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _IconButton = __webpack_require__(503); + +Object.defineProperty(exports, 'default', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_IconButton).default; + } +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/***/ }), +/* 39 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -2057,11 +2140,11 @@ var _inherits2 = __webpack_require__(12); var _inherits3 = _interopRequireDefault(_inherits2); -var _typeof2 = __webpack_require__(70); +var _typeof2 = __webpack_require__(76); var _typeof3 = _interopRequireDefault(_typeof2); -var _keys = __webpack_require__(27); +var _keys = __webpack_require__(30); var _keys2 = _interopRequireDefault(_keys); @@ -2069,7 +2152,7 @@ var _objectWithoutProperties2 = __webpack_require__(4); var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2); -var _assign = __webpack_require__(195); +var _assign = __webpack_require__(196); var _assign2 = _interopRequireDefault(_assign); @@ -2081,7 +2164,7 @@ var _propTypes = __webpack_require__(0); var _propTypes2 = _interopRequireDefault(_propTypes); -var _shallowEqual = __webpack_require__(241); +var _shallowEqual = __webpack_require__(242); var _shallowEqual2 = _interopRequireDefault(_shallowEqual); @@ -2089,7 +2172,7 @@ var _warning = __webpack_require__(15); var _warning2 = _interopRequireDefault(_warning); -var _supports = __webpack_require__(526); +var _supports = __webpack_require__(531); var supports = _interopRequireWildcard(_supports); @@ -2255,51 +2338,6 @@ EventListener.propTypes = process.env.NODE_ENV !== "production" ? { exports.default = EventListener; /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2))) -/***/ }), -/* 37 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _Typography = __webpack_require__(529); - -Object.defineProperty(exports, 'default', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_Typography).default; - } -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/***/ }), -/* 38 */ -/***/ (function(module, exports, __webpack_require__) { - -var dP = __webpack_require__(26); -var createDesc = __webpack_require__(59); -module.exports = __webpack_require__(30) ? function (object, key, value) { - return dP.f(object, key, createDesc(1, value)); -} : function (object, key, value) { - object[key] = value; - return object; -}; - - -/***/ }), -/* 39 */ -/***/ (function(module, exports) { - -module.exports = function (it) { - return typeof it === 'object' ? it !== null : typeof it === 'function'; -}; - - /***/ }), /* 40 */ /***/ (function(module, exports, __webpack_require__) { @@ -2311,12 +2349,12 @@ Object.defineProperty(exports, "__esModule", { value: true }); -var _ButtonBase = __webpack_require__(499); +var _Typography = __webpack_require__(534); Object.defineProperty(exports, 'default', { enumerable: true, get: function get() { - return _interopRequireDefault(_ButtonBase).default; + return _interopRequireDefault(_Typography).default; } }); @@ -2333,7 +2371,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); -var _Paper = __webpack_require__(545); +var _Paper = __webpack_require__(550); Object.defineProperty(exports, 'default', { enumerable: true, @@ -2348,43 +2386,75 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de /* 42 */ /***/ (function(module, exports, __webpack_require__) { -/* WEBPACK VAR INJECTION */(function(process) {/** - * Copyright (c) 2013-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ +var dP = __webpack_require__(29); +var createDesc = __webpack_require__(62); +module.exports = __webpack_require__(33) ? function (object, key, value) { + return dP.f(object, key, createDesc(1, value)); +} : function (object, key, value) { + object[key] = value; + return object; +}; -if (process.env.NODE_ENV !== 'production') { - var REACT_ELEMENT_TYPE = (typeof Symbol === 'function' && - Symbol.for && - Symbol.for('react.element')) || - 0xeac7; - - var isValidElement = function(object) { - return typeof object === 'object' && - object !== null && - object.$$typeof === REACT_ELEMENT_TYPE; - }; - - // By explicitly using `prop-types` you are opting into new development behavior. - // http://fb.me/prop-types-in-prod - var throwOnDirectAccess = true; - module.exports = __webpack_require__(332)(isValidElement, throwOnDirectAccess); -} else { - // By explicitly using `prop-types` you are opting into new production behavior. - // http://fb.me/prop-types-in-prod - module.exports = __webpack_require__(335)(); -} - -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2))) /***/ }), /* 43 */ +/***/ (function(module, exports) { + +module.exports = function (it) { + return typeof it === 'object' ? it !== null : typeof it === 'function'; +}; + + +/***/ }), +/* 44 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _ButtonBase = __webpack_require__(504); + +Object.defineProperty(exports, 'default', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_ButtonBase).default; + } +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/***/ }), +/* 45 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _Button = __webpack_require__(520); + +Object.defineProperty(exports, 'default', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_Button).default; + } +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/***/ }), +/* 46 */ /***/ (function(module, exports, __webpack_require__) { // optional / simple context binding -var aFunction = __webpack_require__(196); +var aFunction = __webpack_require__(197); module.exports = function (fn, that, length) { aFunction(fn); if (that === undefined) return fn; @@ -2406,10 +2476,10 @@ module.exports = function (fn, that, length) { /***/ }), -/* 44 */ +/* 47 */ /***/ (function(module, exports, __webpack_require__) { -var isObject = __webpack_require__(39); +var isObject = __webpack_require__(43); module.exports = function (it) { if (!isObject(it)) throw TypeError(it + ' is not an object!'); return it; @@ -2417,7 +2487,7 @@ module.exports = function (it) { /***/ }), -/* 45 */ +/* 48 */ /***/ (function(module, exports) { module.exports = function (exec) { @@ -2430,7 +2500,7 @@ module.exports = function (exec) { /***/ }), -/* 46 */ +/* 49 */ /***/ (function(module, exports) { var hasOwnProperty = {}.hasOwnProperty; @@ -2440,51 +2510,7 @@ module.exports = function (it, key) { /***/ }), -/* 47 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _Icon = __webpack_require__(497); - -Object.defineProperty(exports, 'default', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_Icon).default; - } -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/***/ }), -/* 48 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _IconButton = __webpack_require__(498); - -Object.defineProperty(exports, 'default', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_IconButton).default; - } -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/***/ }), -/* 49 */ +/* 50 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -2497,12 +2523,12 @@ exports.default = !!(typeof window !== 'undefined' && window.document && window. module.exports = exports['default']; /***/ }), -/* 50 */ +/* 51 */ /***/ (function(module, exports, __webpack_require__) { -var isObject = __webpack_require__(132), - now = __webpack_require__(518), - toNumber = __webpack_require__(520); +var isObject = __webpack_require__(134), + now = __webpack_require__(523), + toNumber = __webpack_require__(525); /** Error message constants. */ var FUNC_ERROR_TEXT = 'Expected a function'; @@ -2691,7 +2717,7 @@ module.exports = debounce; /***/ }), -/* 51 */ +/* 52 */ /***/ (function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(process) {/** @@ -2716,35 +2742,24 @@ if (process.env.NODE_ENV !== 'production') { // By explicitly using `prop-types` you are opting into new development behavior. // http://fb.me/prop-types-in-prod var throwOnDirectAccess = true; - module.exports = __webpack_require__(322)(isValidElement, throwOnDirectAccess); + module.exports = __webpack_require__(327)(isValidElement, throwOnDirectAccess); } else { // By explicitly using `prop-types` you are opting into new production behavior. // http://fb.me/prop-types-in-prod - module.exports = __webpack_require__(325)(); + module.exports = __webpack_require__(330)(); } /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2))) -/***/ }), -/* 52 */ -/***/ (function(module, exports, __webpack_require__) { - -// to indexed object, toObject with fallback for non-array-like ES3 strings -var IObject = __webpack_require__(101); -var defined = __webpack_require__(103); -module.exports = function (it) { - return IObject(defined(it)); -}; - - /***/ }), /* 53 */ /***/ (function(module, exports, __webpack_require__) { -// 7.1.13 ToObject(argument) -var defined = __webpack_require__(103); +// to indexed object, toObject with fallback for non-array-like ES3 strings +var IObject = __webpack_require__(104); +var defined = __webpack_require__(106); module.exports = function (it) { - return Object(defined(it)); + return IObject(defined(it)); }; @@ -2752,12 +2767,23 @@ module.exports = function (it) { /* 54 */ /***/ (function(module, exports, __webpack_require__) { +// 7.1.13 ToObject(argument) +var defined = __webpack_require__(106); +module.exports = function (it) { + return Object(defined(it)); +}; + + +/***/ }), +/* 55 */ +/***/ (function(module, exports, __webpack_require__) { + "use strict"; exports.__esModule = true; -var _getDisplayName = __webpack_require__(217); +var _getDisplayName = __webpack_require__(218); var _getDisplayName2 = _interopRequireDefault(_getDisplayName); @@ -2770,7 +2796,7 @@ var wrapDisplayName = function wrapDisplayName(BaseComponent, hocName) { exports.default = wrapDisplayName; /***/ }), -/* 55 */ +/* 56 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -2781,7 +2807,7 @@ Object.defineProperty(exports, "__esModule", { }); exports.isNumber = exports.isString = exports.formatMs = exports.duration = exports.easing = undefined; -var _keys = __webpack_require__(27); +var _keys = __webpack_require__(30); var _keys2 = _interopRequireDefault(_keys); @@ -2789,7 +2815,7 @@ var _objectWithoutProperties2 = __webpack_require__(4); var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2); -var _isNan = __webpack_require__(235); +var _isNan = __webpack_require__(236); var _isNan2 = _interopRequireDefault(_isNan); @@ -2889,7 +2915,7 @@ exports.default = { /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2))) /***/ }), -/* 56 */ +/* 57 */ /***/ (function(module, exports) { // Source: http://jsfiddle.net/vWx8V/ @@ -3041,7 +3067,7 @@ for (var alias in aliases) { /***/ }), -/* 57 */ +/* 58 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -3075,560 +3101,8 @@ function isMuiComponent(element, muiNames) { return muiNames.indexOf(element.muiName) !== -1; } -/***/ }), -/* 58 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/* WEBPACK VAR INJECTION */(function(process) { - -exports.__esModule = true; - -var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; - -exports.bottom = bottom; -exports.cloneLayout = cloneLayout; -exports.cloneLayoutItem = cloneLayoutItem; -exports.childrenEqual = childrenEqual; -exports.collides = collides; -exports.compact = compact; -exports.compactItem = compactItem; -exports.correctBounds = correctBounds; -exports.getLayoutItem = getLayoutItem; -exports.getFirstCollision = getFirstCollision; -exports.getAllCollisions = getAllCollisions; -exports.getStatics = getStatics; -exports.moveElement = moveElement; -exports.moveElementAwayFromCollision = moveElementAwayFromCollision; -exports.perc = perc; -exports.setTransform = setTransform; -exports.setTopLeft = setTopLeft; -exports.sortLayoutItems = sortLayoutItems; -exports.sortLayoutItemsByRowCol = sortLayoutItemsByRowCol; -exports.sortLayoutItemsByColRow = sortLayoutItemsByColRow; -exports.synchronizeLayoutWithChildren = synchronizeLayoutWithChildren; -exports.validateLayout = validateLayout; -exports.autoBindHandlers = autoBindHandlers; - -var _lodash = __webpack_require__(90); - -var _lodash2 = _interopRequireDefault(_lodash); - -var _react = __webpack_require__(1); - -var _react2 = _interopRequireDefault(_react); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -// All callbacks are of the signature (layout, oldItem, newItem, placeholder, e). -var isProduction = process.env.NODE_ENV === 'production'; - -/** - * Return the bottom coordinate of the layout. - * - * @param {Array} layout Layout array. - * @return {Number} Bottom coordinate. - */ -function bottom(layout) { - var max = 0, - bottomY = void 0; - for (var _i = 0, len = layout.length; _i < len; _i++) { - bottomY = layout[_i].y + layout[_i].h; - if (bottomY > max) max = bottomY; - } - return max; -} - -function cloneLayout(layout) { - var newLayout = Array(layout.length); - for (var _i2 = 0, len = layout.length; _i2 < len; _i2++) { - newLayout[_i2] = cloneLayoutItem(layout[_i2]); - } - return newLayout; -} - -// Fast path to cloning, since this is monomorphic -function cloneLayoutItem(layoutItem) { - return { - w: layoutItem.w, h: layoutItem.h, x: layoutItem.x, y: layoutItem.y, i: layoutItem.i, - minW: layoutItem.minW, maxW: layoutItem.maxW, minH: layoutItem.minH, maxH: layoutItem.maxH, - moved: Boolean(layoutItem.moved), static: Boolean(layoutItem.static), - // These can be null - isDraggable: layoutItem.isDraggable, isResizable: layoutItem.isResizable - }; -} - -/** - * Comparing React `children` is a bit difficult. This is a good way to compare them. - * This will catch differences in keys, order, and length. - */ -function childrenEqual(a, b) { - // $FlowIgnore: Appears to think map calls back w/array - return (0, _lodash2.default)(_react2.default.Children.map(a, function (c) { - return c.key; - }), _react2.default.Children.map(b, function (c) { - return c.key; - })); -} - -/** - * Given two layoutitems, check if they collide. - */ -function collides(l1, l2) { - if (l1 === l2) return false; // same element - if (l1.x + l1.w <= l2.x) return false; // l1 is left of l2 - if (l1.x >= l2.x + l2.w) return false; // l1 is right of l2 - if (l1.y + l1.h <= l2.y) return false; // l1 is above l2 - if (l1.y >= l2.y + l2.h) return false; // l1 is below l2 - return true; // boxes overlap -} - -/** - * Given a layout, compact it. This involves going down each y coordinate and removing gaps - * between items. - * - * @param {Array} layout Layout. - * @param {Boolean} verticalCompact Whether or not to compact the layout - * vertically. - * @return {Array} Compacted Layout. - */ -function compact(layout, compactType, cols) { - // Statics go in the compareWith array right away so items flow around them. - var compareWith = getStatics(layout); - // We go through the items by row and column. - var sorted = sortLayoutItems(layout, compactType); - // Holding for new items. - var out = Array(layout.length); - - for (var _i3 = 0, len = sorted.length; _i3 < len; _i3++) { - var l = cloneLayoutItem(sorted[_i3]); - - // Don't move static elements - if (!l.static) { - l = compactItem(compareWith, l, compactType, cols); - - // Add to comparison array. We only collide with items before this one. - // Statics are already in this array. - compareWith.push(l); - } - - // Add to output array to make sure they still come out in the right order. - out[layout.indexOf(sorted[_i3])] = l; - - // Clear moved flag, if it exists. - l.moved = false; - } - - return out; -} - -/** - * Compact an item in the layout. - */ -function compactItem(compareWith, l, compactType, cols) { - var compactV = compactType === 'vertical'; - var compactH = compactType === 'horizontal'; - if (compactV) { - // Bottom 'y' possible is the bottom of the layout. - // This allows you to do nice stuff like specify {y: Infinity} - // This is here because the layout must be sorted in order to get the correct bottom `y`. - l.y = Math.min(bottom(compareWith), l.y); - // Move the element up as far as it can go without colliding. - while (l.y > 0 && !getFirstCollision(compareWith, l)) { - l.y--; - } - } else if (compactH) { - l.y = Math.min(bottom(compareWith), l.y); - // Move the element left as far as it can go without colliding. - while (l.x > 0 && !getFirstCollision(compareWith, l)) { - l.x--; - } - } - - // Move it down, and keep moving it down if it's colliding. - var collides = void 0; - while (collides = getFirstCollision(compareWith, l)) { - if (compactH) { - l.x = collides.x + collides.w; - } else { - l.y = collides.y + collides.h; - } - // Since we can't grow without bounds horizontally, if we've overflown, let's move it down and try again. - if (compactH && l.x + l.w > cols) { - l.x = cols - l.w; - l.y++; - } - } - return l; -} - -/** - * Given a layout, make sure all elements fit within its bounds. - * - * @param {Array} layout Layout array. - * @param {Number} bounds Number of columns. - */ -function correctBounds(layout, bounds) { - var collidesWith = getStatics(layout); - for (var _i4 = 0, len = layout.length; _i4 < len; _i4++) { - var l = layout[_i4]; - // Overflows right - if (l.x + l.w > bounds.cols) l.x = bounds.cols - l.w; - // Overflows left - if (l.x < 0) { - l.x = 0; - l.w = bounds.cols; - } - if (!l.static) collidesWith.push(l);else { - // If this is static and collides with other statics, we must move it down. - // We have to do something nicer than just letting them overlap. - while (getFirstCollision(collidesWith, l)) { - l.y++; - } - } - } - return layout; -} - -/** - * Get a layout item by ID. Used so we can override later on if necessary. - * - * @param {Array} layout Layout array. - * @param {String} id ID - * @return {LayoutItem} Item at ID. - */ -function getLayoutItem(layout, id) { - for (var _i5 = 0, len = layout.length; _i5 < len; _i5++) { - if (layout[_i5].i === id) return layout[_i5]; - } -} - -/** - * Returns the first item this layout collides with. - * It doesn't appear to matter which order we approach this from, although - * perhaps that is the wrong thing to do. - * - * @param {Object} layoutItem Layout item. - * @return {Object|undefined} A colliding layout item, or undefined. - */ -function getFirstCollision(layout, layoutItem) { - for (var _i6 = 0, len = layout.length; _i6 < len; _i6++) { - if (collides(layout[_i6], layoutItem)) return layout[_i6]; - } -} - -function getAllCollisions(layout, layoutItem) { - return layout.filter(function (l) { - return collides(l, layoutItem); - }); -} - -/** - * Get all static elements. - * @param {Array} layout Array of layout objects. - * @return {Array} Array of static layout items.. - */ -function getStatics(layout) { - return layout.filter(function (l) { - return l.static; - }); -} - -/** - * Move an element. Responsible for doing cascading movements of other elements. - * - * @param {Array} layout Full layout to modify. - * @param {LayoutItem} l element to move. - * @param {Number} [x] X position in grid units. - * @param {Number} [y] Y position in grid units. - * @param {Boolean} [isUserAction] If true, designates that the item we're moving is - * being dragged/resized by the user. - */ -function moveElement(layout, l, x, y, isUserAction, preventCollision, compactType, cols) { - if (l.static) return layout; - - // Short-circuit if nothing to do. - if (l.y === y && l.x === x) return layout; - - var oldX = l.x; - var oldY = l.y; - - var movingUp = y && l.y > y; - // This is quite a bit faster than extending the object - if (typeof x === 'number') l.x = x; - if (typeof y === 'number') l.y = y; - l.moved = true; - - // If this collides with anything, move it. - // When doing this comparison, we have to sort the items we compare with - // to ensure, in the case of multiple collisions, that we're getting the - // nearest collision. - var sorted = sortLayoutItems(layout, compactType); - if (movingUp) sorted = sorted.reverse(); - var collisions = getAllCollisions(sorted, l); - - // There was a collision; abort - if (preventCollision && collisions.length) { - l.x = oldX; - l.y = oldY; - l.moved = false; - return layout; - } - - // Move each item that collides away from this element. - for (var _i7 = 0, len = collisions.length; _i7 < len; _i7++) { - var collision = collisions[_i7]; - // console.log('resolving collision between', l.i, 'at', l.y, 'and', collision.i, 'at', collision.y); - - // Short circuit so we can't infinite loop - if (collision.moved) continue; - - // This makes it feel a bit more precise by waiting to swap for just a bit when moving up. - if (l.y > collision.y && l.y - collision.y > collision.h / 4) continue; - if (l.x > collision.x && l.x - collision.x > collision.w / 4) continue; - - // Don't move static items - we have to move *this* element away - if (collision.static) { - layout = moveElementAwayFromCollision(layout, collision, l, isUserAction, compactType, cols); - } else { - layout = moveElementAwayFromCollision(layout, l, collision, isUserAction, compactType, cols); - } - } - - return layout; -} - -/** - * This is where the magic needs to happen - given a collision, move an element away from the collision. - * We attempt to move it up if there's room, otherwise it goes below. - * - * @param {Array} layout Full layout to modify. - * @param {LayoutItem} collidesWith Layout item we're colliding with. - * @param {LayoutItem} itemToMove Layout item we're moving. - * @param {Boolean} [isUserAction] If true, designates that the item we're moving is being dragged/resized - * by the user. - */ -function moveElementAwayFromCollision(layout, collidesWith, itemToMove, isUserAction, compactType, cols) { - - var compactH = compactType === 'horizontal'; - var compactV = compactType === 'vertical'; - var preventCollision = false; // we're already colliding - // If there is enough space above the collision to put this element, move it there. - // We only do this on the main collision as this can get funky in cascades and cause - // unwanted swapping behavior. - if (isUserAction) { - // Make a mock item so we don't modify the item here, only modify in moveElement. - var fakeItem = { - x: compactH ? Math.max(collidesWith.x - itemToMove.w, 0) : itemToMove.x, - y: !compactH ? Math.max(collidesWith.y - itemToMove.h, 0) : itemToMove.y, - w: itemToMove.w, - h: itemToMove.h, - i: '-1' - }; - - if (!getFirstCollision(layout, fakeItem)) { - return moveElement(layout, itemToMove, compactH ? fakeItem.x : undefined, compactV ? fakeItem.y + 1 : undefined, isUserAction, preventCollision, compactType, cols); - } - } - - // Previously this was optimized to move below the collision directly, but this can cause problems - // with cascading moves, as an item may actually leapflog a collision and cause a reversal in order. - return moveElement(layout, itemToMove, compactH ? itemToMove.x + 1 : undefined, compactV ? itemToMove.y + 1 : undefined, isUserAction, preventCollision, compactType, cols); -} - -/** - * Helper to convert a number to a percentage string. - * - * @param {Number} num Any number - * @return {String} That number as a percentage. - */ -function perc(num) { - return num * 100 + '%'; -} - -function setTransform(_ref) { - var top = _ref.top, - left = _ref.left, - width = _ref.width, - height = _ref.height; - - // Replace unitless items with px - var translate = 'translate(' + left + 'px,' + top + 'px)'; - return { - transform: translate, - WebkitTransform: translate, - MozTransform: translate, - msTransform: translate, - OTransform: translate, - width: width + 'px', - height: height + 'px', - position: 'absolute' - }; -} - -function setTopLeft(_ref2) { - var top = _ref2.top, - left = _ref2.left, - width = _ref2.width, - height = _ref2.height; - - return { - top: top + 'px', - left: left + 'px', - width: width + 'px', - height: height + 'px', - position: 'absolute' - }; -} - -/** - * Get layout items sorted from top left to right and down. - * - * @return {Array} Array of layout objects. - * @return {Array} Layout, sorted static items first. - */ -function sortLayoutItems(layout, compactType) { - if (compactType === 'horizontal') return sortLayoutItemsByColRow(layout);else return sortLayoutItemsByRowCol(layout); -} - -function sortLayoutItemsByRowCol(layout) { - return [].concat(layout).sort(function (a, b) { - if (a.y > b.y || a.y === b.y && a.x > b.x) { - return 1; - } else if (a.y === b.y && a.x === b.x) { - // Without this, we can get different sort results in IE vs. Chrome/FF - return 0; - } - return -1; - }); -} - -function sortLayoutItemsByColRow(layout) { - return [].concat(layout).sort(function (a, b) { - if (a.x > b.x || a.x === b.x && a.y > b.y) { - return 1; - } - return -1; - }); -} - -/** - * Generate a layout using the initialLayout and children as a template. - * Missing entries will be added, extraneous ones will be truncated. - * - * @param {Array} initialLayout Layout passed in through props. - * @param {String} breakpoint Current responsive breakpoint. - * @param {?String} compact Compaction option. - * @return {Array} Working layout. - */ -function synchronizeLayoutWithChildren(initialLayout, children, cols, compactType) { - initialLayout = initialLayout || []; - - // Generate one layout item per child. - var layout = []; - _react2.default.Children.forEach(children, function (child, i) { - // Don't overwrite if it already exists. - var exists = getLayoutItem(initialLayout, String(child.key)); - if (exists) { - layout[i] = cloneLayoutItem(exists); - } else { - if (!isProduction && child.props._grid) { - console.warn('`_grid` properties on children have been deprecated as of React 15.2. ' + // eslint-disable-line - 'Please use `data-grid` or add your properties directly to the `layout`.'); - } - var g = child.props['data-grid'] || child.props._grid; - - // Hey, this item has a data-grid property, use it. - if (g) { - if (!isProduction) { - validateLayout([g], 'ReactGridLayout.children'); - } - layout[i] = cloneLayoutItem(_extends({}, g, { i: child.key })); - } else { - // Nothing provided: ensure this is added to the bottom - layout[i] = cloneLayoutItem({ w: 1, h: 1, x: 0, y: bottom(layout), i: String(child.key) }); - } - } - }); - - // Correct the layout. - layout = correctBounds(layout, { cols: cols }); - layout = compact(layout, compactType, cols); - - return layout; -} - -/** - * Validate a layout. Throws errors. - * - * @param {Array} layout Array of layout items. - * @param {String} [contextName] Context name for errors. - * @throw {Error} Validation error. - */ -function validateLayout(layout, contextName) { - contextName = contextName || "Layout"; - var subProps = ['x', 'y', 'w', 'h']; - if (!Array.isArray(layout)) throw new Error(contextName + " must be an array!"); - for (var _i8 = 0, len = layout.length; _i8 < len; _i8++) { - var item = layout[_i8]; - for (var j = 0; j < subProps.length; j++) { - if (typeof item[subProps[j]] !== 'number') { - throw new Error('ReactGridLayout: ' + contextName + '[' + _i8 + '].' + subProps[j] + ' must be a number!'); - } - } - if (item.i && typeof item.i !== 'string') { - throw new Error('ReactGridLayout: ' + contextName + '[' + _i8 + '].i must be a string!'); - } - if (item.static !== undefined && typeof item.static !== 'boolean') { - throw new Error('ReactGridLayout: ' + contextName + '[' + _i8 + '].static must be a boolean!'); - } - } -} - -// Flow can't really figure this out, so we just use Object -function autoBindHandlers(el, fns) { - fns.forEach(function (key) { - return el[key] = el[key].bind(el); - }); -} -/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2))) - /***/ }), /* 59 */ -/***/ (function(module, exports) { - -module.exports = function (bitmap, value) { - return { - enumerable: !(bitmap & 1), - configurable: !(bitmap & 2), - writable: !(bitmap & 4), - value: value - }; -}; - - -/***/ }), -/* 60 */ -/***/ (function(module, exports, __webpack_require__) { - -// 19.1.2.14 / 15.2.3.14 Object.keys(O) -var $keys = __webpack_require__(199); -var enumBugKeys = __webpack_require__(107); - -module.exports = Object.keys || function keys(O) { - return $keys(O, enumBugKeys); -}; - - -/***/ }), -/* 61 */ -/***/ (function(module, exports) { - -module.exports = {}; - - -/***/ }), -/* 62 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -3638,2058 +3112,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); -var _extends2 = __webpack_require__(3); - -var _extends3 = _interopRequireDefault(_extends2); - -var _getPrototypeOf = __webpack_require__(8); - -var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); - -var _classCallCheck2 = __webpack_require__(9); - -var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); - -var _createClass2 = __webpack_require__(10); - -var _createClass3 = _interopRequireDefault(_createClass2); - -var _possibleConstructorReturn2 = __webpack_require__(11); - -var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); - -var _inherits2 = __webpack_require__(12); - -var _inherits3 = _interopRequireDefault(_inherits2); - -var _react = __webpack_require__(1); - -var _react2 = _interopRequireDefault(_react); - -var _wrapDisplayName = __webpack_require__(54); - -var _wrapDisplayName2 = _interopRequireDefault(_wrapDisplayName); - -var _createMuiTheme = __webpack_require__(126); - -var _createMuiTheme2 = _interopRequireDefault(_createMuiTheme); - -var _themeListener = __webpack_require__(119); - -var _themeListener2 = _interopRequireDefault(_themeListener); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -// weak - -// flow sanity check (DO NOT DELETE) https://flow.org/try/#0JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wG4AoUSWOGATzCTgG84BhXSAOyS5gBUGTAL5xsuAkXQwy5OQHp5cALSq16jZuVwdccorgB3YDAAW-U0hBMAEgHk25JAA9qWAK5cMwCFyMnzS2sAHgAFHDAAZwAuFmEAPgAKcl12Tl9eGFiOcAy+QUZg1jMrJFi7ACMAKyQMOFEAMjhwiCj4gBpyAEps9J58oTCIyPiWOR00ABsUSMi4AHUAi1K4FxheABM55GkAOhzuTKHWyPaWWiCyuEqauoSx1KIuDaQoRK6H1LgiGHcoP2CBzy8GYuzBZmAkV2YGGohK1gAvMwIVDIjAUOtdvCkKJ5PEKKlhAT6ilvkhfv8FktLuRhAolFpGUy1PolMYzMtrHAAKqRFAAcyQ5CmMzmAEFVs51s9tsQYPs+kdipdytVavBGiwULEuO4QBVXmcKjq9QaoPdmHS0L40XBOUgNkD+vAEf4OZdEmKuhQDPMmBtfPh4DwHbQIHAwKK4MA-AADbGx1YAN14Fwg7n5pjgsYAsnQnZlE0QAI7uYBEOYmXbkYL2x2KvhwFBIgCMogqSIATLj4vSVMyB6lWW7TIsNmY4PZHC43LQhHAAEJSADWkBjLoIzki+DgAB8CJEQDv9-gQBtjwRJvyL-hnJNZOR6IwqePTC0onBXcxSTGTMAUJMY5mAA-LES6oKuEDrp0OjGK+oGLiua58J0dJOK40AeF4MA+H47KjsAr7vJ8mCeN4virFwpgoF4SDHFEsRAW+wxJKSqQFnwvS5M6BR0cwcFmGBSFQShcBgrs76RAkMFwD0aTcZkvH0SMYxsXAIqzFSZhMZK0pbIgcoKgpfDKaM35fGSzyvMR5kWepNogr+OEAUxZwCaYoiuii0LDGpjzkn8AIcSC4neTCJyiO5SL4Ie+A9sShIJSSak-IFWkEa+xJEuMZIUn4vDUbRFBoQYA5leow7uHygrCtMmkLrpmyynswVFO5QkQchMBnNqcC6vqhrGn1pqvBapJPC8bwfLZEwOSw7meRckI+ScKUBZSwQbMASZwHipJ0lac1MQ6wWfiOTHvIkC7esOfpwAGXBBn1SChjA4aRppMbZu5iZICmfhmOmmbZnmwVFkgpblkglbyjWx31sZ8DNswbZwB2zDdrt+JAA -var babelPluginFlowReactPropTypes_proptype_HigherOrderComponent = __webpack_require__(78).babelPluginFlowReactPropTypes_proptype_HigherOrderComponent || __webpack_require__(0).any; - -var defaultTheme = void 0; - -function getDefaultTheme() { - if (defaultTheme) { - return defaultTheme; - } - - defaultTheme = (0, _createMuiTheme2.default)(); - return defaultTheme; -} - -var babelPluginFlowReactPropTypes_proptype_InjectedProps = { - theme: __webpack_require__(0).object.isRequired -}; - - -// Provide the theme object as a property to the input component. -var withTheme = function withTheme() { - return function (Component) { - var WithTheme = function (_React$Component) { - (0, _inherits3.default)(WithTheme, _React$Component); - - function WithTheme(props, context) { - (0, _classCallCheck3.default)(this, WithTheme); - - var _this = (0, _possibleConstructorReturn3.default)(this, (WithTheme.__proto__ || (0, _getPrototypeOf2.default)(WithTheme)).call(this, props, context)); - - _this.state = {}; - _this.unsubscribeId = null; - - _this.state = { - // We use || as it's lazy evaluated. - theme: _themeListener2.default.initial(context) || getDefaultTheme() - }; - return _this; - } - - // Exposed for test purposes. - - - (0, _createClass3.default)(WithTheme, [{ - key: 'componentDidMount', - value: function componentDidMount() { - var _this2 = this; - - this.unsubscribeId = _themeListener2.default.subscribe(this.context, function (theme) { - _this2.setState({ theme: theme }); - }); - } - }, { - key: 'componentWillUnmount', - value: function componentWillUnmount() { - if (this.unsubscribeId !== null) { - _themeListener2.default.unsubscribe(this.context, this.unsubscribeId); - } - } - }, { - key: 'render', - value: function render() { - return _react2.default.createElement(Component, (0, _extends3.default)({ theme: this.state.theme }, this.props)); - } - }]); - return WithTheme; - }(_react2.default.Component); - - WithTheme.contextTypes = _themeListener2.default.contextTypes; - WithTheme.displayName = (0, _wrapDisplayName2.default)(Component, 'withTheme'); - WithTheme.Naked = Component; - - - return WithTheme; - }; -}; - -exports.default = withTheme; - -/***/ }), -/* 63 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _Input = __webpack_require__(131); - -Object.defineProperty(exports, 'default', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_Input).default; - } -}); - -var _InputAdornment = __webpack_require__(528); - -Object.defineProperty(exports, 'InputAdornment', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_InputAdornment).default; - } -}); - -var _InputLabel = __webpack_require__(530); - -Object.defineProperty(exports, 'InputLabel', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_InputLabel).default; - } -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/***/ }), -/* 64 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -/** - * Copyright (c) 2013-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * - */ - -function makeEmptyFunction(arg) { - return function () { - return arg; - }; -} - -/** - * This function accepts and discards inputs; it has no side effects. This is - * primarily useful idiomatically for overridable function endpoints which - * always need to be callable, since JS lacks a null-call idiom ala Cocoa. - */ -var emptyFunction = function emptyFunction() {}; - -emptyFunction.thatReturns = makeEmptyFunction; -emptyFunction.thatReturnsFalse = makeEmptyFunction(false); -emptyFunction.thatReturnsTrue = makeEmptyFunction(true); -emptyFunction.thatReturnsNull = makeEmptyFunction(null); -emptyFunction.thatReturnsThis = function () { - return this; -}; -emptyFunction.thatReturnsArgument = function (arg) { - return arg; -}; - -module.exports = emptyFunction; - -/***/ }), -/* 65 */ -/***/ (function(module, exports) { - -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -// css base code, injected by the css-loader -module.exports = function(useSourceMap) { - var list = []; - - // return the list of modules as css string - list.toString = function toString() { - return this.map(function (item) { - var content = cssWithMappingToString(item, useSourceMap); - if(item[2]) { - return "@media " + item[2] + "{" + content + "}"; - } else { - return content; - } - }).join(""); - }; - - // import a list of modules into the list - list.i = function(modules, mediaQuery) { - if(typeof modules === "string") - modules = [[null, modules, ""]]; - var alreadyImportedModules = {}; - for(var i = 0; i < this.length; i++) { - var id = this[i][0]; - if(typeof id === "number") - alreadyImportedModules[id] = true; - } - for(i = 0; i < modules.length; i++) { - var item = modules[i]; - // skip already imported module - // this implementation is not 100% perfect for weird media query combinations - // when a module is imported multiple times with different media queries. - // I hope this will never occur (Hey this way we have smaller bundles) - if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { - if(mediaQuery && !item[2]) { - item[2] = mediaQuery; - } else if(mediaQuery) { - item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; - } - list.push(item); - } - } - }; - return list; -}; - -function cssWithMappingToString(item, useSourceMap) { - var content = item[1] || ''; - var cssMapping = item[3]; - if (!cssMapping) { - return content; - } - - if (useSourceMap && typeof btoa === 'function') { - var sourceMapping = toComment(cssMapping); - var sourceURLs = cssMapping.sources.map(function (source) { - return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */' - }); - - return [content].concat(sourceURLs).concat([sourceMapping]).join('\n'); - } - - return [content].join('\n'); -} - -// Adapted from convert-source-map (MIT) -function toComment(sourceMap) { - // eslint-disable-next-line no-undef - var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))); - var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64; - - return '/*# ' + data + ' */'; -} - - -/***/ }), -/* 66 */ -/***/ (function(module, exports, __webpack_require__) { - -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ - -var stylesInDom = {}; - -var memoize = function (fn) { - var memo; - - return function () { - if (typeof memo === "undefined") memo = fn.apply(this, arguments); - return memo; - }; -}; - -var isOldIE = memoize(function () { - // Test for IE <= 9 as proposed by Browserhacks - // @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805 - // Tests for existence of standard globals is to allow style-loader - // to operate correctly into non-standard environments - // @see https://github.com/webpack-contrib/style-loader/issues/177 - return window && document && document.all && !window.atob; -}); - -var getElement = (function (fn) { - var memo = {}; - - return function(selector) { - if (typeof memo[selector] === "undefined") { - var styleTarget = fn.call(this, selector); - // Special case to return head of iframe instead of iframe itself - if (styleTarget instanceof window.HTMLIFrameElement) { - try { - // This will throw an exception if access to iframe is blocked - // due to cross-origin restrictions - styleTarget = styleTarget.contentDocument.head; - } catch(e) { - styleTarget = null; - } - } - memo[selector] = styleTarget; - } - return memo[selector] - }; -})(function (target) { - return document.querySelector(target) -}); - -var singleton = null; -var singletonCounter = 0; -var stylesInsertedAtTop = []; - -var fixUrls = __webpack_require__(318); - -module.exports = function(list, options) { - if (typeof DEBUG !== "undefined" && DEBUG) { - if (typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment"); - } - - options = options || {}; - - options.attrs = typeof options.attrs === "object" ? options.attrs : {}; - - // Force single-tag solution on IE6-9, which has a hard limit on the # of